Skip to content

Commit

Permalink
added grading module - still buggy
Browse files Browse the repository at this point in the history
  • Loading branch information
reevafaisal committed Nov 12, 2024
1 parent e1bf644 commit e4330cf
Show file tree
Hide file tree
Showing 9 changed files with 829 additions and 16 deletions.
10 changes: 10 additions & 0 deletions src/haz3lschool/GradePrelude.re
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ module ExerciseEnv = {
let output_header = Exercise.output_header_grading;
};

module TutorialEnv = {
type node = unit;
let default = ();
let output_header = Tutorial.output_header_grading;
};

module Tutorial = Tutorial.D(TutorialEnv);

module GradingT = TutorialGrading.D(TutorialEnv);

module Exercise = Exercise.F(ExerciseEnv);

module Grading = Grading.F(ExerciseEnv);
61 changes: 50 additions & 11 deletions src/haz3lschool/Tutorial.re
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,33 @@ module D = (TutorialEnv: TutorialEnv) => {
p.title;
};

let find_key_opt = (key, specs: list(p('code))) => {
specs |> Util.ListUtil.findi_opt(spec => key_of(spec) == key);
// let find_key_opt = (key, specs: list(p('code))) => {
// specs |> Util.ListUtil.findi_opt(spec => key_of(spec) == key);
// };

let find_key_opt =
(key, specs: list(p('code))): option((string, p('code))) => {
let rec loop = remaining_specs => {
switch (remaining_specs) {
| [] => None
| [spec, ...rest] =>
if (key_of(spec) == key) {
Some
((key_of(spec), spec)); // Return as soon as we find a match
} else {
loop(
rest // Continue searching in the remaining specs
);
}
};
};
loop(specs); // Start looping over `specs`
};

[@deriving (show({with_path: false}), sexp, yojson)]
type pos =
| YourImpl
| YourTestsValidation
| HiddenTests;

[@deriving (show({with_path: false}), sexp, yojson)]
Expand Down Expand Up @@ -114,6 +134,7 @@ module D = (TutorialEnv: TutorialEnv) => {
({pos, eds, _}) =>
switch (pos) {
| YourImpl => eds.your_impl
| YourTestsValidation => eds.hidden_tests.tests
| HiddenTests => eds.hidden_tests.tests
};

Expand All @@ -126,6 +147,7 @@ module D = (TutorialEnv: TutorialEnv) => {
your_impl: editor,
},
}
| YourTestsValidation
| HiddenTests => {
...state,
eds: {
Expand All @@ -143,16 +165,16 @@ module D = (TutorialEnv: TutorialEnv) => {
eds.hidden_tests.tests,
];

let editor_positions = [YourImpl, HiddenTests];
let editor_positions = [YourImpl, HiddenTests, YourTestsValidation];

let positioned_editors = state =>
List.combine(editor_positions, editors(state));

let idx_of_pos = (pos, p: p('code)) =>
switch (pos) {
| YourImpl => 0

| HiddenTests => 0 + List.length(p.hidden_tests.tests) // NEED TO FIGURE OUT HOW TO ACTUALLY MAKE THIS WORK
| YourTestsValidation => 1
| HiddenTests => 1 + List.length(p.hidden_tests.tests)
};

let pos_of_idx = (p: p('code), idx: int) =>
Expand Down Expand Up @@ -242,6 +264,7 @@ module D = (TutorialEnv: TutorialEnv) => {
let visible_in = (pos, ~instructor_mode) => {
switch (pos) {
| YourImpl => true
| YourTestsValidation => true
| HiddenTests => instructor_mode
};
};
Expand Down Expand Up @@ -323,6 +346,7 @@ module D = (TutorialEnv: TutorialEnv) => {
// };

type stitched('a) = {
test_validation: 'a, // prelude + correct_impl + your_tests
user_impl: 'a, // prelude + your_impl
instructor: 'a, // prelude + correct_impl + hidden_tests.tests // TODO only needs to run in instructor mode
hidden_tests: 'a,
Expand Down Expand Up @@ -369,12 +393,18 @@ module D = (TutorialEnv: TutorialEnv) => {
let user_impl_term = {
eds.your_impl |> term_of |> wrap_filter(FilterAction.Step);
};
let test_validation_term = eds.hidden_tests.tests |> term_of;

// No combining of your_impl_term with hidden_tests
let hidden_tests_term =
EditorUtil.append_exp(user_impl_term, term_of(eds.hidden_tests.tests));

{user_impl: user_impl_term, instructor, hidden_tests: hidden_tests_term};
{
user_impl: user_impl_term,
instructor,
test_validation: test_validation_term,
hidden_tests: hidden_tests_term,
};
};

let stitch_term = Core.Memo.general(stitch_term);
Expand All @@ -395,6 +425,7 @@ module D = (TutorialEnv: TutorialEnv) => {
};
let instructor = mk(t.instructor);
{
test_validation: mk(t.test_validation),
user_impl: mk(t.user_impl),
instructor,
hidden_tests: mk(t.hidden_tests),
Expand Down Expand Up @@ -427,18 +458,20 @@ module D = (TutorialEnv: TutorialEnv) => {
let key_for_statics = (state: state): string =>
switch (state.pos) {
| YourImpl => user_impl_key
| YourTestsValidation => test_validation_key
| HiddenTests => hidden_tests_key
};

let spliced_elabs =
(settings: CoreSettings.t, state: state)
: list((ModelResults.key, Elaborator.Elaboration.t)) => {
let {user_impl, instructor, hidden_tests} =
let {test_validation, user_impl, instructor, hidden_tests} =
stitch_static(settings, stitch_term(state));
let elab = (s: Editor.CachedStatics.t): Elaborator.Elaboration.t => {
d: Interface.elaborate(~settings, s.info_map, s.term),
};
[
(test_validation_key, elab(test_validation)),
(user_impl_key, elab(user_impl)),
(instructor_key, elab(instructor)),
(hidden_tests_key, elab(hidden_tests)),
Expand Down Expand Up @@ -467,6 +500,7 @@ module D = (TutorialEnv: TutorialEnv) => {
(state: state, s: stitched(DynamicsItem.t)): Editor.CachedStatics.t =>
switch (state.pos) {
| YourImpl => s.user_impl.statics
| YourTestsValidation => s.test_validation.statics
| HiddenTests => s.hidden_tests.statics
};

Expand All @@ -481,7 +515,7 @@ module D = (TutorialEnv: TutorialEnv) => {
)
: stitched(DynamicsItem.t) => {
let {
// test_validation,
test_validation,
user_impl,
// user_tests,
// prelude,
Expand All @@ -497,6 +531,11 @@ module D = (TutorialEnv: TutorialEnv) => {
ModelResults.lookup(results, key)
|> Option.value(~default=ModelResult.NoElab)
};
let test_validation =
DynamicsItem.{
statics: test_validation,
result: result_of(test_validation_key),
};

let user_impl =
DynamicsItem.{statics: user_impl, result: result_of(user_impl_key)};
Expand All @@ -510,7 +549,7 @@ module D = (TutorialEnv: TutorialEnv) => {
result: result_of(hidden_tests_key),
};
{
// test_validation,
test_validation,
user_impl,
// user_tests,
instructor,
Expand All @@ -532,7 +571,7 @@ module D = (TutorialEnv: TutorialEnv) => {
} else if (settings.statics) {
let t = stitch_static(settings, stitch_term(state));
{
// test_validation: DynamicsItem.statics_only(t.test_validation),
test_validation: DynamicsItem.statics_only(t.test_validation),
user_impl: DynamicsItem.statics_only(t.user_impl),
// user_tests: DynamicsItem.statics_only(t.user_tests),
instructor: DynamicsItem.statics_only(t.instructor),
Expand All @@ -542,7 +581,7 @@ module D = (TutorialEnv: TutorialEnv) => {
};
} else {
{
// test_validation: DynamicsItem.empty,
test_validation: DynamicsItem.empty,
user_impl: DynamicsItem.empty,
// user_tests: DynamicsItem.empty,
instructor: DynamicsItem.empty,
Expand Down
133 changes: 133 additions & 0 deletions src/haz3lschool/TutorialGradescope.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
open Haz3lcore;
open Util;

// open Haz3lschool;
open Core;

open Specs;
open GradePrelude.Tutorial;
open GradePrelude.GradingT;

[@deriving (sexp, yojson)]
type item = {
max: int,
percentage,
src: string,
};

let item_to_summary = (name, {max, percentage, src}) =>
Printf.sprintf(
"%s: %.1f/%.1f\n\n",
name,
percentage *. float_of_int(max),
float_of_int(max),
)
++ (
if (String.equal(src, "")) {
"";
} else {
"Source Code:\n\n" ++ src ++ "\n\n";
}
);

[@deriving (sexp, yojson)]
type report = {
summary: string,
overall: score,
};

[@deriving (sexp, yojson)]
type section = {
name: string,
report,
};

[@deriving (sexp, yojson)]
type chapter = list(section);

module Main = {
let settings = CoreSettings.on; /* Statics and Dynamics on */
let name_to_tutorial_export = path => {
let yj = Yojson.Safe.from_file(path);
switch (yj) {
| `Assoc(l) =>
let sch = List.Assoc.find_exn(~equal=String.(==), l, "school");
switch (sch) {
| `String(sch) =>
let exercise_export = sch |> deserialize_exercise_export;
{
cur_exercise: exercise_export.cur_exercise,
exercise_data: exercise_export.exercise_data // Ensure this is list((key, persistent_state))
};
| _ => failwith("School is not a string")
};
| _ => failwith("Json without school key")
};
};
let gen_grading_report = exercise => {
let zipper_pp = zipper => {
Printer.pretty_print(zipper);
};
let model_results =
spliced_elabs(settings, exercise)
|> ModelResults.init_eval
|> ModelResults.run_pending(~settings);
let stitched_dynamics =
stitch_dynamic(settings, exercise, Some(model_results));
let grading_report = exercise.eds |> GradingReport.mk(~stitched_dynamics);
let details = grading_report;

let impl_grading = {
max: 100, // Set fixed maximum score
src: exercise.eds.your_impl.state.zipper |> zipper_pp,
percentage: ImplGradingReport.percentage(details.impl_grading_report),
};

let overall = grading_report |> GradingReport.overall_score;
let (a, b) = overall;
let summary =
Printf.sprintf("Overall: %.1f/%.1f\n\n", a, b)
++ item_to_summary("Impl Grading", impl_grading);
{summary, overall};
};

let create_section = (item): section => {
// Separate `key` and `persistent_state` within the function
let key = fst(item);
let persistent_state = snd(item);

switch (find_key_opt(key, specs)) {
| Some((name, spec)) =>
// Unpersist the state for the exercise
let exercise =
unpersist_state(
persistent_state,
~settings,
~spec,
~instructor_mode=true,
);

// Generate the grading report for this exercise
let report = gen_grading_report(exercise);

// Return a `section` record
{name, report};
| None => failwith("Invalid spec")
};
};

let run = () => {
let hw_path = Sys.get_argv()[1];
let hw = name_to_tutorial_export(hw_path);

let export_chapter: list(section) =
List.map(~f=item => create_section(item), hw.exercise_data);

export_chapter
|> yojson_of_chapter
|> Yojson.Safe.pretty_to_string
|> print_endline;
};
};

Main.run();
Loading

0 comments on commit e4330cf

Please sign in to comment.