Feat: question levels bar figure

This commit is contained in:
Bertrand Benjamin 2021-04-21 07:03:45 +02:00
parent 36425e587e
commit 18f855ab83
3 changed files with 89 additions and 5 deletions

View File

@ -72,6 +72,14 @@ layout = html.Div(
], ],
id="fig_exam_histo_container", id="fig_exam_histo_container",
), ),
html.Div(
children=[
dcc.Graph(
id="fig_questions_bar",
)
],
id="fig_questions_bar_container",
),
], ],
id="analysis", id="analysis",
), ),

View File

@ -18,7 +18,10 @@ from .models import (
get_unstack_scores, get_unstack_scores,
get_students_from_exam, get_students_from_exam,
get_score_colors, get_score_colors,
get_level_color_bar,
score_to_final_mark, score_to_final_mark,
stack_scores,
pivot_score_on,
) )
@ -90,8 +93,8 @@ def update_scores_store(exam):
) )
def update_finale_score_table(scores): def update_finale_score_table(scores):
scores_df = pd.DataFrame.from_records(scores) scores_df = pd.DataFrame.from_records(scores)
# print(scores_df) stacked_scores = stack_scores(scores_df)
return score_to_final_mark(scores_df) return score_to_final_mark(stacked_scores)
@app.callback( @app.callback(
@ -106,7 +109,6 @@ def update_finale_score_table(scores):
def update_statictics_table(finale_score): def update_statictics_table(finale_score):
df = pd.DataFrame.from_records(finale_score) df = pd.DataFrame.from_records(finale_score)
statistics = df["mark"].describe().to_frame().T statistics = df["mark"].describe().to_frame().T
print(statistics)
return [ return [
[{"id": c, "name": c} for c in statistics.columns], [{"id": c, "name": c} for c in statistics.columns],
statistics.to_dict("records"), statistics.to_dict("records"),
@ -155,3 +157,54 @@ def update_exam_histo(finale_scores):
margin=dict(l=5, r=5, b=5, t=5), margin=dict(l=5, r=5, b=5, t=5),
) )
return [fig] return [fig]
@app.callback(
[
Output("fig_questions_bar", "figure"),
],
[
Input("scores_table", "data"),
],
)
def update_questions_bar(finale_scores):
scores = pd.DataFrame.from_records(finale_scores)
scores = stack_scores(scores)
if scores.empty:
return [go.Figure(data=[go.Scatter(x=[], y=[])])]
pt = pivot_score_on(scores, ["exercise", "question", "comment"], "score")
print(pt)
# separation between exercises
for i in {i for i in pt.index.get_level_values(0)}:
pt.loc[(str(i), "", ""), :] = ""
pt.sort_index(inplace=True)
# Bar label
index = (
pt.index.get_level_values(0).map(str)
+ ":"
+ pt.index.get_level_values(1).map(str)
+ " "
+ pt.index.get_level_values(2).map(str)
)
fig = go.Figure()
bars = get_level_color_bar()
for b in bars:
try:
fig.add_bar(
x=index, y=pt[b["score"]], name=b["name"], marker_color=b["color"]
)
except KeyError:
pass
fig.update_layout(barmode="relative")
fig.update_layout(
height=500,
margin=dict(l=5, r=5, b=5, t=5),
)
return [fig]

View File

@ -70,6 +70,13 @@ def get_score_colors():
return score_color return score_color
def get_level_color_bar():
return [
{"score": s["value"], "name": s["comment"], "color": s["color"]}
for s in SCORES_CONFIG.values()
]
is_none_score = lambda x: on_column.is_none_score(x, SCORES_CONFIG) is_none_score = lambda x: on_column.is_none_score(x, SCORES_CONFIG)
score_to_numeric_score = lambda x: on_column.score_to_numeric_score(x, SCORES_CONFIG) score_to_numeric_score = lambda x: on_column.score_to_numeric_score(x, SCORES_CONFIG)
score_to_mark = lambda x: on_column.score_to_mark( score_to_mark = lambda x: on_column.score_to_mark(
@ -80,8 +87,7 @@ score_to_mark = lambda x: on_column.score_to_mark(
def score_to_final_mark(scores): def score_to_final_mark(scores):
""" Compute marks then reduce to final mark per student """ """ Compute marks then reduce to final mark per student """
melted_scores = stack_scores(scores) filtered_scores = scores[~scores.apply(is_none_score, axis=1)]
filtered_scores = melted_scores[~melted_scores.apply(is_none_score, axis=1)]
filtered_scores = filtered_scores.assign( filtered_scores = filtered_scores.assign(
score=filtered_scores.apply(score_to_numeric_score, axis=1) score=filtered_scores.apply(score_to_numeric_score, axis=1)
) )
@ -93,3 +99,20 @@ def score_to_final_mark(scores):
].sum() ].sum()
return [final_score.reset_index().to_dict("records")] return [final_score.reset_index().to_dict("records")]
def pivot_score_on(scores, index, columns, aggfunc="size"):
"""Pivot scores on index, columns with aggfunc
It assumes thant scores are levels
"""
filtered_scores = scores[~scores.apply(is_none_score, axis=1)]
pt = pd.pivot_table(
scores,
index=index,
columns=columns,
aggfunc=aggfunc,
fill_value=0,
)
return pt