Skip to content

feat: Add support for multi-part exercices #491

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

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo1/descr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
The following example link will open another tab/window: [OCaml](https://ocaml.org "External link")
This exercise is just another demo for the exercise environment.
<a href="" onclick="top.location='/exercises/demo/';">Test</a>

<details>
<summary>Hint</summary>
Use an indirection.
</details>
2 changes: 2 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo1/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"learnocaml_version":"1","kind":"exercise","stars":0,
"title":"Demo of the exercise environment (MD version)"}
2 changes: 2 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo1/prelude.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(* Some code is loaded in the toplevel before your code. *)
let greetings = "Hello world!"
Empty file.
4 changes: 4 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo1/solution.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
let plus = (+)
let times = ( * )
let minus = ( - )
let divide = ( / )
6 changes: 6 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo1/template.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

let plus x y = x + y ;;

let minus x y = y - x ;;

let times x y = x *
26 changes: 26 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo1/test.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
open Test_lib
open Report

let () =
set_result @@
ast_sanity_check code_ast @@ fun () ->
[ Section
([ Text "Function:" ; Code "plus" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "plus"
[ (1, 1) ; (2, 2) ; (10, -10) ]) ;
Section
([ Text "Function:" ; Code "minus" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "minus"
[ (1, 1) ; (4, -2) ; (0, 10) ]) ;
Section
([ Text "Function:" ; Code "times" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "times"
[ (1, 3) ; (2, 4) ; (3, 0) ]) ;
Section
([ Text "Function:" ; Code "divide" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "divide"
[ (12, 4) ; (12, 5) ; (3, 0) ]) ]
8 changes: 8 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo2/descr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
The following example link will open another tab/window: [OCaml](https://ocaml.org "External link")
This exercise is just another demo for the exercise environment.
<a href="" onclick="top.location='/exercises/demo/';">Test</a>

<details>
<summary>Hint</summary>
Use an indirection.
</details>
2 changes: 2 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo2/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"learnocaml_version":"1","kind":"exercise","stars":0,
"title":"Demo of the exercise environment (MD version)"}
2 changes: 2 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo2/prelude.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(* Some code is loaded in the toplevel before your code. *)
let greetings = "Hello world!"
Empty file.
4 changes: 4 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo2/solution.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
let plus = (+)
let times = ( * )
let minus = ( - )
let divide = ( / )
6 changes: 6 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo2/template.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

let plus x y = x + y ;;

let minus x y = y - x ;;

let times x y = x *
26 changes: 26 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-exo2/test.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
open Test_lib
open Report

let () =
set_result @@
ast_sanity_check code_ast @@ fun () ->
[ Section
([ Text "Function:" ; Code "plus" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "plus"
[ (1, 1) ; (2, 2) ; (10, -10) ]) ;
Section
([ Text "Function:" ; Code "minus" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "minus"
[ (1, 1) ; (4, -2) ; (0, 10) ]) ;
Section
([ Text "Function:" ; Code "times" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "times"
[ (1, 3) ; (2, 4) ; (3, 0) ]) ;
Section
([ Text "Function:" ; Code "divide" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "divide"
[ (12, 4) ; (12, 5) ; (3, 0) ]) ]
8 changes: 8 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-prof/descr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
The following example link will open another tab/window: [OCaml](https://ocaml.org "External link")
This exercise is just another demo for the exercise environment.
<a href="" onclick="top.location='/exercises/demo/';">Test</a>

<details>
<summary>Hint</summary>
Use an indirection.
</details>
2 changes: 2 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-prof/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"learnocaml_version":"1","kind":"exercise","stars":0,
"title":"Demo of the exercise environment (MD version)"}
2 changes: 2 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-prof/prelude.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(* Some code is loaded in the toplevel before your code. *)
let greetings = "Hello world!"
Empty file.
4 changes: 4 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-prof/solution.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
let plus = (+)
let times = ( * )
let minus = ( - )
let divide = ( / )
6 changes: 6 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-prof/template.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

let plus x y = x + y ;;

let minus x y = y - x ;;

let times x y = x *
26 changes: 26 additions & 0 deletions demo-repository/exercises/demo-multi/demoM-prof/test.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
open Test_lib
open Report

let () =
set_result @@
ast_sanity_check code_ast @@ fun () ->
[ Section
([ Text "Function:" ; Code "plus" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "plus"
[ (1, 1) ; (2, 2) ; (10, -10) ]) ;
Section
([ Text "Function:" ; Code "minus" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "minus"
[ (1, 1) ; (4, -2) ; (0, 10) ]) ;
Section
([ Text "Function:" ; Code "times" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "times"
[ (1, 3) ; (2, 4) ; (3, 0) ]) ;
Section
([ Text "Function:" ; Code "divide" ],
test_function_2_against_solution
[%ty : int -> int -> int ] "divide"
[ (12, 4) ; (12, 5) ; (3, 0) ]) ]
36 changes: 36 additions & 0 deletions demo-repository/exercises/demo-multi/subindex.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{ "learnocaml_version": "1",
"meta": {
"learnocaml_version" : "2",
"kind" : "problem",
"stars" : 2,
"title" : "Demo of a multi-part exercise",
"identifier" : "demo-multi",
"authors" : [["Someone", "[email protected]"]],
"focus" : ["skill1", "skillN", "concept1", "conceptM"],
"requirements" : ["skill1", "skillN", "concept1", "conceptM"],
"forward_exercises" : [ "exercise1", "exercise2"],
"backward_exercises" : [ "exercise1", "exercise2"],
"max_score" : 100
},
"check_all_against": "demoM-prof",
"parts": [
{ "subtitle": "First test",
"subexercise": "demoM-exo1",
"student_hidden": false,
"student_weight": 1,
"teacher_weight": 0
},
{ "subtitle": "Second test",
"subexercise": "demoM-exo2",
"student_hidden": false,
"student_weight": 1,
"teacher_weight": 0
},
{ "subtitle": "Grading",
"subexercise": "demoM-prof",
"student_hidden": true,
"student_weight": 0,
"teacher_weight": 1
}
]
}
2 changes: 1 addition & 1 deletion demo-repository/exercises/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"groups":
{ "demo":
{ "title": "Demo exercise pack",
"exercises": [ "demo", "demo2" ] } } }
"exercises": [ "demo", "demo2", "demo-multi" ] } } }
41 changes: 36 additions & 5 deletions src/app/learnocaml_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,8 @@ let stars_div stars =
H.img ~alt ~src ()
]

let exercise_text ex_meta exo =
let exercise_text ex_meta ex =

let mathjax_url =
api_server ^ "/js/mathjax/MathJax.js?delayStartupUntil=configured"
in
Expand All @@ -575,10 +576,10 @@ let exercise_text ex_meta exo =
let descr =
let lang = "" in
try
List.assoc lang (Learnocaml_exercise.(access File.descr exo))
List.assoc lang (Learnocaml_exercise.(access false File.descr (ex)))
with
Not_found ->
try List.assoc "" (Learnocaml_exercise.(access File.descr exo))
try List.assoc "" (Learnocaml_exercise.(access false File.descr (ex)))
with Not_found -> [%i "No description available for this exercise." ]
in
Format.asprintf
Expand Down Expand Up @@ -980,6 +981,9 @@ module Editor_button (E : Editor_info) = struct

end

(*let update_template template (E : Editor_info) = Ace.set_contents E.ace template;
Lwt.return ()*)

let setup_editor id solution =
let editor_pane = find_component "learnocaml-exo-editor-pane" in
let editor =
Expand Down Expand Up @@ -1032,7 +1036,7 @@ let setup_tab_text_prelude_pane prelude =
let open Tyxml_js.Html5 in
let state =
ref (match arg "tab_text_prelude" with
| exception Not_found -> true
| exception Not_found -> false
| "shown" -> true
| "hidden" -> false
| _ -> failwith "Bad format for argument prelude.") in
Expand Down Expand Up @@ -1060,6 +1064,33 @@ let setup_tab_text_prelude_pane prelude =
Manip.appendChildren prelude_pane
[ prelude_title ; prelude_container ]


let update_prelude prelude =
if prelude = "" then () else
let prelude_pane = find_component "learnocaml-exo-prelude" in
let open Tyxml_js.Html5 in
let state =
ref (match arg "prelude" with
| exception Not_found -> true
| "shown" -> true
| "hidden" -> false
| _ -> failwith "Bad format for argument prelude.") in
let prelude_container =
pre ~a: [ a_class [ "toplevel-code" ] ]
(Learnocaml_toplevel_output.format_ocaml_code prelude) in
let update () =
if !state then begin
Manip.SetCss.display prelude_container "" ;
end else begin
Manip.SetCss.display prelude_container "none" ;
end in
update () ;
Manip.appendChildren prelude_pane
[ prelude_container ]




let setup_prelude_pane ace prelude =
if prelude = "" then () else
let editor_pane = find_component "learnocaml-exo-editor-pane" in
Expand Down Expand Up @@ -1234,7 +1265,7 @@ module Display_exercise =
else
ignore (onclick cid);
Manip.removeChildren exp;
Manip.appendChild exp (txt (if !displayed then "[-]" else "[+]"));
Manip.appendChild exp (txt (if !displayed then "[+]" else "[-]"));
displayed := not !displayed;
true
in
Expand Down
2 changes: 2 additions & 0 deletions src/app/learnocaml_common.mli
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ val set_nickname_div : unit -> unit

val setup_tab_text_prelude_pane : string -> unit

val update_prelude : string -> unit

val setup_prelude_pane : 'a Ace.editor -> string -> unit

val get_token : ?has_server:bool -> unit -> Learnocaml_data.student Learnocaml_data.token option Lwt.t
Expand Down
67 changes: 63 additions & 4 deletions src/app/learnocaml_description_main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,48 @@ module Exercise_link =
module Display = Display_exercise(Exercise_link)
open Display

exception Multipart_missing_exercise
exception Multipart_student_hidden
exception Multipart_state_outofbounds
exception Multipart_state_invalid
exception Multipart_forbidden_navigation

type state_multipart =
Monopart
| Multipart of int * int (* numero du sous-exo, valeur max *)

let state_multipart = ref Monopart

let init_state_multipart exo =
let inival =
match exo, !state_multipart with
| Learnocaml_exercise.Exercise(_ex), _state -> Monopart
| Learnocaml_exercise.Subexercise(l), Monopart ->
let size = List.length l in
if size < 1 then raise Multipart_missing_exercise
else Multipart(1, size)
| Learnocaml_exercise.Subexercise(l), Multipart(num_prec, _) ->
let size = List.length l in
if size < 1 then raise Multipart_missing_exercise
else Multipart(min num_prec size, size)
in state_multipart := inival

let get_current_part exo =
match exo, !state_multipart with
| Learnocaml_exercise.Exercise ex, Monopart -> ex
| Learnocaml_exercise.Subexercise l, Multipart (n, _nmax) ->
(match List.nth_opt l (n - 1) with
| Some(ex, subex) ->
if subex.Learnocaml_exercise.student_hidden
then raise Multipart_student_hidden
else ex
| None -> raise Multipart_state_outofbounds
)
| _ -> raise Multipart_state_invalid


let () =
print_string ("Test Show exo desc : 0 \n");
run_async_with_log @@ fun () ->
let id = match Url.Current.path with
| "" :: "description" :: p | "description" :: p ->
Expand All @@ -69,20 +110,38 @@ let () =
retrieve (Learnocaml_api.Exercise (Some token, id))
in
init_tabs ();
exercise_fetch >>= fun (ex_meta, exo, _deadline) ->
exercise_fetch >>= fun (ex_meta, ex, _deadline) ->
init_state_multipart ex ;
let exo = get_current_part ex in
(*
let exo = match ex with
| Learnocaml_exercise.Subexercise ([]) -> raise Not_found
| Learnocaml_exercise.Subexercise ((exo, subex) :: _ ) ->
if subex.Learnocaml_exercise.student_hidden = false then exo
else raise Not_found
| Learnocaml_exercise.Exercise exo -> exo
in*)
let sub_id = "TODO"
in
(* display exercise questions and prelude *)
setup_tab_text_prelude_pane Learnocaml_exercise.(decipher File.prelude exo);
setup_tab_text_prelude_pane Learnocaml_exercise.(decipher ~subid:sub_id false File.prelude (Learnocaml_exercise.Exercise exo));
let prelude_container = find_component "learnocaml-exo-tab-text-prelude" in
let iframe_container = find_component "learnocaml-exo-tab-text-iframe" in
let text_iframe = Dom_html.createIframe Dom_html.document in
Manip.replaceChildren title_container
Tyxml_js.Html5.[ h1 [ txt ex_meta.title] ];
Manip.replaceChildren iframe_container
[Tyxml_js.Of_dom.of_iFrame text_iframe];
Manip.replaceChildren text_container
[ Tyxml_js.Of_dom.of_iFrame text_iframe ];
[title_container;
prelude_container;
iframe_container ];
Js.Opt.case
(text_iframe##.contentDocument)
(fun () -> failwith "cannot edit iframe document")
(fun d ->
d##open_;
d##write (Js.string (exercise_text ex_meta exo));
d##write (Js.string (exercise_text ex_meta (Learnocaml_exercise.Exercise exo)));
d##close) ;
(* display meta *)
display_meta (Some token) ex_meta id >>= fun () ->
Expand Down
Loading