From 2031ade1abf338026c6b3d0616e73b781d377d09 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sun, 18 Apr 2021 16:44:40 +0200 Subject: [PATCH] Feat: basic score_table --- recopytex/dashboard/pages/exams_scores/app.py | 84 +++++++++++-------- .../dashboard/pages/exams_scores/callbacks.py | 78 ++++++++--------- .../dashboard/pages/exams_scores/models.py | 20 ++++- 3 files changed, 104 insertions(+), 78 deletions(-) diff --git a/recopytex/dashboard/pages/exams_scores/app.py b/recopytex/dashboard/pages/exams_scores/app.py index 753cd88..4e6fe02 100644 --- a/recopytex/dashboard/pages/exams_scores/app.py +++ b/recopytex/dashboard/pages/exams_scores/app.py @@ -15,43 +15,53 @@ layout = html.Div( ], ), html.Main( - html.Section( - children=[ - html.Div( - children=[ - "Classe: ", - dcc.Dropdown( - id="tribe", - options=[ - {"label": t["name"], "value": t["name"]} - for t in get_tribes().values() - ], - value=next(iter(get_tribes().values()))["name"], - ), - ], - ), - html.Div( - children=[ - "Evaluation: ", - dcc.Dropdown(id="exam_select"), - ], - ), - ], - id="selects", - ), - # html.Section( - # children=[ - # html.Div( - # children=[], - # id="final_score_table_container", - # ), - # ], - # id="analysis", - # ), - # html.Section( - # children=[], - # id="scores_table", - # ), + children=[ + html.Section( + children=[ + html.Div( + children=[ + "Classe: ", + dcc.Dropdown( + id="tribe", + options=[ + {"label": t["name"], "value": t["name"]} + for t in get_tribes().values() + ], + value=next(iter(get_tribes().values()))["name"], + ), + ], + ), + html.Div( + children=[ + "Evaluation: ", + dcc.Dropdown(id="exam_select"), + ], + ), + ], + id="selects", + ), + html.Section( + children=[ + html.Div( + children=[], + id="final_score_table_container", + ), + ], + id="analysis", + ), + html.Section( + children=[ + dash_table.DataTable( + id="scores_table", + columns=[], + style_data_conditional=[], + fixed_columns={}, + editable=True, + ) + ], + id="edit", + ), + ], ), dcc.Store(id="scores"), ], diff --git a/recopytex/dashboard/pages/exams_scores/callbacks.py b/recopytex/dashboard/pages/exams_scores/callbacks.py index e785284..4628d8e 100644 --- a/recopytex/dashboard/pages/exams_scores/callbacks.py +++ b/recopytex/dashboard/pages/exams_scores/callbacks.py @@ -1,14 +1,15 @@ #!/usr/bin/env python # encoding: utf-8 -from dash.dependencies import Input, Output +from dash.dependencies import Input, Output, State +import dash from dash.exceptions import PreventUpdate import dash_table import json import pandas as pd from ...app import app -from .models import get_tribes, get_exams, get_scores +from .models import get_tribes, get_exams, get_unstack_scores, get_students_from_exam @app.callback( @@ -18,10 +19,10 @@ from .models import get_tribes, get_exams, get_scores ], [Input("tribe", "value")], ) -def update_exams_choices(value): - if not value: +def update_exams_choices(tribe): + if not tribe: raise PreventUpdate - exams = get_exams(value) + exams = get_exams(tribe) exams.reset_index(inplace=True) if not exams.empty: return [ @@ -29,39 +30,38 @@ def update_exams_choices(value): ], exams.loc[0].to_json() return [], None + @app.callback( - [Output("scores", "data")], - [Input("exam_select", "value")] - ) -def update_scores_store(value): - if not value: - return [[]] - exam = pd.DataFrame.from_dict([json.loads(value)]) - return [get_scores(exam)] + [ + Output("scores_table", "columns"), + Output("scores_table", "data"), + Output("scores_table", "style_data_conditional"), + Output("scores_table", "fixed_columns"), + ], + [ + Input("exam_select", "value"), + ], +) +def update_scores_store(exam): + ctx = dash.callback_context + if not exam: + return [[], [], [], {}] + exam = pd.DataFrame.from_dict([json.loads(exam)]) + scores = get_unstack_scores(exam, "score", "student_name") + fixed_columns = [ + "exercise", + "question", + "competence", + "theme", + "comment", + "score_rate", + "is_leveled", + ] + columns = fixed_columns + list(get_students_from_exam(exam)) - - - -# @app.callback( -# [ -# Output("final_score", "data"), -# ], -# [Input("scores_table", "data")], -# ) -# def update_final_scores(data): -# if not data: -# raise PreventUpdate -# -# scores = pd.DataFrame.from_records(data) -# try: -# if scores.iloc[0]["Commentaire"] == "commentaire": -# scores.drop([0], inplace=True) -# except KeyError: -# pass -# scores = flat_df_students(scores).dropna(subset=["Score"]) -# if scores.empty: -# return [{}] -# -# scores = pp_q_scores(scores) -# assessment_scores = scores.groupby(["Eleve"]).agg({"Note": "sum", "Bareme": "sum"}) -# return [assessment_scores.reset_index().to_dict("records")] + return [ + [{"id": c, "name": c} for c in columns], + scores.to_dict("records"), + [], + {"headers": True, "data": len(fixed_columns)}, + ] diff --git a/recopytex/dashboard/pages/exams_scores/models.py b/recopytex/dashboard/pages/exams_scores/models.py index b50d8f1..bc1638c 100644 --- a/recopytex/dashboard/pages/exams_scores/models.py +++ b/recopytex/dashboard/pages/exams_scores/models.py @@ -14,5 +14,21 @@ def get_exams(tribe): return LOADER.get_exams([tribe]) -def get_scores(exam): - return LOADER.get_exam_scores(exam).to_dict('records') +def get_record_scores(exam): + return LOADER.get_exam_scores(exam) + + +def get_unstack_scores(exam, value_column="score", unstack_column="student_name"): + flat_scores = LOADER.get_exam_scores(exam) + pivot_columns = [col for col in LOADER.score_columns if col != value_column] + unstacked_df = ( + flat_scores.set_index(pivot_columns)[value_column] + .unstack(unstack_column) + .reset_index() + ) + return unstacked_df + + +def get_students_from_exam(exam): + flat_scores = LOADER.get_exam_scores(exam) + return flat_scores["student_name"].unique()