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

Exercise Development Mode: Title Editor #928

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 0 additions & 2 deletions INSTRUCTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ Make sure the module_name argument matches your module name. Use the .ml extensi
4. When satisfied, you can click the Export Exercise Module button in the top bar, which will generate
a replacement for your .ml file that you can drop in (blowing away the code in the blank template is ok).

5. You will then have to create a prompt file, <module_name>_prompt.re. See examples for what goes there.

Once you're done, you can compile and run again and your exercise should be in the state you exported.
You can keep doing this cycle to tweak the exercise until it is ready.

Expand Down
2 changes: 2 additions & 0 deletions src/haz3lweb/Log.re
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ let is_action_logged: Update.t => bool =
| ResetSlide
| ToggleMode
| SwitchSlide(_)
| UpdateTitle(_)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's group all Exercise related actions into an ExerciseAction.t type that is injected into Action.t at once, to avoid having too many cases?

| UpdatePrompt(_)
| SwitchEditor(_)
| PerformAction(_)
| FailedInput(_)
Expand Down
66 changes: 47 additions & 19 deletions src/haz3lweb/SchoolExercise.re
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
open Virtual_dom.Vdom;
open Sexplib.Std;
open Haz3lcore;
open Editor;

[@deriving (show({with_path: false}), sexp, yojson)]
type wrong_impl('code) = {
Expand Down Expand Up @@ -39,8 +39,7 @@ type p('code) = {
title: string,
version: int,
module_name: string,
prompt:
[@printer (fmt, _) => Format.pp_print_string(fmt, "prompt")] [@opaque] Node.t,
prompt: string,
point_distribution,
prelude: 'code,
correct_impl: 'code,
Expand All @@ -63,6 +62,7 @@ let find_key_opt = (key, specs: list(p('code))) => {

[@deriving (show({with_path: false}), sexp, yojson)]
type pos =
| Title
| Prelude
| CorrectImpl
| YourTestsValidation
Expand Down Expand Up @@ -125,6 +125,7 @@ type persistent_state = (pos, Id.t, list((pos, PersistentZipper.t)));
let editor_of_state: state => Editor.t =
({pos, eds, _}) =>
switch (pos) {
| Title => eds.prelude
| Prelude => eds.prelude
| CorrectImpl => eds.correct_impl
| YourTestsValidation => eds.your_tests.tests
Expand All @@ -141,6 +142,13 @@ let id_of_state = ({eds, _}: state): Id.t => {
let put_editor_and_id =
({pos, eds, _} as state: state, next_id, editor: Editor.t) =>
switch (pos) {
| Title => {
...state,
eds: {
...eds,
next_id,
},
}
| Prelude => {
...state,
eds: {
Expand Down Expand Up @@ -224,33 +232,35 @@ let positioned_editors = state =>

let idx_of_pos = (pos, p: p('code)) =>
switch (pos) {
| Prelude => 0
| CorrectImpl => 1
| YourTestsTesting => 2
| YourTestsValidation => 3
| YourImpl => 4
| Title => 0
| Prelude => 1
| CorrectImpl => 2
| YourTestsTesting => 3
| YourTestsValidation => 4
| YourImpl => 5
| HiddenBugs(i) =>
if (i < List.length(p.hidden_bugs)) {
5 + i;
6 + i;
} else {
failwith("invalid hidden bug index");
}
| HiddenTests => 5 + List.length(p.hidden_bugs)
| HiddenTests => 6 + List.length(p.hidden_bugs)
};

let pos_of_idx = (p: p('code), idx: int) =>
switch (idx) {
| 0 => Prelude
| 1 => CorrectImpl
| 2 => YourTestsTesting
| 3 => YourTestsValidation
| 4 => YourImpl
| 0 => Title
| 1 => Prelude
| 2 => CorrectImpl
| 3 => YourTestsTesting
| 4 => YourTestsValidation
| 5 => YourImpl
| _ =>
if (idx < 0) {
failwith("negative idx");
} else if (idx < 5 + List.length(p.hidden_bugs)) {
HiddenBugs(idx - 5);
} else if (idx == 5 + List.length(p.hidden_bugs)) {
} else if (idx < 6 + List.length(p.hidden_bugs)) {
HiddenBugs(idx - 6);
} else if (idx == 6 + List.length(p.hidden_bugs)) {
HiddenTests;
} else {
failwith("element idx");
Expand All @@ -262,6 +272,22 @@ let switch_editor = (idx: int, {eds, _}) => {
eds,
};

let update_title = ({eds, pos}, title) => {
pos,
eds: {
...eds,
title,
},
};

let update_prompt = ({eds, pos}, prompt) => {
pos,
eds: {
...eds,
prompt,
},
};

let zipper_of_code = (id, code) => {
switch (Printer.zipper_of_string(id, code)) {
| None => failwith("Transition failed.")
Expand Down Expand Up @@ -459,6 +485,7 @@ let set_instructor_mode = ({eds, _} as state: state, new_mode: bool) => {

let visible_in = (pos, ~instructor_mode) => {
switch (pos) {
| Title => instructor_mode
| Prelude => instructor_mode
| CorrectImpl => instructor_mode
| YourTestsValidation => true
Expand Down Expand Up @@ -759,6 +786,7 @@ let focus = (state: state, stitched_dynamics: stitched(DynamicsItem.t)) => {

let (focal_zipper, focal_info_map) =
switch (pos) {
| Title => (eds.prelude.state.zipper, instructor.info_map)
| Prelude => (eds.prelude.state.zipper, instructor.info_map)
| CorrectImpl => (eds.correct_impl.state.zipper, instructor.info_map)
| YourTestsValidation => (
Expand Down Expand Up @@ -848,7 +876,7 @@ let blank_spec =
title,
version: 1,
module_name,
prompt: Node.text("TODO: prompt"),
prompt: "TODO: prompt",
point_distribution,
prelude,
correct_impl,
Expand Down
19 changes: 19 additions & 0 deletions src/haz3lweb/Update.re
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ let reevaluate_post_update =
| FinishImportAll(_)
| FinishImportScratchpad(_)
| ResetSlide
| UpdateTitle(_)
| UpdatePrompt(_)
| SwitchEditor(_)
| SwitchSlide(_)
| ToggleMode
Expand Down Expand Up @@ -297,6 +299,23 @@ let apply =
Ok({...model, editors: School(n, specs, exercise)});
}
}

| UpdateTitle(title) =>
switch (model.editors) {
| Scratch(_) => assert(false)
| School(m, specs, exercise) =>
let exercise = SchoolExercise.update_title(exercise, title);
Ok({...model, editors: School(m, specs, exercise)});
}

| UpdatePrompt(prompt) =>
switch (model.editors) {
| Scratch(_) => assert(false)
| School(m, specs, exercise) =>
let exercise = SchoolExercise.update_prompt(exercise, prompt);
Ok({...model, editors: School(m, specs, exercise)});
}

| SwitchEditor(n) =>
switch (model.editors) {
| Scratch(_) => Error(FailedToSwitch) // one editor per scratch
Expand Down
2 changes: 2 additions & 0 deletions src/haz3lweb/UpdateAction.re
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ type t =
| Save
| ToggleMode
| SwitchSlide(int)
| UpdateTitle(string)
| UpdatePrompt(string)
| SwitchEditor(int)
| SetFontMetrics(FontMetrics.t)
| SetLogoFontMetrics(FontMetrics.t)
Expand Down
7 changes: 4 additions & 3 deletions src/haz3lweb/exercises/Ex_OddlyRecursive.ml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
let prompt = Ex_OddlyRecursive_prompt.prompt

let exercise : SchoolExercise.spec =
{
next_id = 5134;
title = "Oddly Recursive";
version = 1;
module_name = "Ex_OddlyRecursive";
prompt;
prompt =
"Write a recursive function that determines whether the given integer is \
odd.\n\n\
odd(n) ≡ true iff n is odd.\n";
point_distribution =
{ test_validation = 1; mutation_testing = 1; impl_grading = 2 };
prelude =
Expand Down
9 changes: 5 additions & 4 deletions src/haz3lweb/exercises/Ex_RecursiveFibonacci.ml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
let prompt = Ex_RecursiveFibonacci_prompt.prompt

let exercise : SchoolExercise.spec =
{
next_id = 813;
title = "Recursive Fibonacci";
version = 1;
module_name = "Ex_RecursiveFibonacci";
prompt;
prompt =
"Write tests cases for, and then implement, a function, that recursively \
determines the nth fibonacci number.\n\n\
fib(n) ≡ the nth fibonacci number, assuming n >= 0";
point_distribution =
{ test_validation = 1; mutation_testing = 1; impl_grading = 2 };
prelude =
Expand All @@ -15,7 +16,7 @@ let exercise : SchoolExercise.spec =
backpack = [];
relatives =
{
siblings = ([ Grout { id = 0; shape = Convex } ], []);
siblings = ([ Grout { id = 1; shape = Convex } ], []);
ancestors = [];
};
caret = Outer;
Expand Down
32 changes: 32 additions & 0 deletions src/haz3lweb/view/Cell.re
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,38 @@ let code_cell_view =
);
};

let text_cell_view = (~attrs, ~selected, ~caption, ~text) =>
Node.div(
~attr=attrs,
[
Node.div(
~attr=Attr.class_("cell-container"),
[
Node.div(
~attr=
Attr.many([
Attr.classes(
["cell-item", "cell"]
@ (selected ? ["selected"] : ["deselected"]),
),
]),
[
bolded_caption(caption),
Node.textarea(
~attr=
Attr.many([
Attr.id("textarea-container"),
Attr.classes(["textarea-container"]),
]),
[Node.text(text)],
),
],
),
],
),
],
);

let test_status_icon_view =
(~font_metrics, insts, ms: Measured.Shards.t): option(Node.t) =>
switch (ms) {
Expand Down
14 changes: 12 additions & 2 deletions src/haz3lweb/view/Page.re
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,16 @@ let get_selection = (model: Model.t): string =>

let view = (~inject, ~handlers, model: Model.t) => {
let main_ui = main_ui_view(~inject, model);

let additional_attrs = {
switch (model.editors) {
| School(_, _, exercise) =>
let SchoolExercise.{pos, _} = exercise;
pos == Title ? [] : handlers(~inject, ~model);
| _ => handlers(~inject, ~model)
};
};

div(
~attr=
Attr.many(
Expand Down Expand Up @@ -280,8 +290,8 @@ let view = (~inject, ~handlers, model: Model.t) => {
Dom.preventDefault(evt);
inject(UpdateAction.Paste(pasted_text));
}),
...handlers(~inject, ~model),
],
]
@ additional_attrs,
),
[
FontSpecimen.view("font-specimen"),
Expand Down
Loading