Compare commits

...

4 Commits

6 changed files with 93 additions and 15 deletions

View File

@ -0,0 +1,23 @@
#!/usr/bin/env python
# encoding: utf-8
def highlight_scores(highlight_columns, score_color):
""" Cells style in a datatable for scores
:param highlight_columns: columns to highlight
:param value_color: dictionnary {"score": "color"}
"""
hight = []
for v, color in score_color.items():
if v:
hight += [
{
"if": {"filter_query": "{{{}}} = {}".format(col, v), "column_id": col},
"backgroundColor": color,
"color": "white",
}
for col in highlight_columns
]
return hight

View File

@ -8,8 +8,16 @@ import dash_table
import json import json
import pandas as pd import pandas as pd
from ...app import app from recopytex.dashboard.app import app
from .models import get_tribes, get_exams, get_unstack_scores, get_students_from_exam from recopytex.dashboard.common.formating import highlight_scores
from .models import (
get_tribes,
get_exams,
get_unstack_scores,
get_students_from_exam,
get_score_colors,
)
@app.callback( @app.callback(
@ -57,11 +65,15 @@ def update_scores_store(exam):
"score_rate", "score_rate",
"is_leveled", "is_leveled",
] ]
columns = fixed_columns + list(get_students_from_exam(exam))
students = list(get_students_from_exam(exam))
columns = fixed_columns + students
score_color = get_score_colors()
return [ return [
[{"id": c, "name": c} for c in columns], [{"id": c, "name": c} for c in columns],
scores.to_dict("records"), scores.to_dict("records"),
[], highlight_scores(students, score_color),
{"headers": True, "data": len(fixed_columns)}, {"headers": True, "data": len(fixed_columns)},
] ]

View File

@ -2,7 +2,7 @@
# encoding: utf-8 # encoding: utf-8
from recopytex.database.filesystem.loader import CSVLoader from recopytex.database.filesystem.loader import CSVLoader
from recopytex.lib.dataframe import column_values_to_column from recopytex.datalib.dataframe import column_values_to_column
LOADER = CSVLoader("./test_config.yml") LOADER = CSVLoader("./test_config.yml")
@ -22,9 +22,17 @@ def get_record_scores(exam):
def get_unstack_scores(exam): def get_unstack_scores(exam):
flat_scores = LOADER.get_exam_scores(exam) flat_scores = LOADER.get_exam_scores(exam)
kept_columns = [col for col in LOADER.score_columns if col != "score"] kept_columns = [col for col in LOADER.score_columns if col != "score"]
return column_values_to_column(flat_scores, "student_name", "score", kept_columns) return column_values_to_column("student_name", "score", kept_columns, flat_scores)
def get_students_from_exam(exam): def get_students_from_exam(exam):
flat_scores = LOADER.get_exam_scores(exam) flat_scores = LOADER.get_exam_scores(exam)
return flat_scores["student_name"].unique() return flat_scores["student_name"].unique()
def get_score_colors():
scores_config = LOADER.get_config()["valid_scores"]
score_color = {}
for key, score in scores_config.items():
score_color[score["value"]] = score["color"]
return score_color

View File

@ -22,13 +22,27 @@ competences: # Competences
abrv: Com abrv: Com
valid_scores: # valid_scores: #
BAD: 0 # Everything is bad BAD: # Everything is bad
FEW: 1 # Few good things value: 0
NEARLY: 2 # Nearly good but things are missing color: "#E7472B"
GOOD: 3 # Everything is good FEW: # Few good things
NOTFILLED: # The item is not scored yet value: 1
NOANSWER: . # Student gives no answer (count as 0) color: "#FF712B"
ABS: "a" # Student has absent (this score won't be impact the final mark) NEARLY: # Nearly good but things are missing
value: 2
color: "#F2EC4C"
GOOD: # Everything is good
value: 3
color: "#68D42F"
NOTFILLED: # The item is not scored yet
value: ""
color: white
NOANSWER: # Student gives no answer (count as 0)
value: "."
color: black
ABS: # Student has absent (this score won't be impact the final mark)
value: a
color: lightgray
csv_fields: # dataframe_field: csv_field csv_fields: # dataframe_field: csv_field
term: Trimestre term: Trimestre

View File

@ -31,10 +31,10 @@ class CSVLoader(Loader):
:example: :example:
>>> loader = CSVLoader() >>> loader = CSVLoader()
>>> loader.get_config() >>> loader.get_config()
{'source': './', 'competences': {'Chercher': {'name': 'Chercher', 'abrv': 'Cher'}, 'Représenter': {'name': 'Représenter', 'abrv': 'Rep'}, 'Modéliser': {'name': 'Modéliser', 'abrv': 'Mod'}, 'Raisonner': {'name': 'Raisonner', 'abrv': 'Rai'}, 'Calculer': {'name': 'Calculer', 'abrv': 'Cal'}, 'Communiquer': {'name': 'Communiquer', 'abrv': 'Com'}}, 'valid_scores': {'BAD': 0, 'FEW': 1, 'NEARLY': 2, 'GOOD': 3, 'NOTFILLED': None, 'NOANSWER': '.', 'ABS': 'a'}, 'csv_fields': {'term': 'Trimestre', 'exam': 'Nom', 'date': 'Date', 'exercise': 'Exercice', 'question': 'Question', 'competence': 'Competence', 'theme': 'Domaine', 'comment': 'Commentaire', 'score_rate': 'Bareme', 'is_leveled': 'Est_nivele'}, 'id_templates': {'exam': '{name}_{tribe}', 'question': '{exam_id}_{exercise}_{question}_{comment}'}} {'source': './', 'competences': {'Chercher': {'name': 'Chercher', 'abrv': 'Cher'}, 'Représenter': {'name': 'Représenter', 'abrv': 'Rep'}, 'Modéliser': {'name': 'Modéliser', 'abrv': 'Mod'}, 'Raisonner': {'name': 'Raisonner', 'abrv': 'Rai'}, 'Calculer': {'name': 'Calculer', 'abrv': 'Cal'}, 'Communiquer': {'name': 'Communiquer', 'abrv': 'Com'}}, 'valid_scores': {'BAD': {'value': 0, 'color': '#E7472B'}, 'FEW': {'value': 1, 'color': '#FF712B'}, 'NEARLY': {'value': 2, 'color': '#F2EC4C'}, 'GOOD': {'value': 3, 'color': '#68D42F'}, 'NOTFILLED': {'value': '', 'color': 'white'}, 'NOANSWER': {'value': '.', 'color': 'black'}, 'ABS': {'value': 'a', 'color': 'lightgray'}}, 'csv_fields': {'term': 'Trimestre', 'exam': 'Nom', 'date': 'Date', 'exercise': 'Exercice', 'question': 'Question', 'competence': 'Competence', 'theme': 'Domaine', 'comment': 'Commentaire', 'score_rate': 'Bareme', 'is_leveled': 'Est_nivele'}, 'id_templates': {'exam': '{name}_{tribe}', 'question': '{exam_id}_{exercise}_{question}_{comment}'}}
>>> loader = CSVLoader("./test_config.yml") >>> loader = CSVLoader("./test_config.yml")
>>> loader.get_config() >>> loader.get_config()
{'source': './example', 'competences': {'Chercher': {'name': 'Chercher', 'abrv': 'Cher'}, 'Représenter': {'name': 'Représenter', 'abrv': 'Rep'}, 'Modéliser': {'name': 'Modéliser', 'abrv': 'Mod'}, 'Raisonner': {'name': 'Raisonner', 'abrv': 'Rai'}, 'Calculer': {'name': 'Calculer', 'abrv': 'Cal'}, 'Communiquer': {'name': 'Communiquer', 'abrv': 'Com'}}, 'valid_scores': {'BAD': 0, 'FEW': 1, 'NEARLY': 2, 'GOOD': 3, 'NOTFILLED': None, 'NOANSWER': '.', 'ABS': 'a'}, 'csv_fields': {'term': 'Trimestre', 'exam': 'Nom', 'date': 'Date', 'exercise': 'Exercice', 'question': 'Question', 'competence': 'Competence', 'theme': 'Domaine', 'comment': 'Commentaire', 'score_rate': 'Bareme', 'is_leveled': 'Est_nivele'}, 'id_templates': {'exam': '{name}_{tribe}', 'question': '{exam_id}_{exercise}_{question}_{comment}'}, 'output': './output', 'templates': 'templates/', 'tribes': {'Tribe1': {'name': 'Tribe1', 'type': 'Type1', 'students': 'tribe1.csv'}, 'Tribe2': {'name': 'Tribe2', 'students': 'tribe2.csv'}}} {'source': './example', 'competences': {'Chercher': {'name': 'Chercher', 'abrv': 'Cher'}, 'Représenter': {'name': 'Représenter', 'abrv': 'Rep'}, 'Modéliser': {'name': 'Modéliser', 'abrv': 'Mod'}, 'Raisonner': {'name': 'Raisonner', 'abrv': 'Rai'}, 'Calculer': {'name': 'Calculer', 'abrv': 'Cal'}, 'Communiquer': {'name': 'Communiquer', 'abrv': 'Com'}}, 'valid_scores': {'BAD': {'value': 0, 'color': '#E7472B'}, 'FEW': {'value': 1, 'color': '#FF712B'}, 'NEARLY': {'value': 2, 'color': '#F2EC4C'}, 'GOOD': {'value': 3, 'color': '#68D42F'}, 'NOTFILLED': {'value': '', 'color': 'white'}, 'NOANSWER': {'value': '.', 'color': 'black'}, 'ABS': {'value': 'a', 'color': 'lightgray'}}, 'csv_fields': {'term': 'Trimestre', 'exam': 'Nom', 'date': 'Date', 'exercise': 'Exercice', 'question': 'Question', 'competence': 'Competence', 'theme': 'Domaine', 'comment': 'Commentaire', 'score_rate': 'Bareme', 'is_leveled': 'Est_nivele'}, 'id_templates': {'exam': '{name}_{tribe}', 'question': '{exam_id}_{exercise}_{question}_{comment}'}, 'output': './output', 'templates': 'templates/', 'tribes': {'Tribe1': {'name': 'Tribe1', 'type': 'Type1', 'students': 'tribe1.csv'}, 'Tribe2': {'name': 'Tribe2', 'students': 'tribe2.csv'}}}
""" """
CONFIG = DEFAULT_CONFIG CONFIG = DEFAULT_CONFIG

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python
# encoding: utf-8
def column_values_to_column(pivot_column, value_column, kept_columns, df):
"""Pivot_column's values go to column with value_column under it, keeping kept_columns
:param pivot_column: column name where value will become columns
:param value_column: column name where value will be under pivot_column
:param kept_columns: unchanged columns
:param df: DataFrame to work with
:return: Stack dataframe
"""
if pivot_column in kept_columns:
pivot_columns = kept_columns
else:
pivot_columns = kept_columns + [pivot_column]
return df.set_index(pivot_columns).unstack(pivot_column)[value_column].reset_index()