diff --git a/Makefile b/Makefile index 84df75ec1..56d2ae3c3 100644 --- a/Makefile +++ b/Makefile @@ -35,17 +35,22 @@ static/dune: # Generates up-to-date translation template for lang % from the sources LANGS = $(patsubst translations/%.po,%,$(wildcard translations/*.po)) translations/$(LANGS:=.pot): - @for f in $(LANGS); do echo >> translations/$$f.po; done - @rm -f translations/*.pot + @for f in $(LANGS); do \ + echo >> translations/$$f.po; \ + rm -f translations/$$f.pot; \ + cp translations/$$f.po.header translations/$$f.pot; \ + done @${DUNE} clean ${DUNE_ARGS} -rm -f ${INDEX_ODOC_PATH} @DUMP_POT=1 ${DUNE} build ${DUNE_ARGS} -j 1 @for f in $(LANGS); do \ mv translations/$$f.pot translations/$$f.pot.bak; \ - msguniq translations/$$f.pot.bak > translations/$$f.pot; \ - rm translations/$$f.pot.bak; \ + msguniq -t utf-8 translations/$$f.pot.bak > translations/$$f.pot \ + && rm translations/$$f.pot.bak; \ done +.PHONY: translations/$(LANGS:=.pot) + # Updates existing translations (.po) for the latest source template update-%-translation: translations/%.pot @msgmerge -U translations/$*.po translations/$*.pot diff --git a/src/app/dune b/src/app/dune index e39e59219..22ff6da05 100644 --- a/src/app/dune +++ b/src/app/dune @@ -39,7 +39,8 @@ learnocaml_toplevel js_of_ocaml-ppx ocplib_i18n) - (modules Learnocaml_teacher_tab + (modules Learnocaml_teacher_tab_doc + Learnocaml_teacher_tab Learnocaml_index_main) (preprocess (pps ppx_ocplib_i18n js_of_ocaml-ppx)) ) diff --git a/src/app/learnocaml_common.ml b/src/app/learnocaml_common.ml index 8cc865c54..fe95e6ec9 100644 --- a/src/app/learnocaml_common.ml +++ b/src/app/learnocaml_common.ml @@ -143,16 +143,25 @@ let confirm ~title ?(ok_label=[%i"OK"]) ?(cancel_label=[%i"Cancel"]) contents f close_button cancel_label; ] -let ask_string ~title ?(ok_label=[%i"OK"]) contents = +let ask_string ~title ?(ok_label=[%i"OK"]) ?(may_cancel=true) contents = let input_field = H.input ~a:[ H.a_input_type `Text; ] () in let result_t, up = Lwt.wait () in - ext_alert ~title (contents @ [input_field]) ~buttons:[ - box_button ok_label (fun () -> Lwt.wakeup up @@ Manip.value input_field) - ]; + let validate _ = + Lwt.wakeup up @@ Manip.value input_field + in + Manip.Ev.onreturn input_field validate; + let buttons = + box_button ok_label validate + :: (if may_cancel + then [close_button [%i"Cancel"]] + else []) + in + ext_alert ~title (contents @ [input_field]) ~buttons; + Manip.focus input_field; result_t let default_exn_printer = function @@ -1157,7 +1166,7 @@ let get_token ?(has_server = true) () = Lwt.return with Not_found -> - ask_string ~title:"Token" + ask_string ~title:"Token" ~may_cancel:false [H.txt [%i"Enter your token"]] >>= fun input_tok -> let token = Token.parse (input_tok) in diff --git a/src/app/learnocaml_common.mli b/src/app/learnocaml_common.mli index f94ce7f6b..eff755d06 100644 --- a/src/app/learnocaml_common.mli +++ b/src/app/learnocaml_common.mli @@ -45,6 +45,7 @@ val confirm : val ask_string : title: string -> ?ok_label: string -> + ?may_cancel: bool -> [< Html_types.div_content > `Input] Tyxml_js.Html.elt list -> string Lwt.t diff --git a/src/app/learnocaml_teacher_tab.ml b/src/app/learnocaml_teacher_tab.ml index f0bf5fbd2..0993e6470 100644 --- a/src/app/learnocaml_teacher_tab.ml +++ b/src/app/learnocaml_teacher_tab.ml @@ -68,22 +68,46 @@ let tag_addremove list_id placeholder add_fun remove_fun = ] [ H.txt "\xe2\x9e\x96" (* U+2796 heavy minus sign *) ]; ] +let help_button name (title,md_text) = + let dialog () = + let text_div = + let d = + H.div [] + ~a:[H.a_class ["doc-popup-body"]] + in + (* Manip.SetCss.maxHeight d "85vh"; + * Manip.SetCss.overflowY d "auto"; *) + let doc_html_string = + Omd.(md_text |> of_string |> to_html) + in + Manip.setInnerHtml d doc_html_string; + d + in + Learnocaml_common.ext_alert ~title [text_div] + in + H.button ~a:[ + H.a_id ("button_help_"^name); + H.a_onclick (fun _ -> dialog (); true); + H.a_style "margin-left: 1em;"; + ] [H.txt "?"] + let rec teacher_tab token _select _params () = let action_new_token () = - retrieve (Learnocaml_api.Create_teacher_token token) + Learnocaml_common.ask_string + ~title:"NEW TEACHER TOKEN" + [H.txt @@ "Enter a nickname for the new token:"] + >>= fun nickname -> + let nick = match String.trim nickname with + | "" -> None + | s -> Some s + in + retrieve (Learnocaml_api.Create_teacher_token (token, nick)) >|= fun new_token -> alert ~title:[%i"TEACHER TOKEN"] (Printf.sprintf [%if"New teacher token created:\n%s\n\n\ write it down."] (Token.to_string new_token)) in - let action_csv_export () = - retrieve (Learnocaml_api.Students_csv (token, [], [])) - >|= fun csv -> - Learnocaml_common.fake_download - ~name:"learnocaml.csv" - ~contents:(Js.string csv) - in let indent_style lvl = H.a_style (Printf.sprintf "text-align: left; padding-left: %dem;" lvl) in @@ -183,6 +207,23 @@ let rec teacher_tab token _select _params () = let assignment_change = ref (fun _ -> assert false) in let assignment_remove = ref (fun _ -> assert false) in + let action_csv_export () = + let exercises = + Hashtbl.to_seq_keys selected_exercises |> + List.of_seq + in + let students = + Hashtbl.to_seq_keys selected_students |> + Seq.filter_map (function `Token tk -> Some tk | `Any -> None) |> + List.of_seq + in + retrieve (Learnocaml_api.Students_csv (token, exercises, students)) + >|= fun csv -> + Learnocaml_common.fake_download + ~name:"learnocaml.csv" + ~contents:(Js.string csv) + in + (* Exercises table *) let rec mk_table group_level acc status group = match group with @@ -211,7 +252,7 @@ let rec teacher_tab token _select _params () = in let open_partition_ () = Lwt.async (fun () -> - ask_string ~title:"Choose a function name" + ask_string ~title:"Partitioning of student solutions" [H.txt @@ "Choose a function name to partition codes from "^ id ^": "] >|= fun funname -> let _win = @@ -258,12 +299,13 @@ let rec teacher_tab token _select _params () = H.td [stars_div meta.Exercise.Meta.stars]; H.td [ let cls, text = - if Token.Map.is_empty ES.(st.assignments.token_map) then - match ES.(st.assignments.default) with - | ES.Open -> "exo_open", [%i"Open"] - | ES.Closed -> "exo_closed", [%i"Closed"] - | ES.Assigned _ -> "exo_assigned", [%i"Assigned"] - else "exo_assigned", [%i"Assigned"] + match Token.Map.is_empty ES.(st.assignments.token_map), + ES.(st.assignments.default) with + | true, ES.Open -> "exo_open", [%i"Open"] + | true, ES.Closed -> "exo_closed", [%i"Closed"] + | _, (ES.Assigned _ | ES.Closed) -> + "exo_assigned", [%i"Assigned"] + | false, ES.Open -> "exo_assigned", [%i"Open/Assg"] in H.span ~a:[H.a_class [cls]] [H.txt text] ]; @@ -328,11 +370,14 @@ let rec teacher_tab token _select _params () = let exercise_skills_list_id = "exercise_skills_list" in let exercises_div = let legend = - H.legend ~a:[ - H.a_onclick (fun _ -> - !toggle_selected_exercises (all_exercises !exercises_index); - true); - ] [H.txt [%i"Exercises"]; H.txt " \xe2\x98\x90" (* U+2610 *)] + H.legend [ + H.span + [ H.txt [%i"Exercises"]; H.txt " \xe2\x98\x90" (* U+2610 *) ] + ~a:[H.a_onclick (fun _ -> + !toggle_selected_exercises (all_exercises !exercises_index); + true)]; + help_button "exercises" Learnocaml_teacher_tab_doc.exercises_pane_md + ] in H.div ~a:[H.a_id "exercises_pane"; H.a_class ["learnocaml_pane"]] [ H.div ~a:[H.a_id "exercises_filter_box"] [ @@ -530,23 +575,26 @@ let rec teacher_tab token _select _params () = in let students_div = let legend = - H.legend ~a:[ - H.a_onclick (fun _ -> - let all = - Token.Map.fold (fun k _ acc -> (`Token k)::acc) - !students_map [`Any] - in - let all = - List.filter (fun t -> - not (Manip.hasClass (find_component (student_line_id t)) - "student_hidden")) - all - in - !toggle_selected_students all; - true - ); - ] [H.txt [%i"Students"]; - H.txt " \xe2\x98\x90" (* U+2610 ballot box *)] + H.legend [ + H.span + [ H.txt [%i"Students"]; + H.txt " \xe2\x98\x90" (* U+2610 ballot box *) ] + ~a:[H.a_onclick (fun _ -> + let all = + Token.Map.fold (fun k _ acc -> (`Token k)::acc) + !students_map [`Any] + in + let all = + List.filter (fun t -> + not (Manip.hasClass (find_component (student_line_id t)) + "student_hidden")) + all + in + !toggle_selected_students all; + true + )]; + help_button "students" Learnocaml_teacher_tab_doc.students_pane_md + ] in H.div ~a:[H.a_id "students_pane"; H.a_class ["learnocaml_pane"]] [ H.div ~a:[H.a_id "students_filter_box"] [ @@ -812,12 +860,10 @@ let rec teacher_tab token _select _params () = ES.(default_assignment st.assignments = Open)) ids then ES.(fun assg -> - (* fixme: invisible change if the exercise is assigned! *) match default_assignment assg with | Open -> set_default_assignment assg Closed | _ -> assg) else ES.(fun assg -> - (* fixme: invisible change if the exercise is assigned! *) match default_assignment assg with | Closed -> set_default_assignment assg Open | _ -> assg) @@ -841,9 +887,16 @@ let rec teacher_tab token _select _params () = Manip.appendChild exercises_div exercise_control_div; let assignments_div = H.div [] in let control_div = + let legend = + H.legend [ + H.txt [%i"Assignments"]; + help_button "assignments" + Learnocaml_teacher_tab_doc.assignments_pane_md + ] + in H.div ~a:[H.a_id "control_pane"] [ H.fieldset - ~legend:(H.legend [H.txt [%i"Assignments"]]) + ~legend [assignments_div]; ] in @@ -932,7 +985,7 @@ let rec teacher_tab token _select _params () = H.li ~a: [ H.a_onclick (fun _ -> Lwt.async action_new_token; true) ] [ H.txt [%i"Create new teacher token"] ]; H.li ~a: [ H.a_onclick (fun _ -> Lwt.async action_csv_export; true) ] - [ H.txt [%i"Download student data as CSV"] ]; + [ H.txt [%i"Download the data for selected students/exercises as CSV"] ]; ] ]; ] @@ -1102,10 +1155,16 @@ let rec teacher_tab token _select _params () = if SMap.is_empty !status_changes && Token.Map.is_empty !students_changes then (Manip.replaceChildren status_text_div []; - Manip.removeClass status_text_div "warning") + Manip.removeClass status_text_div "warning"; + Option.iter + (fun b -> Manip.removeClass b "warning") + (Manip.by_id "button_apply")) else (Manip.replaceChildren status_text_div [H.txt [%i"Unsaved changes"]]; - Manip.addClass status_text_div "warning") + Manip.addClass status_text_div "warning"; + Option.iter + (fun b -> Manip.addClass b "warning") + (Manip.by_id "button_apply")) end; toggle_selected_exercises := begin fun ?force ?(update = force=None) ids -> diff --git a/src/app/learnocaml_teacher_tab_doc.ml b/src/app/learnocaml_teacher_tab_doc.ml new file mode 100644 index 000000000..a5b3d4296 --- /dev/null +++ b/src/app/learnocaml_teacher_tab_doc.ml @@ -0,0 +1,118 @@ +(* This file is part of Learn-OCaml. + * + * Copyright (C) 2023 OCaml Software Foundation. + * + * Learn-OCaml is distributed under the terms of the MIT license. See the + * included LICENSE file for details. *) + +type markdown_doc = string * string (** title, markdown text *) + +let exercises_pane_md = + [%i"Handling exercises"], [%i{markdown| +> Don't forget to click "Apply" on the bottom-right after any changes + +- **Filtering exercises**
+ Use the 🔍 box on top. + - type for raw text search on the titles + - click again to list the tags and filter based on those + - multiple search terms are allowed + +- **Selecting which exercises can be seen by the students** + - select the exercises with a click (the box on top (de)selects all; you can + also click folder titles) + - click the [Open/Close] button to toggle + - for complex selections, use the filter box then select all + +- **Defining exercise skill tags**
+ Those can also be pre-defined in the exercises metadata. + 1. Select the exercises to tag + 2. Select or type a new tag in one of the boxes on the lower-right of the pane + (left for requirements, right for skills trained by the exercise) + 3. Click the corresponding [+] button to add the tag ([-] to remove) + +- **Checking an exercise contents** + - double-click an exercise + - it will be opened in a new tab + +- **Classifying students answers**
+ As documented +[here](https://ocaml-sf.org/learn-ocaml/howto-classify-students-answers.html): + - Assume you want to analyze students' definitions of function `foo` in + exercise `exo` + - Hold Ctrl (on macOS: Hold ⌘) and left click on `exo` in the list of + exercises + - Enter `foo` in the dialog box + - The dissimilarity analysis will be opened in a new tab +|markdown}] + +let students_pane_md = + [%i"Handling students"], [%i{markdown| +> Don't forget to click "Apply" on the bottom-right after any changes + +- **Filtering students**
+ Use the 🔍 box on top. + - type for raw text search on the nicknames + - click again to list the tags and filter based on those + - multiple search terms are allowed + +- **Tagging students**
+ Tags can be used for groups, level, etc. + 1. select the students to tag. To select a group, use the filter box and the + box on top to select all. Use the `Sort by` box on the top right to easily + select new students or untagged studends + 2. select or type the tag on the box to the lower-right + 3. Click the corresponding [+] button to add the tag ([-] to remove) + +- **Following a student** + - at a glance: + a bar on the right of each student shows a short insight of their progress, + - vertical stripes correspond to the exercises, in order + - grey is shown for exercises not attempted + - the color otherwise varies from green (good) to red (bad) depending on the + grade + - in detail: + - double-click on the student line to open their track record in a new tab + - there you can see their detailed results per exercise, and check their + answers + - see "Classifying students answers" in the exercise pane guide for a way + to compare solutions between students + +- **Downloading the grades** + - use the [Actions] menu at the bottom to export a CSV file containing a line + per student, and two columns (grade and date) per exercise + - if any exercises or students are selected, the CSV will only contain those + - otherwise everything is exported +|markdown}] + +let assignments_pane_md = + [%i"Handling assignments"], [%i{markdown| +Assignments provide a way to open select exercises to select students for a +given period of time. Once due, the students will be restricted to read-only +access. + +- **Creating an assignment** + 1. Select exercises, as above + 2. Select students, as above. You can use the "any future students" line to + apply to students who haven't yet registered + 3. Click the "New assignment" button at the bottom + 4. Edit the two dates (start and end of the assignment, resp.) + 5. Don't forget to [Apply] on the bottom right + 6. Click the assignment again to unselect it and quit assignment mode + +- **Editing an assignment** + - select it in the bottom pane + - the corresponding exercises and students are automatically selected. Update + the selection as needed + - click the assignment again when finished (to quit editing mode), and don't + forget to [Apply] + +- **Following assignments** + - an additional small "progression" bar is shown for each assignment in the + students pane + - past assignment dates are outlined in red + - when selecting students, exercises assigned to any of them will be greyed + out to prevent assigning multiple times the same exercise to the same + student + - conversely, when selecting exercises, students who already have them + assigned are greyed out +|markdown}] diff --git a/src/app/learnocaml_teacher_tab_doc.mli b/src/app/learnocaml_teacher_tab_doc.mli new file mode 100644 index 000000000..d24e16e20 --- /dev/null +++ b/src/app/learnocaml_teacher_tab_doc.mli @@ -0,0 +1,15 @@ +(* This file is part of Learn-OCaml. + * + * Copyright (C) 2023 OCaml Software Foundation. + * + * Learn-OCaml is distributed under the terms of the MIT license. See the + * included LICENSE file for details. *) + +(** This module contains documentation strings (in markdown) for the different + panes in the teacher tab *) + +type markdown_doc = string * string (** title, markdown text *) + +val exercises_pane_md: markdown_doc +val students_pane_md: markdown_doc +val assignments_pane_md: markdown_doc diff --git a/src/server/learnocaml_server.ml b/src/server/learnocaml_server.ml index 5e9917f0e..0bccadfac 100644 --- a/src/server/learnocaml_server.ml +++ b/src/server/learnocaml_server.ml @@ -279,12 +279,15 @@ module Request_handler = struct (function | Failure body -> (`Bad_request, body) | exn -> (`Internal_server_error, Printexc.to_string exn)) - | Api.Create_teacher_token token -> + | Api.Create_teacher_token (token, nick) -> verify_teacher_token token >?= fun () -> Token.create_teacher () - >>= respond_json cache - + >>= fun tok -> + (match nick with | None -> Lwt.return_unit + | Some nickname -> + Save.set tok Save.{empty with nickname}) + >>= fun () -> respond_json cache tok | Api.Fetch_save token -> lwt_catch_fail (fun () -> diff --git a/src/state/learnocaml_api.ml b/src/state/learnocaml_api.ml index 30a084402..16496a9d0 100644 --- a/src/state/learnocaml_api.ml +++ b/src/state/learnocaml_api.ml @@ -101,7 +101,7 @@ type _ request = | Create_token: string * student token option * string option -> student token request | Create_teacher_token: - teacher token -> teacher token request + teacher token * string option -> teacher token request | Fetch_save: 'a token -> Save.t request | Archive_zip: @@ -299,9 +299,10 @@ module Conversions (Json: JSON_CODEC) = struct | Create_token (secret_candiate, token, nick) -> get ?token (["sync"; "new"; secret_candiate] @ (match nick with None -> [] | Some n -> [n])) - | Create_teacher_token token -> + | Create_teacher_token (token, nick) -> assert (Token.is_teacher token); - get ~token ["teacher"; "new"] + get ~token (["teacher"; "new"] @ + (match nick with None -> [] | Some n -> [n])) | Fetch_save token -> get ~token ["save.json"] @@ -415,8 +416,9 @@ module Server (Json: JSON_CODEC) (Rh: REQUEST_HANDLER) = struct | `GET, ["sync"; "new"; secret_candidate; nick], token -> Create_token (secret_candidate, token, Some nick) |> k | `GET, ["teacher"; "new"], Some token when Token.is_teacher token -> - Create_teacher_token token |> k - + Create_teacher_token (token, None) |> k + | `GET, ["teacher"; "new"; nick], Some token when Token.is_teacher token -> + Create_teacher_token (token, Some nick) |> k | `GET, ["save.json"], Some token -> Fetch_save token |> k | `GET, ["archive.zip"], Some token -> diff --git a/src/state/learnocaml_api.mli b/src/state/learnocaml_api.mli index 4b04ab6f0..86f0ca385 100644 --- a/src/state/learnocaml_api.mli +++ b/src/state/learnocaml_api.mli @@ -87,7 +87,7 @@ type _ request = | Create_token: string * student token option * string option -> student token request | Create_teacher_token: - teacher token -> teacher token request + teacher token * string option -> teacher token request | Fetch_save: 'a token -> Save.t request | Archive_zip: diff --git a/src/utils/dune b/src/utils/dune index 51b8db206..cb9f0f4dc 100644 --- a/src/utils/dune +++ b/src/utils/dune @@ -21,7 +21,7 @@ (wrapped false) (flags :standard -warn-error -4-42-44-45-48) (kind ppx_rewriter) - (libraries ocaml-migrate-parsetree) + (libraries ocaml-migrate-parsetree re) (modules Ppx_ocplib_i18n) ) diff --git a/src/utils/ppx_ocplib_i18n.ml b/src/utils/ppx_ocplib_i18n.ml index fd5bfab09..28a60c4c5 100644 --- a/src/utils/ppx_ocplib_i18n.ml +++ b/src/utils/ppx_ocplib_i18n.ml @@ -103,7 +103,11 @@ let dump_pot () = Format.pp_print_char fmt ' '; Location.print_loc fmt l) locs; - Format.fprintf fmt "\nmsgid %S\n" s; + Format.fprintf fmt "\nmsgid \"%s\"\n" + Re.(s |> + replace_string (compile (char '\\')) ~by:"\\\\" |> + replace_string (compile (char '"')) ~by:"\\\"" |> + replace_string (compile (char '\n')) ~by:"\\n"); Format.fprintf fmt "msgstr \"\"\n\n"; ) misses; close_out oc diff --git a/static/css/learnocaml_teacher_tab.css b/static/css/learnocaml_teacher_tab.css index 9c06f086a..a2dffac50 100644 --- a/static/css/learnocaml_teacher_tab.css +++ b/static/css/learnocaml_teacher_tab.css @@ -54,10 +54,19 @@ cursor: cell; } +#learnocaml-main-teacher > #teacher_menubar { + margin-top: auto; + max-height: 4ex; +} + #learnocaml-main-teacher > #teacher_menubar > * { float: right; } +#learnocaml-main-teacher > #teacher_menubar button { + margin: 5px; +} + #learnocaml-main-teacher > #teacher_menubar > #status-text-div { float: left; margin-left: 16px; @@ -68,6 +77,13 @@ color: red; } +#learnocaml-main-teacher > #teacher_menubar button.warning { + color: black; + background-color: #d62; + border-color: #910; + border-radius: 4px; +} + #learnocaml-main-teacher table { width: 100%; border-spacing: 0; @@ -663,3 +679,27 @@ td.skills-prereq { line-height: 24px; margin: 0 5px; } + +.doc-popup-body { + max-height: 80vh; + overflow-y: auto; +} +.doc-popup-body blockquote p { + background-color: #555; + padding: 4px 4px 4px 4px; +} +.doc-popup-body code { + display: inline !important; + background-color: #222; + font-size: 80%; + padding: 0; + border: 1px solid #666; + border-radius: 3px; +} +.doc-popup-body > ul > li { + margin: 2ex 0; + line-height: 120%; +} +.doc-popup-body li li { + line-height: 110%; +} diff --git a/translations/fr.po b/translations/fr.po index 782bedd19..d52ec457e 100644 --- a/translations/fr.po +++ b/translations/fr.po @@ -6,12 +6,12 @@ msgid "" msgstr "" "Project-Id-Version: learn-ocaml ~dev\n" -"PO-Revision-Date: 2023-04-18 19:58+0200\n" +"PO-Revision-Date: 2023-05-09 14:32+0200\n" "Last-Translator: Erik Martin-Dorel \n" "Language-Team: OCSF\n" "Language: french\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: File "src/grader/learnocaml_report.ml", line 240, characters 50-66 595, @@ -85,247 +85,251 @@ msgstr "OK" msgid "ERROR" msgstr "ERREUR" -#: File "src/app/learnocaml_common.ml", line 140, characters 58-66 446, 12-20 -#: 942, 19-27 "src/app/learnocaml_index_main.ml", 583, 17-25 +#: File "src/app/learnocaml_common.ml", line 140, characters 58-66 160, 30-38 +#: 455, 12-20 952, 19-27 "src/app/learnocaml_index_main.ml", 583, 17-25 msgid "Cancel" msgstr "Annuler" -#: File "src/app/learnocaml_common.ml", line 438, characters 26-41 937, 32-47 +#: File "src/app/learnocaml_common.ml", line 447, characters 26-41 947, 32-47 #: "src/app/learnocaml_index_main.ml", 578, msgid "REQUEST ERROR" msgstr "ERREUR DE REQUÊTE" -#: File "src/app/learnocaml_common.ml", line 439, characters 22-59 938, 30-67 +#: File "src/app/learnocaml_common.ml", line 448, characters 22-59 948, 30-67 #: "src/app/learnocaml_index_main.ml", 579, 28-65 msgid "Could not retrieve data from server" msgstr "Échec lors du tĂ©lĂ©chargement des donnĂ©es du serveur" -#: File "src/app/learnocaml_common.ml", line 442, characters 12-19 486, 13-20 -#: 941, 19-26 "src/app/learnocaml_index_main.ml", 582, 17-24 +#: File "src/app/learnocaml_common.ml", line 451, characters 12-19 495, 13-20 +#: 951, 19-26 "src/app/learnocaml_index_main.ml", 582, 17-24 msgid "Retry" msgstr "RĂ©essayer" -#: File "src/app/learnocaml_common.ml", line 445, characters 25-33 487, 13-21 +#: File "src/app/learnocaml_common.ml", line 454, characters 25-33 496, 13-21 msgid "Ignore" msgstr "Ignorer" -#: File "src/app/learnocaml_common.ml", line 482, characters 26-39 +#: File "src/app/learnocaml_common.ml", line 491, characters 26-39 msgid "SYNC FAILED" msgstr "ECHEC DE LA SYNCHRONISATION" -#: File "src/app/learnocaml_common.ml", line 483, characters 22-66 +#: File "src/app/learnocaml_common.ml", line 492, characters 22-66 msgid "Could not synchronise save with the server" msgstr "Les donnĂ©es n'ont pas pu ĂȘtre synchronisĂ©es avec le serveur" -#: File "src/app/learnocaml_common.ml", line 542, characters 39-50 +#: File "src/app/learnocaml_common.ml", line 552, characters 39-50 msgid "%dd %02dh" msgstr "%dj %02dh" -#: File "src/app/learnocaml_common.ml", line 543, characters 40-51 +#: File "src/app/learnocaml_common.ml", line 553, characters 40-51 msgid "%02d:%02d" msgstr "%02d:%02d" -#: File "src/app/learnocaml_common.ml", line 544, characters 23-36 +#: File "src/app/learnocaml_common.ml", line 554, characters 23-36 msgid "0:%02d:%02d" msgstr "0:%02d:%02d" -#: File "src/app/learnocaml_common.ml", line 575, characters 34-55 1182, 38-59 +#: File "src/app/learnocaml_common.ml", line 585, characters 34-55 1209, 38-59 msgid "difficulty: %d / 40" msgstr "difficultĂ©: %d / 40" -#: File "src/app/learnocaml_common.ml", line 610, characters 30-75 +#: File "src/app/learnocaml_common.ml", line 620, characters 30-75 msgid "No description available for this exercise." msgstr "Aucune description pour cet exercice." -#: File "src/app/learnocaml_common.ml", line 633, characters 32-41 +#: File "src/app/learnocaml_common.ml", line 643, characters 32-41 #: "src/app/learnocaml_index_main.ml", 132, 54-63 msgid "project" msgstr "projet" -#: File "src/app/learnocaml_common.ml", line 634, characters 32-41 +#: File "src/app/learnocaml_common.ml", line 644, characters 32-41 #: "src/app/learnocaml_index_main.ml", 133, 54-63 msgid "problem" msgstr "problĂšme" -#: File "src/app/learnocaml_common.ml", line 635, characters 33-43 +#: File "src/app/learnocaml_common.ml", line 645, characters 33-43 #: "src/app/learnocaml_index_main.ml", 134, 55-65 msgid "exercise" msgstr "exercice" -#: File "src/app/learnocaml_common.ml", line 787, characters 26-33 +#: File "src/app/learnocaml_common.ml", line 797, characters 26-33 msgid "Clear" msgstr "Effacer" -#: File "src/app/learnocaml_common.ml", line 792, characters 25-32 917, 24-31 +#: File "src/app/learnocaml_common.ml", line 802, characters 25-32 927, 24-31 msgid "Reset" msgstr "RĂ©initialiser" -#: File "src/app/learnocaml_common.ml", line 797, characters 22-35 +#: File "src/app/learnocaml_common.ml", line 807, characters 22-35 msgid "Eval phrase" msgstr "Évaluer la phrase" -#: File "src/app/learnocaml_common.ml", line 812, characters 24-51 +#: File "src/app/learnocaml_common.ml", line 822, characters 24-51 msgid "Preparing the environment" msgstr "PrĂ©paration de l'environnement" -#: File "src/app/learnocaml_common.ml", line 813, characters 39-47 818, 37-45 +#: File "src/app/learnocaml_common.ml", line 823, characters 39-47 828, 37-45 msgid "Editor" msgstr "Éditeur" -#: File "src/app/learnocaml_common.ml", line 814, characters 41-51 -#: "src/app/learnocaml_index_main.ml", 691, 30-40 +#: File "src/app/learnocaml_common.ml", line 824, characters 41-51 +#: "src/app/learnocaml_index_main.ml", 692, 30-40 msgid "Toplevel" msgstr "Toplevel" -#: File "src/app/learnocaml_common.ml", line 815, characters 39-47 827, +#: File "src/app/learnocaml_common.ml", line 825, characters 39-47 837, #: "src/app/learnocaml_exercise_main.ml", 58, 30-38 62, 67, #: "src/app/learnocaml_student_view.ml", 383, 28-36 396, 400, 405, msgid "Report" msgstr "Rapport" -#: File "src/app/learnocaml_common.ml", line 816, characters 37-47 +#: File "src/app/learnocaml_common.ml", line 826, characters 37-47 msgid "Exercise" msgstr "Exercice" -#: File "src/app/learnocaml_common.ml", line 817, characters 37-46 +#: File "src/app/learnocaml_common.ml", line 827, characters 37-46 msgid "Details" msgstr "DĂ©tails" -#: File "src/app/learnocaml_common.ml", line 819, characters 27-70 +#: File "src/app/learnocaml_common.ml", line 829, characters 27-70 msgid "Click the Grade button to get your report" msgstr "Cliquez sur le bouton Noter pour obtenir votre rapport" -#: File "src/app/learnocaml_common.ml", line 824, characters 22-44 +#: File "src/app/learnocaml_common.ml", line 834, characters 22-44 msgid "Loading student data" msgstr "Chargement des informations sur les Ă©tudiants" -#: File "src/app/learnocaml_common.ml", line 825, characters 38-45 +#: File "src/app/learnocaml_common.ml", line 835, characters 38-45 msgid "Stats" msgstr "Statistiques" -#: File "src/app/learnocaml_common.ml", line 826, characters 37-48 -#: "src/app/learnocaml_index_main.ml", 688, 29-40 -#: "src/app/learnocaml_teacher_tab.ml", 329, 18-29 +#: File "src/app/learnocaml_common.ml", line 836, characters 37-48 +#: "src/app/learnocaml_index_main.ml", 689, 29-40 +#: "src/app/learnocaml_teacher_tab.ml", 375, 21-32 #: "src/app/learnocaml_exercise_main.ml", 204, 23-34 msgid "Exercises" msgstr "Exercices" -#: File "src/app/learnocaml_common.ml", line 828, characters 37-46 +#: File "src/app/learnocaml_common.ml", line 838, characters 37-46 msgid "Subject" msgstr "ÉnoncĂ©" -#: File "src/app/learnocaml_common.ml", line 829, characters 39-47 +#: File "src/app/learnocaml_common.ml", line 839, characters 39-47 msgid "Answer" msgstr "RĂ©ponse" -#: File "src/app/learnocaml_common.ml", line 918, characters 22-42 968, 34-54 +#: File "src/app/learnocaml_common.ml", line 928, characters 22-42 978, 34-54 msgid "START FROM SCRATCH" msgstr "TOUT RECOMMENCER" -#: File "src/app/learnocaml_common.ml", line 919, characters 16-65 969, 28-77 +#: File "src/app/learnocaml_common.ml", line 929, characters 16-65 979, 28-77 msgid "This will discard all your edits. Are you sure?" msgstr "Toutes vos modifications seront perdues. Êtes-vous sĂ»r·e ?" -#: File "src/app/learnocaml_common.ml", line 933, characters 28-45 1152, +#: File "src/app/learnocaml_common.ml", line 943, characters 28-45 1179, #: "src/app/learnocaml_index_main.ml", 574, msgid "TOKEN NOT FOUND" msgstr "TOKEN NON TROUVÉ" -#: File "src/app/learnocaml_common.ml", line 934, characters 17-60 1153, +#: File "src/app/learnocaml_common.ml", line 944, characters 17-60 1180, #: "src/app/learnocaml_index_main.ml", 575, msgid "The entered token couldn't be recognised." msgstr "Le token entrĂ© n'a pas Ă©tĂ© reconnu." -#: File "src/app/learnocaml_common.ml", line 952, characters 34-61 957, 25-52 +#: File "src/app/learnocaml_common.ml", line 962, characters 34-61 967, 25-52 msgid "Reload latest graded code" msgstr "Reprendre le dernier code notĂ©" -#: File "src/app/learnocaml_common.ml", line 953, characters 28-99 +#: File "src/app/learnocaml_common.ml", line 963, characters 28-99 msgid "This will replace your code with your last graded code. Are you sure?" -msgstr "Cela va remplacer votre code par la derniĂšre version Ă©valuĂ©e. Êtes-vous sĂ»r·e ?" +msgstr "" +"Cela va remplacer votre code par la derniĂšre version Ă©valuĂ©e. Êtes-vous " +"sĂ»r·e ?" -#: File "src/app/learnocaml_common.ml", line 960, characters 34-61 965, 25-52 +#: File "src/app/learnocaml_common.ml", line 970, characters 34-61 975, 25-52 msgid "Reload latest saved draft" msgstr "Reprendre le dernier code synchronisĂ©" -#: File "src/app/learnocaml_common.ml", line 961, characters 28-99 +#: File "src/app/learnocaml_common.ml", line 971, characters 28-99 msgid "This will replace your code with your last saved draft. Are you sure?" -msgstr "Cela va remplacer votre code par la derniĂšre version synchronisĂ©e. Êtes-vous sĂ»r·e ?" +msgstr "" +"Cela va remplacer votre code par la derniĂšre version synchronisĂ©e. Êtes-vous " +"sĂ»r·e ?" -#: File "src/app/learnocaml_common.ml", line 972, characters 25-52 +#: File "src/app/learnocaml_common.ml", line 982, characters 25-52 msgid "Reset to initial template" msgstr "RĂ©initialiser le code" -#: File "src/app/learnocaml_common.ml", line 975, characters 9-17 +#: File "src/app/learnocaml_common.ml", line 985, characters 9-17 msgid "Reload" msgstr "Recharger" -#: File "src/app/learnocaml_common.ml", line 982, characters 27-37 +#: File "src/app/learnocaml_common.ml", line 1011, characters 27-37 msgid "Download" msgstr "TĂ©lĂ©charger" -#: File "src/app/learnocaml_common.ml", line 990, characters 22-33 +#: File "src/app/learnocaml_common.ml", line 1019, characters 22-33 msgid "Eval code" msgstr "ExĂ©cuter le code" -#: File "src/app/learnocaml_common.ml", line 1000, characters 23-29 +#: File "src/app/learnocaml_common.ml", line 1029, characters 23-29 msgid "Sync" msgstr "Sync" -#: File "src/app/learnocaml_common.ml", line 1069, characters 34-49 1104, +#: File "src/app/learnocaml_common.ml", line 1098, characters 34-49 1133, msgid "OCaml prelude" msgstr "PrĂ©lude OCaml" -#: File "src/app/learnocaml_common.ml", line 1076, characters 59-65 1111, +#: File "src/app/learnocaml_common.ml", line 1105, characters 59-65 1140, msgid "Hide" msgstr "Cacher" -#: File "src/app/learnocaml_common.ml", line 1081, characters 59-65 1118, +#: File "src/app/learnocaml_common.ml", line 1110, characters 59-65 1147, msgid "Show" msgstr "Montrer" -#: File "src/app/learnocaml_common.ml", line 1143, characters 18-36 +#: File "src/app/learnocaml_common.ml", line 1170, characters 18-36 #: "src/app/learnocaml_index_main.ml", 629, 31-49 msgid "Enter your token" msgstr "Entrez votre token" -#: File "src/app/learnocaml_common.ml", line 1188, characters 22-35 +#: File "src/app/learnocaml_common.ml", line 1215, characters 22-35 msgid "Difficulty:" msgstr "DifficultĂ© :" -#: File "src/app/learnocaml_common.ml", line 1202, characters 39-49 +#: File "src/app/learnocaml_common.ml", line 1229, characters 39-49 msgid "Kind: %s" msgstr "Type : %s" -#: File "src/app/learnocaml_common.ml", line 1343, characters 46-59 +#: File "src/app/learnocaml_common.ml", line 1370, characters 46-59 msgid "Identifier:" msgstr "Identifiant de l'exercice :" -#: File "src/app/learnocaml_common.ml", line 1347, characters 48-57 +#: File "src/app/learnocaml_common.ml", line 1374, characters 48-57 msgid "Author:" msgstr "Auteur :" -#: File "src/app/learnocaml_common.ml", line 1348, characters 47-57 +#: File "src/app/learnocaml_common.ml", line 1375, characters 47-57 msgid "Authors:" msgstr "Auteurs :" -#: File "src/app/learnocaml_common.ml", line 1353, characters 31-48 +#: File "src/app/learnocaml_common.ml", line 1380, characters 31-48 msgid "Skills trained:" msgstr "CompĂ©tences pratiquĂ©es :" -#: File "src/app/learnocaml_common.ml", line 1357, characters 31-49 +#: File "src/app/learnocaml_common.ml", line 1384, characters 31-49 msgid "Skills required:" msgstr "CompĂ©tences requises :" -#: File "src/app/learnocaml_common.ml", line 1362, characters 36-57 +#: File "src/app/learnocaml_common.ml", line 1389, characters 36-57 msgid "Previous exercises:" msgstr "Exercices prĂ©cĂ©dents :" -#: File "src/app/learnocaml_common.ml", line 1365, characters 35-52 +#: File "src/app/learnocaml_common.ml", line 1392, characters 35-52 msgid "Next exercises:" msgstr "Exercices suivants :" -#: File "src/app/learnocaml_common.ml", line 1370, characters 26-36 +#: File "src/app/learnocaml_common.ml", line 1397, characters 26-36 msgid "Metadata" msgstr "MĂ©tadonnĂ©es" @@ -523,41 +527,41 @@ msgstr "Vous avez dĂ©jĂ  un token ? Par ici !" msgid "Connect" msgstr "Se connecter" -#: File "src/app/learnocaml_index_main.ml", line 631, characters 35-50 -msgid "Send feedback to Learn-OCaml developers" -msgstr "Envoyer un commentaire aux dĂ©veloppeurs Learn-OCaml" - #: File "src/app/learnocaml_index_main.ml", line 638, characters 9-19 640, -#: "src/app/learnocaml_teacher_tab.ml", 558, 22-32 +#: "src/app/learnocaml_teacher_tab.ml", 612, 22-32 msgid "Nickname" msgstr "Pseudonyme" -#: File "src/app/learnocaml_index_main.ml", line 675, characters 38-59 +#: File "src/app/learnocaml_index_main.ml", line 647, characters 7-48 +msgid "Send feedback to Learn-OCaml developers" +msgstr "Envoyer un commentaire aux dĂ©veloppeurs Learn-OCaml" + +#: File "src/app/learnocaml_index_main.ml", line 676, characters 38-59 msgid "Choose an activity." msgstr "SĂ©lectionnez une activitĂ©." -#: File "src/app/learnocaml_index_main.ml", line 684, characters 31-42 +#: File "src/app/learnocaml_index_main.ml", line 685, characters 31-42 msgid "Tutorials" msgstr "Tutoriels" -#: File "src/app/learnocaml_index_main.ml", line 686, characters 29-38 +#: File "src/app/learnocaml_index_main.ml", line 687, characters 29-38 msgid "Lessons" msgstr "Cours" -#: File "src/app/learnocaml_index_main.ml", line 693, characters 32-44 +#: File "src/app/learnocaml_index_main.ml", line 694, characters 32-44 #: "src/app/learnocaml_playground_main.ml", 77, 23-35 msgid "Playground" msgstr "Bac-Ă -sable" -#: File "src/app/learnocaml_index_main.ml", line 696, characters 28-35 +#: File "src/app/learnocaml_index_main.ml", line 697, characters 28-35 msgid "Teach" msgstr "Enseignement" -#: File "src/app/learnocaml_index_main.ml", line 794, characters 15-69 +#: File "src/app/learnocaml_index_main.ml", line 795, characters 15-69 msgid "Be sure to write down your token before logging out:" msgstr "Assurez-vous d'avoir notĂ© votre token :" -#: File "src/app/learnocaml_index_main.ml", lines 796-798, characters 15-26 +#: File "src/app/learnocaml_index_main.ml", lines 797-799, characters 15-26 msgid "" "WARNING: the data could not be synchronised with the server. Logging out " "will lose your local changes, be sure you exported a backup." @@ -566,40 +570,40 @@ msgstr "" "En vous dĂ©connectant, vous perdrez tous les changements locaux, Ă  moins " "d'avoir exportĂ© votre espace de travail au prĂ©alable." -#: File "src/app/learnocaml_index_main.ml", line 800, characters 22-30 45-53 -#: 822, 9-17 +#: File "src/app/learnocaml_index_main.ml", line 801, characters 22-30 45-53 +#: 823, 9-17 msgid "Logout" msgstr "DĂ©connexion" -#: File "src/app/learnocaml_index_main.ml", line 813, characters 9-21 +#: File "src/app/learnocaml_index_main.ml", line 814, characters 9-21 msgid "Show token" msgstr "Afficher le token" -#: File "src/app/learnocaml_index_main.ml", line 816, characters 9-25 +#: File "src/app/learnocaml_index_main.ml", line 817, characters 9-25 msgid "Sync workspace" msgstr "Synchroniser" -#: File "src/app/learnocaml_index_main.ml", line 819, characters 9-25 +#: File "src/app/learnocaml_index_main.ml", line 820, characters 9-25 msgid "Export to file" msgstr "Exporter vers un fichier" -#: File "src/app/learnocaml_index_main.ml", line 820, characters 9-17 +#: File "src/app/learnocaml_index_main.ml", line 821, characters 9-17 msgid "Import" msgstr "Importer" -#: File "src/app/learnocaml_index_main.ml", line 821, characters 9-36 +#: File "src/app/learnocaml_index_main.ml", line 822, characters 9-36 msgid "Download all source files" msgstr "TĂ©lĂ©charger tous les fichiers sources" -#: File "src/app/learnocaml_index_main.ml", line 827, characters 38-44 +#: File "src/app/learnocaml_index_main.ml", line 828, characters 38-44 msgid "Menu" msgstr "Menu" -#: File "src/app/learnocaml_teacher_tab.ml", line 75, characters 20-35 +#: File "src/app/learnocaml_teacher_tab.ml", line 106, characters 20-35 msgid "TEACHER TOKEN" msgstr "TOKEN PROF." -#: File "src/app/learnocaml_teacher_tab.ml", lines 76-77, characters 26-42 +#: File "src/app/learnocaml_teacher_tab.ml", lines 107-108, characters 26-42 msgid "" "New teacher token created:\n" "%s\n" @@ -611,112 +615,365 @@ msgstr "" "\n" "Notez-le !" -#: File "src/app/learnocaml_teacher_tab.ml", line 257, characters 48-54 +#: File "src/app/learnocaml_teacher_tab.ml", line 304, characters 52-58 msgid "Open" msgstr "Ouvert" -#: File "src/app/learnocaml_teacher_tab.ml", line 258, characters 52-60 +#: File "src/app/learnocaml_teacher_tab.ml", line 305, characters 56-64 msgid "Closed" msgstr "FermĂ©" -#: File "src/app/learnocaml_teacher_tab.ml", line 259, characters 58-68 260, -#: 42-52 +#: File "src/app/learnocaml_teacher_tab.ml", line 307, characters 41-51 msgid "Assigned" msgstr "Devoir" -#: File "src/app/learnocaml_teacher_tab.ml", line 320, characters 49-61 340, +#: File "src/app/learnocaml_teacher_tab.ml", line 308, characters 57-68 +msgid "Open/Assg" +msgstr "Ouvert/Devoir" + +#: File "src/app/learnocaml_teacher_tab.ml", line 368, characters 49-61 391, #: 48-60 msgid "Loading..." msgstr "Chargement..." -#: File "src/app/learnocaml_teacher_tab.ml", line 394, characters 17-38 +#: File "src/app/learnocaml_teacher_tab.ml", line 445, characters 17-38 msgid "any future students" msgstr "tout nouvel Ă©tudiant" -#: File "src/app/learnocaml_teacher_tab.ml", line 542, characters 18-28 +#: File "src/app/learnocaml_teacher_tab.ml", line 580, characters 21-31 msgid "Students" msgstr "Étudiants" -#: File "src/app/learnocaml_teacher_tab.ml", line 552, characters 20-29 +#: File "src/app/learnocaml_teacher_tab.ml", line 606, characters 20-29 msgid "Sort by" msgstr "Tri par" -#: File "src/app/learnocaml_teacher_tab.ml", line 560, characters 22-29 +#: File "src/app/learnocaml_teacher_tab.ml", line 614, characters 22-29 msgid "Token" msgstr "Token" -#: File "src/app/learnocaml_teacher_tab.ml", line 562, characters 22-37 +#: File "src/app/learnocaml_teacher_tab.ml", line 616, characters 22-37 msgid "Creation date" msgstr "Date d'entrĂ©e" -#: File "src/app/learnocaml_teacher_tab.ml", line 564, characters 22-28 +#: File "src/app/learnocaml_teacher_tab.ml", line 618, characters 22-28 msgid "Tags" msgstr "Tags" -#: File "src/app/learnocaml_teacher_tab.ml", line 569, characters 46-52 +#: File "src/app/learnocaml_teacher_tab.ml", line 623, characters 46-52 msgid "tags" msgstr "tags" -#: File "src/app/learnocaml_teacher_tab.ml", line 645, characters 16-28 +#: File "src/app/learnocaml_teacher_tab.ml", line 699, characters 16-28 msgid "1 exercise" msgstr "1 exercice" -#: File "src/app/learnocaml_teacher_tab.ml", line 646, characters 32-46 +#: File "src/app/learnocaml_teacher_tab.ml", line 700, characters 32-46 msgid "%d exercises" msgstr "%d exercices" -#: File "src/app/learnocaml_teacher_tab.ml", line 649, characters 23-34 +#: File "src/app/learnocaml_teacher_tab.ml", line 703, characters 23-34 msgid "1 student" msgstr "1 Ă©tudiant" -#: File "src/app/learnocaml_teacher_tab.ml", line 650, characters 39-52 +#: File "src/app/learnocaml_teacher_tab.ml", line 704, characters 39-52 msgid "%d students" msgstr "%d Ă©tudiants" -#: File "src/app/learnocaml_teacher_tab.ml", line 651, characters 38-52 +#: File "src/app/learnocaml_teacher_tab.ml", line 705, characters 38-52 msgid "%d+ students" msgstr "%d+ Ă©tudiants" -#: File "src/app/learnocaml_teacher_tab.ml", line 718, characters 45-61 +#: File "src/app/learnocaml_teacher_tab.ml", line 772, characters 45-61 msgid "New assignment" msgstr "Nouveau devoir" -#: File "src/app/learnocaml_teacher_tab.ml", line 821, characters 16-28 +#: File "src/app/learnocaml_teacher_tab.ml", line 873, characters 16-28 msgid "Open/Close" msgstr "Ouvrir/Fermer" -#: File "src/app/learnocaml_teacher_tab.ml", line 827, characters 47-64 +#: File "src/app/learnocaml_teacher_tab.ml", line 879, characters 47-64 msgid "required skills" msgstr "comp. requises" -#: File "src/app/learnocaml_teacher_tab.ml", line 831, characters 47-63 +#: File "src/app/learnocaml_teacher_tab.ml", line 883, characters 47-63 msgid "trained skills" msgstr "comp. travaillĂ©es" -#: File "src/app/learnocaml_teacher_tab.ml", line 840, characters 36-49 +#: File "src/app/learnocaml_teacher_tab.ml", line 892, characters 17-30 msgid "Assignments" msgstr "Devoirs" -#: File "src/app/learnocaml_teacher_tab.ml", line 923, characters 18-25 +#: File "src/app/learnocaml_teacher_tab.ml", line 982, characters 18-25 msgid "Apply" msgstr "Appliquer" -#: File "src/app/learnocaml_teacher_tab.ml", line 924, characters 54-63 +#: File "src/app/learnocaml_teacher_tab.ml", line 983, characters 54-63 msgid "Actions" msgstr "Actions" -#: File "src/app/learnocaml_teacher_tab.ml", line 927, characters 23-49 +#: File "src/app/learnocaml_teacher_tab.ml", line 986, characters 23-49 msgid "Create new teacher token" msgstr "CrĂ©er un nouveau token enseignant" -#: File "src/app/learnocaml_teacher_tab.ml", line 929, characters 23-53 -msgid "Download student data as CSV" -msgstr "Exporter les donnĂ©es Ă©tudiants en CSV" +#: File "src/app/learnocaml_teacher_tab.ml", line 988, characters 23-81 +msgid "Download the data for selected students/exercises as CSV" +msgstr "TĂ©lĂ©charger un CSV des exercices/Ă©tudiants sĂ©lectionnĂ©s" -#: File "src/app/learnocaml_teacher_tab.ml", line 1101, characters 55-72 +#: File "src/app/learnocaml_teacher_tab.ml", line 1163, characters 55-72 msgid "Unsaved changes" msgstr "Modifications non sauvegardĂ©es" +#: File "src/app/learnocaml_teacher_tab_doc.ml", line 11, characters 5-25 +msgid "Handling exercises" +msgstr "Gestion des exercices" + +#: File "src/app/learnocaml_teacher_tab_doc.ml", lines 11-46, characters 31-10 +msgid "" +"\n" +"> Don't forget to click \"Apply\" on the bottom-right after any changes\n" +"\n" +"- **Filtering exercises**
\n" +" Use the 🔍 box on top.\n" +" - type for raw text search on the titles\n" +" - click again to list the tags and filter based on those\n" +" - multiple search terms are allowed\n" +"\n" +"- **Selecting which exercises can be seen by the students**\n" +" - select the exercises with a click (the box on top (de)selects all; you " +"can\n" +" also click folder titles)\n" +" - click the [Open/Close] button to toggle\n" +" - for complex selections, use the filter box then select all\n" +"\n" +"- **Defining exercise skill tags**
\n" +" Those can also be pre-defined in the exercises metadata.\n" +" 1. Select the exercises to tag\n" +" 2. Select or type a new tag in one of the boxes on the lower-right of the " +"pane\n" +" (left for requirements, right for skills trained by the exercise)\n" +" 3. Click the corresponding [+] button to add the tag ([-] to remove)\n" +"\n" +"- **Checking an exercise contents**\n" +" - double-click an exercise\n" +" - it will be opened in a new tab\n" +"\n" +"- **Classifying students answers**
\n" +" As documented\n" +"[here](https://ocaml-sf.org/learn-ocaml/howto-classify-students-answers." +"html):\n" +" - Assume you want to analyze students' definitions of function `foo` in\n" +" exercise `exo`\n" +" - Hold Ctrl (on macOS: Hold ⌘) and left click on `exo` in the list of\n" +" exercises\n" +" - Enter `foo` in the dialog box\n" +" - The dissimilarity analysis will be opened in a new tab\n" +msgstr "" +"\n" +"> Don't forget to click \"Apply\" on the bottom-right after any changes\n" +"\n" +"- **Filtering exercises**
\n" +" Use the 🔍 box on top.\n" +" - type for raw text search on the titles\n" +" - click again to list the tags and filter based on those\n" +" - multiple search terms are allowed\n" +"\n" +"- **Selecting which exercises can be seen by the students**\n" +" - select the exercises with a click (the box on top (de)selects all; you can\n" +" also click folder titles)\n" +" - click the [Open/Close] button to toggle\n" +" - for complex selections, use the filter box then select all\n" +"\n" +"- **Defining exercise skill tags**
\n" +" Those can also be pre-defined in the exercises metadata.\n" +" 1. Select the exercises to tag\n" +" 2. Select or type a new tag in one of the boxes on the lower-right of the pane\n" +" (left for requirements, right for skills trained by the exercise)\n" +" 3. Click the corresponding [+] button to add the tag ([-] to remove)\n" +"\n" +"- **Checking an exercise contents**\n" +" - double-click an exercise\n" +" - it will be opened in a new tab\n" +"\n" +"- **Classifying students answers**
\n" +" As documented\n" +"[here](https://ocaml-sf.org/learn-ocaml/howto-classify-students-answers.html):\n" +" - Assume you want to analyze students' definitions of function `foo` in\n" +" exercise `exo`\n" +" - Hold Ctrl (on macOS: Hold ⌘) and left click on `exo` in the list of\n" +" exercises\n" +" - Enter `foo` in the dialog box\n" +" - The dissimilarity analysis will be opened in a new tab\n" + +#: File "src/app/learnocaml_teacher_tab_doc.ml", line 49, characters 5-24 +msgid "Handling students" +msgstr "Gestion des Ă©tudiants" + +#: File "src/app/learnocaml_teacher_tab_doc.ml", lines 49-85, characters 30-10 +msgid "" +"\n" +"> Don't forget to click \"Apply\" on the bottom-right after any changes\n" +"\n" +"- **Filtering students**
\n" +" Use the 🔍 box on top.\n" +" - type for raw text search on the nicknames\n" +" - click again to list the tags and filter based on those\n" +" - multiple search terms are allowed\n" +"\n" +"- **Tagging students**
\n" +" Tags can be used for groups, level, etc.\n" +" 1. select the students to tag. To select a group, use the filter box and " +"the\n" +" box on top to select all. Use the `Sort by` box on the top right to " +"easily\n" +" select new students or untagged studends\n" +" 2. select or type the tag on the box to the lower-right\n" +" 3. Click the corresponding [+] button to add the tag ([-] to remove)\n" +"\n" +"- **Following a student**\n" +" - at a glance:\n" +" a bar on the right of each student shows a short insight of their " +"progress,\n" +" - vertical stripes correspond to the exercises, in order\n" +" - grey is shown for exercises not attempted\n" +" - the color otherwise varies from green (good) to red (bad) depending on " +"the\n" +" grade\n" +" - in detail:\n" +" - double-click on the student line to open their track record in a new " +"tab\n" +" - there you can see their detailed results per exercise, and check " +"their\n" +" answers\n" +" - see \"Classifying students answers\" in the exercise pane guide for a " +"way\n" +" to compare solutions between students\n" +"\n" +"- **Downloading the grades**\n" +" - use the [Actions] menu at the bottom to export a CSV file containing a " +"line\n" +" per student, and two columns (grade and date) per exercise\n" +" - if any exercises or students are selected, the CSV will only contain " +"those\n" +" - otherwise everything is exported\n" +msgstr "" +"\n" +"> Don't forget to click \"Apply\" on the bottom-right after any changes\n" +"\n" +"- **Filtering students**
\n" +" Use the 🔍 box on top.\n" +" - type for raw text search on the nicknames\n" +" - click again to list the tags and filter based on those\n" +" - multiple search terms are allowed\n" +"\n" +"- **Tagging students**
\n" +" Tags can be used for groups, level, etc.\n" +" 1. select the students to tag. To select a group, use the filter box and the\n" +" box on top to select all. Use the `Sort by` box on the top right to easily\n" +" select new students or untagged studends\n" +" 2. select or type the tag on the box to the lower-right\n" +" 3. Click the corresponding [+] button to add the tag ([-] to remove)\n" +"\n" +"- **Following a student**\n" +" - at a glance:\n" +" a bar on the right of each student shows a short insight of their progress,\n" +" - vertical stripes correspond to the exercises, in order\n" +" - grey is shown for exercises not attempted\n" +" - the color otherwise varies from green (good) to red (bad) depending on the\n" +" grade\n" +" - in detail:\n" +" - double-click on the student line to open their track record in a new tab\n" +" - there you can see their detailed results per exercise, and check their\n" +" answers\n" +" - see \"Classifying students answers\" in the exercise pane guide for a way\n" +" to compare solutions between students\n" +"\n" +"- **Downloading the grades**\n" +" - use the [Actions] menu at the bottom to export a CSV file containing a line\n" +" per student, and two columns (grade and date) per exercise\n" +" - if any exercises or students are selected, the CSV will only contain those\n" +" - otherwise everything is exported\n" + +#: File "src/app/learnocaml_teacher_tab_doc.ml", line 88, characters 5-27 +msgid "Handling assignments" +msgstr "Gestion des devoirs" + +#: File "src/app/learnocaml_teacher_tab_doc.ml", lines 88-118, characters 33-10 +msgid "" +"\n" +"Assignments provide a way to open select exercises to select students for a\n" +"given period of time. Once due, the students will be restricted to read-" +"only\n" +"access.\n" +"\n" +"- **Creating an assignment**\n" +" 1. Select exercises, as above\n" +" 2. Select students, as above. You can use the \"any future students\" line " +"to\n" +" apply to students who haven't yet registered\n" +" 3. Click the \"New assignment\" button at the bottom\n" +" 4. Edit the two dates (start and end of the assignment, resp.)\n" +" 5. Don't forget to [Apply] on the bottom right\n" +" 6. Click the assignment again to unselect it and quit assignment mode\n" +"\n" +"- **Editing an assignment**\n" +" - select it in the bottom pane\n" +" - the corresponding exercises and students are automatically selected. " +"Update\n" +" the selection as needed\n" +" - click the assignment again when finished (to quit editing mode), and " +"don't\n" +" forget to [Apply]\n" +"\n" +"- **Following assignments**\n" +" - an additional small \"progression\" bar is shown for each assignment in " +"the\n" +" students pane\n" +" - past assignment dates are outlined in red\n" +" - when selecting students, exercises assigned to any of them will be " +"greyed\n" +" out to prevent assigning multiple times the same exercise to the same\n" +" student\n" +" - conversely, when selecting exercises, students who already have them\n" +" assigned are greyed out\n" +msgstr "" +"\n" +"Assignments provide a way to open select exercises to select students for a\n" +"given period of time. Once due, the students will be restricted to read-" +"only\n" +"access.\n" +"\n" +"- **Creating an assignment**\n" +" 1. Select exercises, as above\n" +" 2. Select students, as above. You can use the \"any future students\" line " +"to\n" +" apply to students who haven't yet registered\n" +" 3. Click the \"New assignment\" button at the bottom\n" +" 4. Edit the two dates (start and end of the assignment, resp.)\n" +" 5. Don't forget to [Apply] on the bottom right\n" +" 6. Click the assignment again to unselect it and quit assignment mode\n" +"\n" +"- **Editing an assignment**\n" +" - select it in the bottom pane\n" +" - the corresponding exercises and students are automatically selected. " +"Update\n" +" the selection as needed\n" +" - click the assignment again when finished (to quit editing mode), and " +"don't\n" +" forget to [Apply]\n" +"\n" +"- **Following assignments**\n" +" - an additional small \"progression\" bar is shown for each assignment in " +"the\n" +" students pane\n" +" - past assignment dates are outlined in red\n" +" - when selecting students, exercises assigned to any of them will be " +"greyed\n" +" out to prevent assigning multiple times the same exercise to the same\n" +" student\n" +" - conversely, when selecting exercises, students who already have them\n" +" assigned are greyed out\n" + #: File "src/grader/grading.ml", line 16, characters 28-67 msgid "" "Exercise definition error %s:\n" @@ -966,6 +1223,9 @@ msgstr "" msgid "Unexpected error:\n" msgstr "Erreur inattendue:\n" +#~ msgid "Download student data as CSV" +#~ msgstr "Exporter les données étudiants en CSV" + #~ msgid "Fetch from server" #~ msgstr "Télécharger du serveur" @@ -1050,11 +1310,11 @@ msgstr "Erreur inattendue:\n" #~ "icon_upload_white.svg\" class=\"icon\" alt=\"Restaurer\">." #~ msgid "" -#~ "Save online using the \"sync\" button above." +#~ "Save online using the \"sync\" button above." #~ msgstr "" -#~ "Sauvez sur le serveur avec le bouton \"Sync\"." +#~ "Sauvez sur le serveur avec le bouton \"Sync\"." #~ msgid "Before log out, be sure to save your token" #~ msgstr "Avant de vous déconnecter, assurez-vous de sauvegarder votre token" @@ -1088,4 +1348,3 @@ msgstr "Erreur inattendue:\n" #~ msgid "This session has been closed. You can close this tab." #~ msgstr "La session a été fermée. Vous pouvez fermer cet onglet." -