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

Implement multi-part exercises #402

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 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
38 changes: 37 additions & 1 deletion src/app/learnocaml_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,42 @@ let set_nickname_div () =
| nickname -> Manip.setInnerText nickname_div nickname
| exception Not_found -> ()

(* setup for the prelude in the description page (in description_main.ml) *)
let setup_tab_text_prelude_pane prelude =
if prelude = "" then () else
let iframe_pane = find_component "learnocaml-exo-tab-text-iframe" in
let prelude_pane = find_component "learnocaml-exo-tab-text-prelude" in
let open Tyxml_js.Html5 in
let state =
ref (match arg "tab_text_prelude" with
| exception Not_found -> false
| "shown" -> true
| "hidden" -> false
| _ -> failwith "Bad format for argument prelude.") in
let prelude_btn = button [] in
let prelude_title = h1 [ txt [%i"OCaml prelude"] ;
prelude_btn ] 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.replaceChildren prelude_btn [ txt ("↳ "^[%i"Hide"]) ] ;
Manip.SetCss.display prelude_container "" ;
Manip.SetCss.top iframe_pane "241px";
set_arg "tab_text_prelude" "shown"
end else begin
Manip.replaceChildren prelude_btn [ txt ("↰ "^[%i"Show"]) ] ;
Manip.SetCss.display prelude_container "none" ;
Manip.SetCss.top iframe_pane "90px";
set_arg "tab_text_prelude" "hidden"
end in
update () ;
Manip.Ev.onclick prelude_btn
(fun _ -> state := not !state ; update () ; true) ;
Manip.appendChildren prelude_pane
[ prelude_title ; 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 @@ -1115,7 +1151,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
4 changes: 3 additions & 1 deletion src/app/learnocaml_common.mli
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ val stars_div: float -> [> Html_types.div ] Tyxml_js.Html5.elt

(** Returns an HTML string expected to be put in an iframe *)
val exercise_text:
Exercise.Meta.t -> Exercise.t -> string
Exercise.Meta.t -> Learnocaml_exercise.t -> string
Copy link
Member

Choose a reason for hiding this comment

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

is this legit? (I'm not saying this is not the case 🙂 but we'll just need to have a look)


val string_of_exercise_kind: Exercise.Meta.kind -> string

Expand Down Expand Up @@ -221,6 +221,8 @@ val typecheck :

val set_nickname_div : unit -> unit

val setup_tab_text_prelude_pane : 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
26 changes: 19 additions & 7 deletions src/app/learnocaml_description_main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ module Exercise_link =
a_class cl ]
content)
end
module Display = Display_exercise(Exercise_link)

module Display = Display_exercise(Exercise_link)
open Display

let () =
run_async_with_log @@ fun () ->
let id = match Url.Current.path with
Expand All @@ -33,12 +33,24 @@ let () =
retrieve (Learnocaml_api.Exercise (Some token, id))
in
init_tabs ();
exercise_fetch >>= fun (ex_meta, exo, _deadline) ->
(* display exercise questions *)
exercise_fetch >>= fun (ex_meta, ex, _deadline) ->
let exo = match ex with
| Learnocaml_exercise.Subexercise (_,ex) ->
(match ex with
| [] -> raise Not_found
| ex1 :: _ -> Learnocaml_exercise.Exercise ex1)
| ex -> ex
in
(* display exercise questions and prelude *)
setup_tab_text_prelude_pane Learnocaml_exercise.(decipher File.prelude 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 iframe_container [Tyxml_js.Of_dom.of_iFrame text_iframe];
Manip.replaceChildren text_container
Tyxml_js.Html5.[ h1 [ txt ex_meta.title ] ;
Tyxml_js.Of_dom.of_iFrame text_iframe ] ;
Tyxml_js.Html5.[ h1 [ txt ex_meta.title] ;
prelude_container;
iframe_container ] ;
Js.Opt.case
(text_iframe##.contentDocument)
(fun () -> failwith "cannot edit iframe document")
Expand Down
66 changes: 63 additions & 3 deletions src/app/learnocaml_exercise_main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,20 @@ let () =
in
let after_init top =
exercise_fetch >>= fun (_meta, exo, _deadline) ->
begin match Learnocaml_exercise.(decipher File.prelude exo) with
let ex = match exo with
| Learnocaml_exercise.Exercise ex -> ex
| Learnocaml_exercise.Subexercise (_, [] ) -> raise Not_found
| Learnocaml_exercise.Subexercise (_id, ex :: _ ) -> ex
in
begin match Learnocaml_exercise.(decipher File.prelude (Learnocaml_exercise.Exercise ex)) with
| "" -> Lwt.return true
| prelude ->
Learnocaml_toplevel.load ~print_outcome:true top
~message: [%i"loading the prelude..."]
prelude
end >>= fun r1 ->
Learnocaml_toplevel.load ~print_outcome:false top
(Learnocaml_exercise.(decipher File.prepare exo)) >>= fun r2 ->
(Learnocaml_exercise.(decipher File.prepare (Learnocaml_exercise.Exercise ex))) >>= fun r2 ->
if not r1 || not r2 then failwith [%i"error in prelude"] ;
Learnocaml_toplevel.set_checking_environment top >>= fun () ->
Lwt.return () in
Expand Down Expand Up @@ -193,11 +198,62 @@ let () =
d##open_;
d##write (Js.string (exercise_text ex_meta exo));
d##close) ;

(* ------------------- Subexercise navigation -------- *)

let nav_available = match exo with
| Learnocaml_exercise.Exercise _ -> false
| Learnocaml_exercise.Subexercise _ -> true
in
(* Traitement du "sous-index" pour savoir si on peut naviguer *)
token >>= fun tok ->
retrieve (Learnocaml_api.Exercise_index tok) >>= fun (index,l) ->
let navigation_toolbar = find_component "learnocaml-exo-tab-navigation" in
let prev_and_next id =
let rec loop = function
| [] -> assert false
| [ _ ] (* assumes single id *) -> None, None
| (one, _) :: (two, _) :: _ when id = one -> None, Some two
| (one, _) :: (two, _) :: [] when id = two -> Some one, None
| (one, _) :: (two, _) :: (three, _) :: _ when id = two -> Some one, Some three
| _ :: rest -> loop rest
in loop [id,1] in
let prev_button_state = button_state () in
let next_button_state = button_state () in
begin match prev_and_next id with
| None, None ->
disable_button prev_button_state ;
disable_button next_button_state
| Some _, None ->
enable_button prev_button_state ;
disable_button next_button_state
| None, Some _ ->
disable_button prev_button_state ;
enable_button next_button_state
| Some _, Some _ ->
enable_button prev_button_state ;
enable_button next_button_state
end ;
let subtitle_field = Tyxml_js.Html5.(h4 ~a: [a_class ["learnocaml-exo-subtitle"]]
[txt id]) in
let button_next = find_component "learnocaml-exo-button-next" in
let button_prev = find_component "learnocaml-exo-button-prev" in
Manip.appendChild ~before: button_next navigation_toolbar subtitle_field ;
if nav_available then
(Manip.SetCss.display button_next "";
Manip.SetCss.display button_prev "";
)
else
(Manip.SetCss.display button_next "none";
Manip.SetCss.display button_prev "none";
Manip.SetCss.width subtitle_field "100%";
);

(* ---- main toolbar -------------------------------------------------- *)
let exo_toolbar = find_component "learnocaml-exo-toolbar" in
let toolbar_button = button ~container: exo_toolbar ~theme: "light" in
begin toolbar_button
~icon: "list" [%i"Exercises"] @@ fun () ->
~icon: "list" [%i"Exercises"] @@ fun () ->
Dom_html.window##.location##assign
(Js.string (api_server ^ "/index.html#activity=exercises")) ;
Lwt.return ()
Expand Down Expand Up @@ -283,6 +339,10 @@ let () =
Ace.focus ace ;
typecheck true
end ;
begin toolbar_button
~icon: "reload" [%i"AllGrade!"] @@ fun () ->
typecheck true
end;
Window.onunload (fun _ev -> local_save ace id; true);
(* ---- return -------------------------------------------------------- *)
toplevel_launch >>= fun _ ->
Expand Down
Loading