refactor: improve cohort report
This commit is contained in:
@@ -549,6 +549,105 @@ pub fn cohort_value(diagnostic: &CohortDiagnostic, config: &RenderConfig) -> Val
|
||||
),
|
||||
);
|
||||
|
||||
out.insert(
|
||||
"grades",
|
||||
Value::Array(
|
||||
diagnostic
|
||||
.grades
|
||||
.iter()
|
||||
.map(|grade| {
|
||||
let mut value = Value::dict();
|
||||
value.insert("letter", Value::str(&grade.letter));
|
||||
value.insert("low", Value::Float(grade.low));
|
||||
value.insert("high", Value::Float(grade.high));
|
||||
value.insert_some("gpa", grade.gpa.map(Value::Float));
|
||||
value.insert_some("attainment", grade.attainment.as_ref().map(Value::str));
|
||||
value.insert("group", Value::str(&grade.group));
|
||||
value.insert("count", Value::Int(grade.count as i64));
|
||||
value.insert("share", Value::Float(grade.share));
|
||||
value.insert("at-or-above", Value::Int(grade.at_or_above as i64));
|
||||
value
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
|
||||
out.insert(
|
||||
"lectures",
|
||||
Value::Array(
|
||||
diagnostic
|
||||
.lectures
|
||||
.iter()
|
||||
.map(|lecture| {
|
||||
let mut value = Value::dict();
|
||||
value.insert("lecture", Value::str(&lecture.lecture));
|
||||
value.insert("title", Value::str(&lecture.title));
|
||||
value.insert("items", Value::Int(lecture.n_items as i64));
|
||||
value.insert("objectives", Value::Int(lecture.n_objectives as i64));
|
||||
value.insert(
|
||||
"objectives-below",
|
||||
Value::Int(lecture.n_objectives_below as i64),
|
||||
);
|
||||
value.insert("rate", Value::Float(lecture.rate));
|
||||
value.insert(
|
||||
"questions",
|
||||
Value::Array(
|
||||
lecture
|
||||
.questions
|
||||
.iter()
|
||||
.map(|n| Value::Int(*n as i64))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
value.insert_some(
|
||||
"worst-objective",
|
||||
lecture
|
||||
.worst_objective
|
||||
.as_ref()
|
||||
.map(|text| markup_value(text, content)),
|
||||
);
|
||||
value
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
|
||||
let triage_rows = |rows: &[crate::diagnostic::TriageRow]| -> Value {
|
||||
Value::Array(rows.iter().map(|row| triage_value(row, content)).collect())
|
||||
};
|
||||
let mut triage = Value::dict();
|
||||
triage.insert("discard", triage_rows(&diagnostic.triage.discard));
|
||||
triage.insert("rekey", triage_rows(&diagnostic.triage.rekey));
|
||||
triage.insert("revise", triage_rows(&diagnostic.triage.revise));
|
||||
triage.insert("reteach", triage_rows(&diagnostic.triage.reteach));
|
||||
triage.insert("bounded", triage_rows(&diagnostic.triage.bounded));
|
||||
triage.insert("clean", Value::Int(diagnostic.triage.clean as i64));
|
||||
out.insert("triage", triage);
|
||||
|
||||
let predictions = &diagnostic.predictions;
|
||||
let mut prediction = Value::dict();
|
||||
prediction.insert("predicted", Value::Int(predictions.n_predicted as i64));
|
||||
prediction.insert("calibrated", Value::Int(predictions.n_calibrated as i64));
|
||||
prediction.insert_some(
|
||||
"mean-signed-error",
|
||||
predictions.mean_signed_error.map(Value::Float),
|
||||
);
|
||||
prediction.insert_some(
|
||||
"mean-abs-error",
|
||||
predictions.mean_abs_error.map(Value::Float),
|
||||
);
|
||||
prediction.insert("within", Value::Int(predictions.n_within as i64));
|
||||
prediction.insert("band", Value::Int(predictions.n_band as i64));
|
||||
prediction.insert("band-hit", Value::Int(predictions.n_band_hit as i64));
|
||||
if let Some((number, expected, observed)) = predictions.biggest_surprise {
|
||||
let mut surprise = Value::dict();
|
||||
surprise.insert("number", Value::Int(number as i64));
|
||||
surprise.insert("expected", Value::Float(expected));
|
||||
surprise.insert("observed", Value::Float(observed));
|
||||
prediction.insert("biggest-surprise", surprise);
|
||||
}
|
||||
out.insert("predictions", prediction);
|
||||
|
||||
out.insert(
|
||||
"forms",
|
||||
Value::Array(
|
||||
@@ -601,6 +700,46 @@ pub fn cohort_value(diagnostic: &CohortDiagnostic, config: &RenderConfig) -> Val
|
||||
out
|
||||
}
|
||||
|
||||
/// One triage row as a Typst value.
|
||||
fn triage_value(row: &crate::diagnostic::TriageRow, content: bool) -> Value {
|
||||
let mut value = Value::dict();
|
||||
value.insert("number", Value::Int(row.number as i64));
|
||||
value.insert_some("item", row.item.as_ref().map(Value::str));
|
||||
value.insert_some("level", row.level.map(|l| Value::Int(l as i64)));
|
||||
value.insert("p", Value::Float(row.p_value));
|
||||
value.insert_some("point-biserial", row.point_biserial.map(Value::Float));
|
||||
value.insert_some("discrimination", row.discrimination.map(Value::Float));
|
||||
value.insert(
|
||||
"objectives",
|
||||
Value::Array(
|
||||
row.objectives
|
||||
.iter()
|
||||
.map(|text| markup_value(text, content))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
value.insert(
|
||||
"taught-in",
|
||||
Value::Array(row.taught_in.iter().map(|t| Value::str(t)).collect()),
|
||||
);
|
||||
value.insert_some("option", row.option.as_ref().map(Value::str));
|
||||
value.insert_some("option-share", row.option_share.map(Value::Float));
|
||||
value.insert_some(
|
||||
"option-point-biserial",
|
||||
row.option_point_biserial.map(Value::Float),
|
||||
);
|
||||
value.insert(
|
||||
"reasons",
|
||||
Value::Array(
|
||||
row.reasons
|
||||
.iter()
|
||||
.map(|reason| markup_value(reason, content))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
value
|
||||
}
|
||||
|
||||
/// One histogram bin as a Typst value.
|
||||
fn bin_value(bin: &Bin) -> Value {
|
||||
let mut value = Value::dict();
|
||||
@@ -635,6 +774,29 @@ fn cohort_question_value(question: &CohortQuestionRow, content: bool) -> Value {
|
||||
"objectives",
|
||||
Value::Array(question.objectives.iter().map(|o| Value::str(o)).collect()),
|
||||
);
|
||||
value.insert(
|
||||
"objective-texts",
|
||||
Value::Array(
|
||||
question
|
||||
.objective_texts
|
||||
.iter()
|
||||
.map(|text| markup_value(text, content))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
value.insert(
|
||||
"taught-in",
|
||||
Value::Array(question.taught_in.iter().map(|t| Value::str(t)).collect()),
|
||||
);
|
||||
value.insert(
|
||||
"lectures",
|
||||
Value::Array(question.lectures.iter().map(|l| Value::str(l)).collect()),
|
||||
);
|
||||
value.insert("difficulty-band", Value::str(&question.difficulty_band));
|
||||
value.insert(
|
||||
"discrimination-band",
|
||||
Value::str(&question.discrimination_band),
|
||||
);
|
||||
value.insert("p", Value::Float(question.p_value));
|
||||
value.insert_some("point-biserial", question.point_biserial.map(Value::Float));
|
||||
value.insert_some("discrimination", question.discrimination.map(Value::Float));
|
||||
@@ -676,6 +838,17 @@ fn cohort_question_value(question: &CohortQuestionRow, content: bool) -> Value {
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
value.insert(
|
||||
"prediction-notes",
|
||||
Value::Array(
|
||||
question
|
||||
.prediction_notes
|
||||
.iter()
|
||||
.map(|n| markup_value(n, content))
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
value.insert("calibrated", Value::Bool(question.calibrated));
|
||||
let mut by_form = Value::dict();
|
||||
for (form, p) in &question.by_form {
|
||||
by_form.insert(form.clone(), Value::Float(*p));
|
||||
@@ -953,7 +1126,11 @@ mod tests {
|
||||
levels: Vec::new(),
|
||||
objectives: Vec::new(),
|
||||
gaps: Vec::new(),
|
||||
grades: Vec::new(),
|
||||
lectures: Vec::new(),
|
||||
questions: Vec::new(),
|
||||
triage: crate::diagnostic::Triage::default(),
|
||||
predictions: crate::diagnostic::PredictionSummary::default(),
|
||||
revise: Vec::new(),
|
||||
forms: Vec::new(),
|
||||
blueprint: Vec::new(),
|
||||
|
||||
Reference in New Issue
Block a user