2017-03-23 17:23:05 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# encoding: utf-8
|
|
|
|
|
|
|
|
import pandas as pd
|
|
|
|
import numpy as np
|
2017-03-29 02:45:08 +00:00
|
|
|
from notes_tools.tools.marks_plottings import (pie_pivot_table,
|
|
|
|
parallel_on,
|
|
|
|
radar_on,
|
2017-03-31 16:01:10 +00:00
|
|
|
hist_boxplot
|
2017-03-29 02:45:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
import seaborn as sns
|
2017-03-23 17:23:05 +00:00
|
|
|
|
|
|
|
class Student(object):
|
|
|
|
|
|
|
|
"""
|
|
|
|
Informations on a student which can be use inside template.
|
|
|
|
|
|
|
|
Those informations should not be modify or use for compute analysis otherwise they won't be spread over other POV.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, quest_df, exo_df, eval_df):
|
|
|
|
"""
|
|
|
|
Description of a student from quest, exo and eval
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
name = {*quest_df["Eleve"].unique(),
|
|
|
|
*exo_df["Eleve"].unique(),
|
|
|
|
*eval_df["Eleve"].unique(),
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(name) != 1:
|
|
|
|
raise ValueError("Can't initiate Student: dfs contains different student names")
|
|
|
|
|
|
|
|
self.name = name.pop()
|
|
|
|
|
|
|
|
evalname = {*quest_df["Nom"].unique(),
|
|
|
|
*exo_df["Nom"].unique(),
|
|
|
|
*eval_df["Nom"].unique(),
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(evalname) != 1:
|
2017-03-24 16:30:31 +00:00
|
|
|
raise ValueError(f"Can't initiate Student: dfs contains different evaluation names ({'-'.join(evalname)})")
|
2017-03-23 17:23:05 +00:00
|
|
|
|
|
|
|
self.quest_df = quest_df
|
|
|
|
self.exo_df = exo_df
|
|
|
|
self.eval = eval_df.to_dict('records')[0]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def latex_exo_tabulars(self):
|
|
|
|
""" Return list of latex tabulars. One by exercise of the evaluation """
|
|
|
|
try:
|
|
|
|
self._latex_exo_tabulars
|
|
|
|
except AttributeError:
|
|
|
|
self._latex_exo_tabulars = self.build_latex_exo_tabulars()
|
|
|
|
return self._latex_exo_tabulars
|
|
|
|
|
|
|
|
def build_latex_exo_tabulars(self):
|
|
|
|
tabulars = []
|
|
|
|
for t in self.exo_df["Exercice"]:
|
|
|
|
tabulars.append(self.build_latex_exo_tabular(t))
|
|
|
|
|
|
|
|
return tabulars
|
|
|
|
|
|
|
|
def build_latex_exo_tabular(self, exo_name):
|
|
|
|
exo = self.exo_df[self.exo_df["Exercice"] == exo_name]
|
|
|
|
quest = self.quest_df[self.quest_df["Exercice"] == exo_name]
|
|
|
|
|
2017-03-31 15:33:53 +00:00
|
|
|
tabular = [r"\begin{tabular}{|p{2cm}|p{1cm}|}"]
|
2017-03-23 17:23:05 +00:00
|
|
|
tabular.append(r"\hline")
|
2017-03-31 15:33:53 +00:00
|
|
|
tabular.append(r"\rowcolor{highlightbg}")
|
2017-03-23 17:23:05 +00:00
|
|
|
|
|
|
|
if type(exo_name) == int:
|
2017-03-31 15:33:53 +00:00
|
|
|
l = f"Exercice {exo_name} & {exo['Mark_barem'].all()}"
|
|
|
|
tabular.append(l + r" \\")
|
2017-03-23 17:23:05 +00:00
|
|
|
else:
|
2017-03-31 15:33:53 +00:00
|
|
|
l = f"{exo_name} & {exo['Mark_barem'].all()}"
|
|
|
|
tabular.append(l + r" \\")
|
2017-03-23 17:23:05 +00:00
|
|
|
tabular.append(r"\hline")
|
|
|
|
|
|
|
|
if len(quest) > 1:
|
|
|
|
for _, q in quest.iterrows():
|
|
|
|
line = ""
|
|
|
|
if not pd.isnull(q["Question"]):
|
2017-03-31 15:33:53 +00:00
|
|
|
line += " "+str(q['Question'])
|
2017-03-23 17:23:05 +00:00
|
|
|
if not pd.isnull(q["Commentaire"]):
|
2017-03-31 15:33:53 +00:00
|
|
|
line += " "+str(q['Commentaire'])
|
2017-03-23 17:23:05 +00:00
|
|
|
|
|
|
|
line += " & "
|
|
|
|
if q["Niveau"] == 1:
|
|
|
|
line += q['Latex_rep']
|
|
|
|
else:
|
|
|
|
line += str(q['Mark'])
|
2017-03-31 15:33:53 +00:00
|
|
|
line += r" \\"
|
2017-03-23 17:23:05 +00:00
|
|
|
tabular.append(line)
|
|
|
|
tabular.append(r"\hline")
|
|
|
|
|
|
|
|
tabular.append(r"\end{tabular}")
|
|
|
|
return '\n'.join(tabular)
|
|
|
|
|
2017-03-29 02:45:08 +00:00
|
|
|
@property
|
|
|
|
def pies_on_competence(self):
|
|
|
|
""" Pies chart on competences """
|
|
|
|
try:
|
|
|
|
self._pies_on_competence
|
|
|
|
except AttributeError:
|
|
|
|
self._pies_on_competence = pie_pivot_table(self.quest_df,
|
|
|
|
index = "Level",
|
|
|
|
columns = "Competence",
|
|
|
|
values = "Eleve",
|
|
|
|
aggfunc = len,
|
|
|
|
fill_value = 0,
|
|
|
|
)
|
|
|
|
return self._pies_on_competence
|
|
|
|
|
|
|
|
@property
|
|
|
|
def pies_on_domaine(self):
|
|
|
|
""" Pies chart on domaines """
|
|
|
|
try:
|
|
|
|
self._pies_on_domaine
|
|
|
|
except AttributeError:
|
|
|
|
self._pies_on_domaine = pie_pivot_table(self.quest_df,
|
|
|
|
index = "Level",
|
|
|
|
columns = "Domaine",
|
|
|
|
values = "Eleve",
|
|
|
|
aggfunc = len,
|
|
|
|
fill_value = 0,
|
|
|
|
)
|
|
|
|
return self._pies_on_domaine
|
|
|
|
|
|
|
|
@property
|
|
|
|
def radar_on_competence(self):
|
|
|
|
""" Radar plot on competence """
|
|
|
|
try:
|
|
|
|
self._radar_on_competence
|
|
|
|
except AttributeError:
|
|
|
|
self._radar_on_competence = radar_on(self.quest_df,
|
|
|
|
"Competence")
|
|
|
|
return self._radar_on_competence
|
|
|
|
|
|
|
|
@property
|
|
|
|
def radar_on_domaine(self):
|
|
|
|
""" Radar plot on domaine """
|
|
|
|
try:
|
|
|
|
self._radar_on_domaine
|
|
|
|
except AttributeError:
|
|
|
|
self._radar_on_domaine = radar_on(self.quest_df,
|
|
|
|
"Domaine")
|
|
|
|
return self._radar_on_domaine
|
|
|
|
|
|
|
|
@property
|
|
|
|
def heatmap_on_domain(self):
|
|
|
|
""" Heatmap over evals on domains """
|
|
|
|
try:
|
|
|
|
self._heatmap_on_domain
|
|
|
|
except AttributeError:
|
|
|
|
comp = pd.pivot_table(self.quest_df,
|
|
|
|
index = "Competence",
|
|
|
|
columns = ["Exercice", "Question"],
|
|
|
|
values = ["Normalized"],
|
|
|
|
aggfunc = np.mean,
|
|
|
|
)
|
|
|
|
comp.columns = [f"{i['Exercice']} {i['Question']}" for _,i in self.quest_df[["Exercice", "Question"]].drop_duplicates().iterrows()]
|
|
|
|
self._heatmap_on_domain = sns.heatmap(comp)
|
|
|
|
return self._heatmap_on_domain
|
|
|
|
|
2017-03-23 17:23:05 +00:00
|
|
|
class Classe(object):
|
|
|
|
|
|
|
|
"""
|
|
|
|
Informations on a class which can be use inside template.
|
|
|
|
|
|
|
|
Those informations should not be modify or use for compute analysis otherwise they won't be spread over other POV.
|
|
|
|
"""
|
2017-03-29 02:45:08 +00:00
|
|
|
def __init__(self, quest_df, exo_df, eval_df):
|
|
|
|
""" Init of a class from quest, exo and eval """
|
|
|
|
names = {*quest_df["Nom"].unique(),
|
|
|
|
*exo_df["Nom"].unique(),
|
|
|
|
*eval_df["Nom"].unique(),
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(names) != 1:
|
|
|
|
raise ValueError("Can't initiate Classe: dfs contains different evaluation names")
|
|
|
|
|
|
|
|
self.name = names.pop()
|
|
|
|
|
|
|
|
self.quest_df = quest_df
|
|
|
|
self.exo_df = exo_df
|
|
|
|
self.eval_df = eval_df
|
|
|
|
|
|
|
|
@property
|
|
|
|
def marks_tabular(self):
|
|
|
|
""" Latex tabular with marks of students"""
|
|
|
|
try:
|
|
|
|
self._marks_tabular
|
|
|
|
except AttributeError:
|
|
|
|
self._marks_tabular = self.eval_df[["Eleve", "Mark_barem"]]
|
|
|
|
self._marks_tabular.columns = ["Élèves", "Note"]
|
|
|
|
return self._marks_tabular.to_latex()
|
|
|
|
|
2017-03-31 16:01:10 +00:00
|
|
|
@property
|
|
|
|
def hist_boxplot(self):
|
|
|
|
""" Marks histogram and associed box plot """
|
|
|
|
try:
|
|
|
|
self._hist_boxplot
|
|
|
|
except AttributeError:
|
|
|
|
self._hist_boxplot = hist_boxplot(self.eval_df)
|
|
|
|
return self._hist_boxplot
|
|
|
|
|
2017-03-29 02:45:08 +00:00
|
|
|
@property
|
|
|
|
def level_heatmap(self):
|
|
|
|
""" Heapmap on acheivement level """
|
|
|
|
try:
|
|
|
|
self._level_heatmap
|
|
|
|
except AttributeError:
|
|
|
|
pv = pd.pivot_table(self.quest_df,
|
|
|
|
index = "Eleve",
|
|
|
|
columns = ["Exercice", "Question", "Commentaire"],
|
|
|
|
values = ["Normalized"],
|
|
|
|
aggfunc = "mean",
|
|
|
|
)
|
|
|
|
|
|
|
|
def lines_4_heatmap(c):
|
|
|
|
lines = []
|
|
|
|
ini = ''
|
|
|
|
for k,v in enumerate(c.labels[1][::-1]):
|
|
|
|
if v != ini:
|
|
|
|
lines.append(k)
|
|
|
|
ini = v
|
|
|
|
return lines[1:]
|
|
|
|
exercice_sep = lines_4_heatmap(pv.columns)
|
|
|
|
|
2017-03-31 16:56:36 +00:00
|
|
|
pv.columns = [f"{i[3]:.15} {i[1]} {i[2]}" for i in pv.columns.get_values()]
|
2017-03-29 02:45:08 +00:00
|
|
|
|
|
|
|
self._level_heatmap = sns.heatmap(pv.T)
|
|
|
|
|
|
|
|
self._level_heatmap.hlines(exercice_sep,
|
|
|
|
*self._level_heatmap.get_xlim(),
|
|
|
|
colors = "orange",
|
|
|
|
)
|
|
|
|
|
|
|
|
return self._level_heatmap
|
|
|
|
|
2017-03-31 16:56:36 +00:00
|
|
|
@property
|
|
|
|
def pies_eff_pts_on_competence(self):
|
|
|
|
""" Pie charts on competence with repartition of evaluated times and attributed points """
|
|
|
|
return pie_pivot_table(self.quest_df[["Competence", "Bareme", "Exercice", "Question", "Commentaire"]].drop_duplicates(),
|
|
|
|
index = "Competence",
|
|
|
|
#columns = "Level",
|
|
|
|
values = "Bareme",
|
|
|
|
aggfunc=[len,np.sum],
|
|
|
|
fill_value=0)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def pies_eff_pts_on_domaine(self):
|
|
|
|
""" Pie charts on domaine with repartition of evaluated times and attributed points """
|
|
|
|
return pie_pivot_table(self.quest_df[["Domaine", "Bareme", "Exercice", "Question", "Commentaire"]].drop_duplicates(),
|
|
|
|
index = "Domaine",
|
|
|
|
#columns = "Level",
|
|
|
|
values = "Bareme",
|
|
|
|
aggfunc=[len,np.sum],
|
|
|
|
fill_value=0)
|
2017-03-29 02:45:08 +00:00
|
|
|
|
|
|
|
|
2017-03-23 17:23:05 +00:00
|
|
|
|
|
|
|
# TODO: à factoriser Il y a la même dans term.py |jeu. mars 23 19:36:28 EAT 2017
|
|
|
|
def select(quest_df, exo_df, eval_df, index, value):
|
|
|
|
""" Return quest, exo and eval rows which correspond index == value
|
|
|
|
|
|
|
|
:param quest_df: TODO
|
|
|
|
:param exo_df: TODO
|
|
|
|
:param eval_df: TODO
|
|
|
|
|
|
|
|
"""
|
|
|
|
qu = quest_df[quest_df[index] == value]
|
|
|
|
exo = exo_df[exo_df[index] == value]
|
|
|
|
ev = eval_df[eval_df[index] == value]
|
|
|
|
return qu, exo, ev
|
|
|
|
|
|
|
|
def select_contains(quest_df, exo_df, eval_df, index, name_part):
|
|
|
|
""" Return quest, exo and eval rows which contains name_part
|
|
|
|
|
|
|
|
:param quest_df: TODO
|
|
|
|
:param exo_df: TODO
|
|
|
|
:param eval_df: TODO
|
|
|
|
|
|
|
|
"""
|
|
|
|
qu = quest_df[quest_df[index].str.contains(name_part)]
|
|
|
|
exo = exo_df[exo_df[index].str.contains(name_part)]
|
|
|
|
ev = eval_df[eval_df[index].str.contains(name_part)]
|
|
|
|
return qu, exo, ev
|
|
|
|
|
|
|
|
def students_pov(quest_df, exo_df, eval_df):
|
|
|
|
es = []
|
|
|
|
for e in eval_df["Eleve"].unique():
|
|
|
|
d = select(quest_df, exo_df, eval_df, "Eleve", e)
|
|
|
|
eleve = Student(*d)
|
|
|
|
es.append(eleve)
|
|
|
|
return es
|
|
|
|
|
|
|
|
def class_pov(quest_df, exo_df, eval_df):
|
|
|
|
return Classe(quest_df, exo_df, eval_df)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -----------------------------
|
|
|
|
# Reglages pour 'vim'
|
|
|
|
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
|
|
|
|
# cursor: 16 del
|