Compare commits

...

6 Commits

4 changed files with 177 additions and 101 deletions

View File

@ -7,70 +7,62 @@ from .models import get_tribes, get_exams
from .callbacks import * from .callbacks import *
layout = html.Div( layout = html.Div(
children=[ children=[
html.Header( html.Header(
children=[ children=[
html.H1("Analyse des notes"), html.H1("Analyse des notes"),
html.P("Dernière sauvegarde", id="lastsave"), html.P("Dernière sauvegarde", id="lastsave"),
], ],
), ),
html.Main( html.Main(
html.Section( children=[
[ html.Section(
html.Div( children=[
[ html.Div(
"Classe: ", children=[
dcc.Dropdown( "Classe: ",
id="tribe", dcc.Dropdown(
options=[ id="tribe",
{"label": t["name"], "value": t["name"]} options=[
for t in get_tribes().values() {"label": t["name"], "value": t["name"]}
], for t in get_tribes().values()
value=next(iter(get_tribes().values()))["name"], ],
), value=next(iter(get_tribes().values()))["name"],
], ),
), ],
html.Div( ),
[ html.Div(
"Evaluation: ", children=[
dcc.Dropdown(id="exam_select"), "Evaluation: ",
], dcc.Dropdown(id="exam_select"),
html.P(id="test"), ],
], ),
id="select", ],
), id="selects",
html.Section( ),
[ html.Section(
html.Div( children=[
dash_table.DataTable( html.Div(
id="final_score_table", children=[],
columns=[ id="final_score_table_container",
{"id": "Eleve", "name": "Élève"}, ),
{"id": "Note", "name": "Note"}, ],
{"id": "Bareme", "name": "Barème"}, id="analysis",
], ),
data=[], html.Section(
style_data_conditional=[ children=[
{ dash_table.DataTable(
"if": {"row_index": "odd"}, id="scores_table",
"backgroundColor": "rgb(248, 248, 248)", columns=[],
} style_data_conditional=[],
], fixed_columns={},
style_data={ editable=True,
"width": "100px", )
"maxWidth": "100px", ],
"minWidth": "100px", id="edit",
},
),
id="final_score_table_container",
), ),
], ],
id="analysis", ),
), dcc.Store(id="scores"),
html.Section( ],
id="scores_table", )
),
),
dcc.Store(id="scores"),
],
)

View File

@ -1,11 +1,15 @@
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
from dash.dependencies import Input, Output from dash.dependencies import Input, Output, State
import dash
from dash.exceptions import PreventUpdate from dash.exceptions import PreventUpdate
import dash_table
import json
import pandas as pd
from ...app import app from ...app import app
from .models import get_tribes, get_exams from .models import get_tribes, get_exams, get_unstack_scores, get_students_from_exam
@app.callback( @app.callback(
@ -15,12 +19,11 @@ from .models import get_tribes, get_exams
], ],
[Input("tribe", "value")], [Input("tribe", "value")],
) )
def update_csvs(value): def update_exams_choices(tribe):
if not value: if not tribe:
raise PreventUpdate raise PreventUpdate
exams = get_exams(value) exams = get_exams(tribe)
exams.reset_index(inplace=True) exams.reset_index(inplace=True)
print(exams.loc[0, "name"])
if not exams.empty: if not exams.empty:
return [ return [
{"label": e["name"], "value": e.to_json()} for i, e in exams.iterrows() {"label": e["name"], "value": e.to_json()} for i, e in exams.iterrows()
@ -30,24 +33,35 @@ def update_csvs(value):
@app.callback( @app.callback(
[ [
dash.dependencies.Output("final_score", "data"), Output("scores_table", "columns"),
Output("scores_table", "data"),
Output("scores_table", "style_data_conditional"),
Output("scores_table", "fixed_columns"),
],
[
Input("exam_select", "value"),
], ],
[dash.dependencies.Input("scores_table", "data")],
) )
def update_final_scores(data): def update_scores_store(exam):
if not data: ctx = dash.callback_context
raise PreventUpdate if not exam:
return [[], [], [], {}]
exam = pd.DataFrame.from_dict([json.loads(exam)])
scores = get_unstack_scores(exam)
fixed_columns = [
"exercise",
"question",
"competence",
"theme",
"comment",
"score_rate",
"is_leveled",
]
columns = fixed_columns + list(get_students_from_exam(exam))
scores = pd.DataFrame.from_records(data) return [
try: [{"id": c, "name": c} for c in columns],
if scores.iloc[0]["Commentaire"] == "commentaire": scores.to_dict("records"),
scores.drop([0], inplace=True) [],
except KeyError: {"headers": True, "data": len(fixed_columns)},
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")]

View File

@ -1,7 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
# encoding: utf-8 # encoding: utf-8
from ....database.filesystem.loader import CSVLoader from recopytex.database.filesystem.loader import CSVLoader
from recopytex.lib.dataframe import column_values_to_column
LOADER = CSVLoader("./test_config.yml") LOADER = CSVLoader("./test_config.yml")
@ -13,3 +14,17 @@ def get_tribes():
def get_exams(tribe): def get_exams(tribe):
return LOADER.get_exams([tribe]) return LOADER.get_exams([tribe])
def get_record_scores(exam):
return LOADER.get_exam_scores(exam)
def get_unstack_scores(exam):
flat_scores = LOADER.get_exam_scores(exam)
kept_columns = [col for col in LOADER.score_columns if col != "score"]
return column_values_to_column(flat_scores, "student_name", "score", kept_columns)
def get_students_from_exam(exam):
flat_scores = LOADER.get_exam_scores(exam)
return flat_scores["student_name"].unique()

View File

@ -43,6 +43,49 @@ class CSVLoader(Loader):
""" Get config """ """ Get config """
return self._config return self._config
@property
def exam_columns(self):
return pd.Index(["name", "date", "term", "origin", "tribe", "id"])
@property
def question_columns(self):
return pd.Index(
[
"exercise",
"question",
"competence",
"theme",
"comment",
"score_rate",
"is_leveled",
"origin",
"exam_id",
"id",
]
)
@property
def score_columns(self):
return pd.Index(
[
"term",
"exam",
"date",
"exercise",
"question",
"competence",
"theme",
"comment",
"score_rate",
"is_leveled",
"origin",
"exam_id",
"question_id",
"student_name",
"score",
]
)
def rename_columns(self, dataframe): def rename_columns(self, dataframe):
"""Rename dataframe column to match with `csv_fields` """Rename dataframe column to match with `csv_fields`
@ -84,8 +127,8 @@ class CSVLoader(Loader):
:example: :example:
>>> loader = CSVLoader("./test_config.yml") >>> loader = CSVLoader("./test_config.yml")
>>> exams = loader.get_exams(["Tribe1"]) >>> exams = loader.get_exams(["Tribe1"])
>>> exams.columns >>> all(exams.columns == loader.exam_columns)
Index(['name', 'date', 'term', 'origin', 'tribe', 'id'], dtype='object') True
>>> exams >>> exams
name date term origin tribe id name date term origin tribe id
0 DS 12/01/2021 1 example/Tribe1/210112_DS.csv Tribe1 DS_Tribe1 0 DS 12/01/2021 1 example/Tribe1/210112_DS.csv Tribe1 DS_Tribe1
@ -118,10 +161,7 @@ class CSVLoader(Loader):
:example: :example:
>>> loader = CSVLoader("./test_config.yml") >>> loader = CSVLoader("./test_config.yml")
>>> exams = loader.get_exams(["Tribe1"]) >>> exams = loader.get_exams(["Tribe1"])
>>> loader.get_exam_questions([exams.iloc[0]]).columns >>> all(loader.get_exam_questions([exams.iloc[0]]).columns == loader.score_columns)
Index(['exercise', 'question', 'competence', 'theme', 'comment', 'score_rate',
'is_leveled', 'origin', 'exam_id', 'id'],
dtype='object')
>>> questions = loader.get_exam_questions(exams) >>> questions = loader.get_exam_questions(exams)
>>> questions.iloc[0] >>> questions.iloc[0]
exercise Exercice 1 exercise Exercice 1
@ -172,11 +212,8 @@ class CSVLoader(Loader):
>>> exams = loader.get_exams(["Tribe1"]) >>> exams = loader.get_exams(["Tribe1"])
>>> questions = loader.get_exam_questions(exams) >>> questions = loader.get_exam_questions(exams)
>>> scores = loader.get_questions_scores(questions) >>> scores = loader.get_questions_scores(questions)
>>> scores.columns >>> all(scores.columns == loader.score_columns)
Index(['term', 'exam', 'date', 'exercise', 'question', 'competence', 'theme', True
'comment', 'score_rate', 'is_leveled', 'origin', 'exam_id',
'question_id', 'student_name', 'score'],
dtype='object')
>>> scores["student_name"].unique() >>> scores["student_name"].unique()
array(['Star Tice', 'Umberto Dingate', 'Starlin Crangle', array(['Star Tice', 'Umberto Dingate', 'Starlin Crangle',
'Humbert Bourcq', 'Gabriella Handyside', 'Stewart Eaves', 'Humbert Bourcq', 'Gabriella Handyside', 'Stewart Eaves',
@ -214,6 +251,24 @@ class CSVLoader(Loader):
return pd.concat(scores) return pd.concat(scores)
def get_exam_scores(self, exams=[]):
"""Get scores for all question of the exam
:param exams: list or dataframe of exams metadatas (need origin field to find the csv)
:example:
>>> loader = CSVLoader("./test_config.yml")
>>> exams = loader.get_exams(["Tribe1"])
>>> scores = loader.get_exam_scores(exams)
>>> scores.columns
Index(['term', 'exam', 'date', 'exercise', 'question', 'competence', 'theme',
'comment', 'score_rate', 'is_leveled', 'origin', 'exam_id',
'question_id', 'student_name', 'score'],
dtype='object')
"""
questions = self.get_exam_questions(exams)
return self.get_questions_scores(questions)
def get_students(self, tribes=[]): def get_students(self, tribes=[]):
"""Get student list """Get student list