Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Title Editor, Exercise Keying with UUID, Mutant Editing, and New Persistent State Type #1316

Open
wants to merge 32 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9989097
init
russell-rozenbaum Jun 13, 2024
1c53e11
title-box added
russell-rozenbaum Jun 22, 2024
63ccab7
added ability to change display between div/text-box depending on ins…
russell-rozenbaum Jun 22, 2024
c52269e
added functionality to switch between an edit state and finalized sta…
russell-rozenbaum Jun 24, 2024
52bbf77
added functionality to disable (set to read only) any editors while u…
russell-rozenbaum Jun 24, 2024
bde06c2
added disabling of editing_title when switching between exercise slid…
russell-rozenbaum Jun 24, 2024
80ec611
updating module name as well now
russell-rozenbaum Jun 24, 2024
5edf1c9
added an edit symbol
russell-rozenbaum Jun 24, 2024
294f669
updated UI with pencil icon wobble and positioning
russell-rozenbaum Jun 25, 2024
968fe1c
fixed bug with resetting code in editors; fixed bug with not saving w…
russell-rozenbaum Jun 26, 2024
6ffc5cf
added UI for creating and deleting buggy implementations (the buttons…
russell-rozenbaum Jun 27, 2024
a1e82bf
added functionality for creating and deleting buggy implementations; …
russell-rozenbaum Jun 27, 2024
7ea356a
added functionality for creating and deleting buggy implementations; …
russell-rozenbaum Jul 10, 2024
69fea7f
fixed bug with refreshing page while editing title causes code editor…
russell-rozenbaum Jul 23, 2024
45c783a
replaced local storage key system with Uuidm.t types rather than (key…
russell-rozenbaum Aug 3, 2024
fbd81a0
Mutant Add Delete and Title Editor (#1352)
russell-rozenbaum Aug 3, 2024
8374866
changed some string displays for mutation testing; still need to save…
russell-rozenbaum Aug 3, 2024
a2494c9
changed some string displays for mutation testing; still need to save…
russell-rozenbaum Aug 3, 2024
0ab1240
changed some string displays for mutation testing; still need to save…
russell-rozenbaum Aug 3, 2024
30c1035
fixed bugs with some editors not being disabled when editing title
russell-rozenbaum Aug 5, 2024
ebdaefb
reverted changes to exclusively title-editor w/ bugs resolved
russell-rozenbaum Aug 6, 2024
b487d0f
Merge branch 'dev' into title-editor
russell-rozenbaum Aug 6, 2024
612009a
merging to use UUIDs and title-editing properties
russell-rozenbaum Aug 7, 2024
887fa6c
resolved merge conflicts with dev
russell-rozenbaum Aug 17, 2024
229b969
fixed build failure
russell-rozenbaum Aug 17, 2024
d31a338
build and runs locally
russell-rozenbaum Aug 17, 2024
ac146e5
merge conflicts resolved; css still outdated
russell-rozenbaum Aug 17, 2024
f630de6
css updated
russell-rozenbaum Aug 17, 2024
e272d4f
Merge remote-tracking branch 'origin/mutant-add-delete' into title-ed…
russell-rozenbaum Aug 18, 2024
a938192
PersistentState type made into record field, now also storing hidden …
russell-rozenbaum Sep 27, 2024
1b89fd3
commiting before merge with dev
russell-rozenbaum Oct 11, 2024
fa62a34
fixed bugs with deleting last/first editors
russell-rozenbaum Oct 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 175 additions & 62 deletions src/haz3lschool/Exercise.re
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ module F = (ExerciseEnv: ExerciseEnv) => {

[@deriving (show({with_path: false}), sexp, yojson)]
type p('code) = {
id: Id.t,
title: string,
version: int,
module_name: string,
prompt:
[@printer (fmt, _) => Format.pp_print_string(fmt, "prompt")] [@opaque] ExerciseEnv.node,
Expand All @@ -68,15 +68,14 @@ module F = (ExerciseEnv: ExerciseEnv) => {
syntax_tests,
};

[@deriving (show({with_path: false}), sexp, yojson)]
type key = (string, int);
type record = p(Zipper.t);

let key_of = p => {
(p.title, p.version);
let id_of = p => {
p.id;
};

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

[@deriving (show({with_path: false}), sexp, yojson)]
Expand All @@ -97,8 +96,8 @@ module F = (ExerciseEnv: ExerciseEnv) => {

let map = (p: p('a), f: 'a => 'b): p('b) => {
{
id: p.id,
title: p.title,
version: p.version,
module_name: p.module_name,
prompt: p.prompt,
point_distribution: p.point_distribution,
Expand Down Expand Up @@ -135,10 +134,29 @@ module F = (ExerciseEnv: ExerciseEnv) => {
eds,
};

let key_of_state = ({eds, _}) => key_of(eds);

[@deriving (show({with_path: false}), sexp, yojson)]
type persistent_state = (pos, list((pos, PersistentZipper.t)));
type persistent_state = {
focus: pos,
editors: list((pos, PersistentZipper.t)),
title: string,
hidden_bugs: list(wrong_impl(PersistentZipper.t)),
// NOTE: Add new fields to record here as new instructor editable features are
// implemented (eg. prelude: PersistentZipper.t when adding the feature
// to edit the prelude). After adding these field(s), we will need to
// go into persistent_state_of_state and unpersist_state to implement
// how these fields are saved and loaded to and from local memory
// respectively.
// NOTE: It may be helpful to look at changes made in the mutant-add-delete and title-editor
// branches in the Hazel repo to see and understand where changes
// were made. It is likely that new implementations of editble features
// will follow a similar route.
};

let clamp_idx = (eds: eds, idx: int) => {
let length = List.length(eds.hidden_bugs);
let idx = idx > length - 1 ? idx - 1 : idx;
idx >= 0 ? Some(idx) : None;
};

let editor_of_state: state => Editor.t =
({pos, eds, _}) =>
Expand All @@ -148,7 +166,11 @@ module F = (ExerciseEnv: ExerciseEnv) => {
| YourTestsValidation => eds.your_tests.tests
| YourTestsTesting => eds.your_tests.tests
| YourImpl => eds.your_impl
| HiddenBugs(i) => List.nth(eds.hidden_bugs, i).impl
| HiddenBugs(i) =>
switch (clamp_idx(eds, i)) {
| Some(idx) => List.nth(eds.hidden_bugs, idx).impl
| None => eds.your_impl
}
| HiddenTests => eds.hidden_tests.tests
};

Expand Down Expand Up @@ -285,8 +307,8 @@ module F = (ExerciseEnv: ExerciseEnv) => {
let transition: transitionary_spec => spec =
(
{
id,
title,
version,
module_name,
prompt,
point_distribution,
Expand Down Expand Up @@ -321,8 +343,8 @@ module F = (ExerciseEnv: ExerciseEnv) => {
{tests, hints};
};
{
id,
title,
version,
module_name,
prompt,
point_distribution,
Expand All @@ -340,7 +362,7 @@ module F = (ExerciseEnv: ExerciseEnv) => {
(
{
title,
version,
id,
module_name,
prompt,
point_distribution,
Expand Down Expand Up @@ -376,7 +398,7 @@ module F = (ExerciseEnv: ExerciseEnv) => {
};
{
title,
version,
id,
module_name,
prompt,
point_distribution,
Expand Down Expand Up @@ -467,6 +489,84 @@ module F = (ExerciseEnv: ExerciseEnv) => {
},
};

let set_editing_title = ({eds, _} as state: state, editing: bool) => {
...state,
eds: {
...eds,
prelude: Editor.set_read_only(eds.prelude, editing),
correct_impl: Editor.set_read_only(eds.correct_impl, editing),
your_tests: {
let tests = Editor.set_read_only(eds.your_tests.tests, editing);
{
tests,
required: eds.your_tests.required,
provided: eds.your_tests.provided,
};
},
hidden_bugs:
eds.hidden_bugs
|> List.map(({impl, hint}) => {
let impl = Editor.set_read_only(impl, editing);
{impl, hint};
}),
hidden_tests: {
let tests = Editor.set_read_only(eds.hidden_tests.tests, editing);
{tests, hints: eds.hidden_tests.hints};
},
your_impl: Editor.set_read_only(eds.your_impl, editing),
},
};

let update_exercise_title = ({eds, _} as state: state, new_title: string) => {
...state,
eds: {
...eds,
title: new_title,
},
};

let add_buggy_impl =
(~settings: CoreSettings.t, state: state, ~editing_title) => {
let new_buggy_impl = {
impl: Editor.init(Zipper.init(), ~settings),
hint: "no hint available",
};
let new_state = {
pos: HiddenBugs(List.length(state.eds.hidden_bugs)),
eds: {
...state.eds,
hidden_bugs: state.eds.hidden_bugs @ [new_buggy_impl],
},
};
let new_state = set_editing_title(new_state, editing_title);
put_editor(new_state, new_buggy_impl.impl);
};

let delete_buggy_impl = (state: state, index: int) => {
let length = List.length(state.eds.hidden_bugs);
let editor_on =
length > 1
? List.nth(
state.eds.hidden_bugs,
index < length - 1 ? index + 1 : index - 1,
).
impl
: state.eds.your_tests.tests;
let pos =
length > 1
? HiddenBugs(index < length - 1 ? index : index - 1)
: YourTestsValidation;
let new_state = {
pos,
eds: {
...state.eds,
hidden_bugs:
List.filteri((i, _) => i != index, state.eds.hidden_bugs),
},
};
put_editor(new_state, editor_on);
};

let visible_in = (pos, ~instructor_mode) => {
switch (pos) {
| Prelude => instructor_mode
Expand All @@ -485,28 +585,38 @@ module F = (ExerciseEnv: ExerciseEnv) => {
set_instructor_mode({pos: YourImpl, eds}, instructor_mode);
};

let persistent_state_of_state =
({pos, _} as state: state, ~instructor_mode: bool) => {
let persistent_state_of_state = (state: state, ~instructor_mode: bool) => {
let zippers =
positioned_editors(state)
|> List.filter(((pos, _)) => visible_in(pos, ~instructor_mode))
|> List.map(((pos, editor)) => {
(pos, PersistentZipper.persist(Editor.(editor.state.zipper)))
});
(pos, zippers);
let persistent_hidden_bugs =
state.eds.hidden_bugs
|> List.map(({impl, hint}) => {
{impl: PersistentZipper.persist(Editor.(impl.state.zipper)), hint}
});
{
focus: state.pos,
editors: zippers,
title: state.eds.title,
hidden_bugs: persistent_hidden_bugs,
};
};

let unpersist_state =
(
(pos, positioned_zippers): persistent_state,
{focus, editors, title, hidden_bugs}: persistent_state,
~spec: spec,
~instructor_mode: bool,
~editing_title: bool,
~settings: CoreSettings.t,
)
: state => {
let lookup = (pos, default) =>
if (visible_in(pos, ~instructor_mode)) {
let persisted_zipper = List.assoc(pos, positioned_zippers);
let persisted_zipper = List.assoc(pos, editors);
let zipper = PersistentZipper.unpersist(persisted_zipper);
Editor.init(zipper, ~settings);
} else {
Expand All @@ -516,44 +626,43 @@ module F = (ExerciseEnv: ExerciseEnv) => {
let correct_impl = lookup(CorrectImpl, spec.correct_impl);
let your_tests_tests = lookup(YourTestsValidation, spec.your_tests.tests);
let your_impl = lookup(YourImpl, spec.your_impl);
let (_, hidden_bugs) =
List.fold_left(
((i, hidden_bugs: list(wrong_impl(Editor.t))), {impl, hint}) => {
let impl = lookup(HiddenBugs(i), impl);
(i + 1, hidden_bugs @ [{impl, hint}]);
},
(0, []),
spec.hidden_bugs,
);
let hidden_bugs =
hidden_bugs
|> List.map(({impl, hint}) => {
let impl =
Editor.init(PersistentZipper.unpersist(impl), ~settings);
{impl, hint};
});
let hidden_tests_tests = lookup(HiddenTests, spec.hidden_tests.tests);

set_instructor_mode(
{
pos,
eds: {
title: spec.title,
version: spec.version,
module_name: spec.module_name,
prompt: spec.prompt,
point_distribution: spec.point_distribution,
prelude,
correct_impl,
your_tests: {
tests: your_tests_tests,
required: spec.your_tests.required,
provided: spec.your_tests.provided,
},
your_impl,
hidden_bugs,
hidden_tests: {
tests: hidden_tests_tests,
hints: spec.hidden_tests.hints,
let state =
set_instructor_mode(
{
pos: focus,
eds: {
id: spec.id,
title,
module_name: spec.module_name,
prompt: spec.prompt,
point_distribution: spec.point_distribution,
prelude,
correct_impl,
your_tests: {
tests: your_tests_tests,
required: spec.your_tests.required,
provided: spec.your_tests.provided,
},
your_impl,
hidden_bugs,
hidden_tests: {
tests: hidden_tests_tests,
hints: spec.hidden_tests.hints,
},
syntax_tests: spec.syntax_tests,
},
syntax_tests: spec.syntax_tests,
},
},
instructor_mode,
);
instructor_mode,
);
set_editing_title(state, editing_title);
};

// # Stitching
Expand Down Expand Up @@ -725,7 +834,11 @@ module F = (ExerciseEnv: ExerciseEnv) => {
| YourTestsValidation => s.test_validation.statics
| YourTestsTesting => s.user_tests.statics
| YourImpl => s.user_impl.statics
| HiddenBugs(idx) => List.nth(s.hidden_bugs, idx).statics
| HiddenBugs(idx) =>
switch (clamp_idx(state.eds, idx)) {
| Some(idx) => List.nth(s.hidden_bugs, idx).statics
| None => s.user_impl.statics
}
| HiddenTests => s.hidden_tests.statics
};

Expand Down Expand Up @@ -897,8 +1010,8 @@ module F = (ExerciseEnv: ExerciseEnv) => {
);
let hidden_tests_tests = Zipper.next_blank();
{
id: Id.mk(),
title,
version: 1,
module_name,
prompt: ExerciseEnv.default,
point_distribution,
Expand All @@ -923,8 +1036,8 @@ module F = (ExerciseEnv: ExerciseEnv) => {

[@deriving (show({with_path: false}), sexp, yojson)]
type exercise_export = {
cur_exercise: key,
exercise_data: list((key, persistent_state)),
cur_exercise: Id.t,
exercise_data: list((Id.t, persistent_state)),
};

let serialize_exercise = (exercise, ~instructor_mode) => {
Expand All @@ -933,11 +1046,11 @@ module F = (ExerciseEnv: ExerciseEnv) => {
|> Sexplib.Sexp.to_string;
};

let deserialize_exercise = (data, ~spec, ~instructor_mode) => {
let deserialize_exercise = (data, ~spec, ~instructor_mode, ~editing_title) => {
data
|> Sexplib.Sexp.of_string
|> persistent_state_of_sexp
|> unpersist_state(~spec, ~instructor_mode);
|> unpersist_state(~spec, ~instructor_mode, ~editing_title);
};

let deserialize_exercise_export = data => {
Expand Down
Loading
Loading