Skip to content

Commit

Permalink
feat(ui): teacher tab: Display last synced student's draft (≠ graded …
Browse files Browse the repository at this point in the history
…code) (#548)

* feat: test implementation

* feat: working implementation

* feat: internationalization and class ongoing

* feat: translations

* feat: css code

* refactor: move draft tab

Apply a tab order by decreasing importance:
  list (exos) → stats → report →
  editor (graded code) → draft (saved code) →
  text (subject)

Note:
  It would suffice to reorder the buttons in student-view.html,
  but for consistency the other "sequences" are also reordered.

* feat: comments for fr.po file

* feat: fixed bug when switching exercise

* feat: test implementation for the text field displaying time of sync

* feat: seperate divs for sync_time & draft_editor

* feat: change of id in learnocaml-student-view.ml

Co-authored-by: Erik Martin-Dorel <erik@martin-dorel.org>

* feat: change of id in student-view.html

Co-authored-by: Erik Martin-Dorel <erik@martin-dorel.org>

* feat: sync date reformatted

* feat: implementation for flexible display

* feat: implementation for flexible display

* feat: correcting bug

* feat: CSS styling

* feat: CSS styling

* feat: internationalization

* feat: rewriting css

* feat: styling & renaming id

* fix: Apply fixes and improvements from PR review

* feat(web-app): Make (report, editor) tabs buttons likewise

* Add a CSS comment about a potential minor issue

* fix: details

* fix: fr.po

* fix: Apply review suggestions

* Update src/app/learnocaml_student_view.ml

Apply code review suggestion

Co-authored-by: Louis Gesbert <louis.gesbert@ocamlpro.com>

* style(fr.po): Revert the slightly-ugly unneeded newlines

---------

Co-authored-by: Erik Martin-Dorel <erik@martin-dorel.org>
Co-authored-by: Louis Gesbert <louis.gesbert@ocamlpro.com>
  • Loading branch information
3 people authored Jul 7, 2023
1 parent 0b150b9 commit 48583ba
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 132 deletions.
1 change: 1 addition & 0 deletions src/app/learnocaml_common.ml
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,7 @@ let set_string_translations_view () =
"learnocaml-exo-button-stats", [%i"Stats"];
"learnocaml-exo-button-list", [%i"Exercises"];
"learnocaml-exo-button-report", [%i"Report"];
"learnocaml-exo-button-draft", [%i"Draft"];
"learnocaml-exo-button-text", [%i"Subject"];
"learnocaml-exo-button-editor", [%i"Answer"];
] in set_inner_list translations
Expand Down
76 changes: 54 additions & 22 deletions src/app/learnocaml_student_view.ml
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,14 @@ module El = struct
btn = snd (id ("learnocaml-exo-button-" ^ name));
tab = snd (id ("learnocaml-exo-tab-" ^ name));
}
let stats = tid "stats"
let list = tid "list"
let stats = tid "stats"
let report = tid "report"
let editor = tid "editor"
let draft = tid "draft"
let text = tid "text"

let all = [stats; list; report; editor; text]
let all = [list; stats; report; editor; draft; text]
end

let nickname_id, nickname = id "learnocaml-student-nickname"
Expand Down Expand Up @@ -362,44 +363,44 @@ let _exercise_selection_updater =
let previously_selected = ref None in
selected_exercise_signal |> React.S.map @@ fun id ->
let line id = find_component (El.Dyn.exercise_line_id id) in
(match !previously_selected with
| None -> ()
| Some id -> Manip.removeClass (line id) "selected");
Option.iter (fun id -> Manip.removeClass (line id) "selected") !previously_selected;
previously_selected := id;
match id with
| None -> ()
| Some id ->
Option.iter (fun id ->
Manip.addClass (line id) "selected";
let selected_tab = React.S.value tab_select_signal in
if selected_tab = El.Tabs.list || selected_tab = El.Tabs.stats then
select_tab El.Tabs.report
select_tab El.Tabs.report) id

let restore_report_button () =
let report_button = El.Tabs.(report.btn) in
Manip.removeClass report_button "success";
Manip.removeClass report_button "failure";
Manip.removeClass report_button "partial";
let editor_button = El.Tabs.(editor.btn) in
let remove_class (b1, b2) c = Manip.removeClass b1 c; Manip.removeClass b2 c in
remove_class (report_button, editor_button) "success";
remove_class (report_button, editor_button) "failure";
remove_class (report_button, editor_button) "partial";
Manip.replaceChildren report_button
Tyxml_js.Html5.[ txt [%i"Report"] ]

let display_report exo report =
let score, _failed = Report.result report in
let report_button = El.Tabs.(report.btn) in
let editor_button = El.Tabs.(editor.btn) in
let add_class (b1, b2) c = Manip.addClass b1 c; Manip.addClass b2 c in
restore_report_button ();
let grade =
let max = Learnocaml_exercise.(access File.max_score exo) in
if max = 0 then 999 else score * 100 / max
in
if grade >= 100 then begin
Manip.addClass report_button "success" ;
add_class (report_button, editor_button) "success" ;
Manip.replaceChildren report_button
Tyxml_js.Html5.[ txt [%i"Report"] ]
end else if grade = 0 then begin
Manip.addClass report_button "failure" ;
add_class (report_button, editor_button) "failure" ;
Manip.replaceChildren report_button
Tyxml_js.Html5.[ txt [%i"Report"] ]
end else begin
Manip.addClass report_button "partial" ;
add_class (report_button, editor_button) "partial" ;
let pct = Format.asprintf "%2d%%" grade in
Manip.replaceChildren report_button
Tyxml_js.Html5.[ txt [%i"Report"] ;
Expand All @@ -411,13 +412,43 @@ let display_report exo report =

let update_answer_tab, clear_answer_tab = ace_display El.Tabs.(editor.tab)

let init_draft_tab () =
let draft_time = H.div ~a:[H.a_id "learnocaml-exo-draft-time"]
[H.txt [%i"No draft available."]] in
let draft_editor = find_component "learnocaml-exo-draft-editor" in
Manip.appendChild ~before:draft_editor El.Tabs.(draft.tab) draft_time;
Manip.replaceChildren El.Tabs.(draft.btn) Tyxml_js.Html5.[ txt [%i"Draft"] ]

let update_draft, clear_draft_tab =
ace_display (find_component "learnocaml-exo-draft-editor")

let restore_draft_button () =
Manip.removeClass El.Tabs.(draft.btn) "ongoing"

let clear_tabs () =
restore_report_button ();
restore_draft_button ();
List.iter (fun t ->
Manip.replaceChildren El.Tabs.(t.tab) [])
El.Tabs.([report; text]);
clear_draft_tab ();
Manip.replaceChildren (find_component "learnocaml-exo-draft-time")
[H.txt [%i"No draft available."]];
clear_answer_tab ()

let update_draft_tab syn =
restore_draft_button ();
let draft_button = El.Tabs.(draft.btn) in
let inner_div_time, syn_draft = match syn with
| Some syn ->
let () = Manip.addClass draft_button "ongoing" in
([H.txt [%i"Ungraded draft, synced on: "]; date ~time:true @@ fst syn], snd syn)
| None ->
([H.txt [%i"No draft available."]], "")
in
Manip.replaceChildren (find_component "learnocaml-exo-draft-time") inner_div_time;
update_draft syn_draft

let update_text_tab meta exo =
let text_iframe = Dom_html.createIframe Dom_html.document in
Manip.replaceChildren El.Tabs.(text.tab) [
Expand Down Expand Up @@ -446,20 +477,19 @@ let update_report_tab exo ans =
Manip.replaceChildren El.Tabs.(report.tab)
[H.div [H.txt [%i"No report available"]]]

let update_tabs meta exo ans =
let update_tabs meta exo ans syn =
update_text_tab meta exo;
match ans with
| None -> ()
| Some ans ->
update_draft_tab syn;
Option.iter (fun ans ->
update_report_tab exo ans;
update_answer_tab ans.Answer.solution
update_answer_tab ans.Answer.solution) ans

let () =
run_async_with_log @@ fun () ->
(* set_string_translations (); *)
(* Manip.setInnerText El.version ("v."^Learnocaml_api.version); *)
Learnocaml_local_storage.init ();
(match Js_utils.get_lang() with Some l -> Ocplib_i18n.set_lang l | None -> ());
Option.iter Ocplib_i18n.set_lang (Js_utils.get_lang ());
set_string_translations_view ();
let teacher_token = Learnocaml_local_storage.(retrieve sync_token) in
if not (Token.is_teacher teacher_token) then
Expand All @@ -470,6 +500,7 @@ let () =
try Token.parse (List.assoc "token" Url.Current.arguments)
with Not_found | Failure _ -> failwith "Student token missing or invalid"
in
init_draft_tab ();
Manip.setInnerText El.token
([%i"Status of student: "] ^ Token.to_string student_token);
retrieve (Learnocaml_api.Fetch_save student_token)
Expand All @@ -488,7 +519,8 @@ let () =
>>= fun (meta, exo, _) ->
clear_tabs ();
let ans = SMap.find_opt ex_id save.Save.all_exercise_states in
update_tabs meta exo ans;
let syn = SMap.find_opt ex_id save.Save.all_exercise_editors in
update_tabs meta exo ans syn;
Lwt.return_unit
in
Lwt.return_unit
45 changes: 45 additions & 0 deletions static/css/learnocaml_student_view.css
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,22 @@
background: #e80;
color: white;
}
#learnocaml-exo-tab-buttons > #learnocaml-exo-button-editor.success {
background: #0a0;
color: white;
}
#learnocaml-exo-tab-buttons > #learnocaml-exo-button-editor.failure {
background: #b00;
color: white;
}
#learnocaml-exo-tab-buttons > #learnocaml-exo-button-editor.partial {
background: #e80;
color: white;
}
#learnocaml-exo-tab-buttons > #learnocaml-exo-button-draft.ongoing {
background: #F08080;
color: white;
}
#learnocaml-exo-button-report > .score {
padding: 0 5px;
float: right;
Expand Down Expand Up @@ -177,6 +193,25 @@
background: linear-gradient(to bottom, rgba(0,0,0,0.3) 0, transparent 100%)
}

#learnocaml-exo-tab-draft {
display: flex;
flex-flow: column;
}
#learnocaml-exo-draft-time {
flex: 0 1 auto;
border-bottom: 1px solid black;
padding: 5px;
font-family: 'Inconsolata', monospace;
font-size: 18px;
background: #eee;
}
#learnocaml-exo-draft-editor {
flex: 1 1 auto;
z-index: 997;
background: #eee;
padding: 0;
}

/* -------------------- two columns mode -------------------------- */
@media (min-width: 1200px) {
#learnocaml-exo-toolbar {
Expand Down Expand Up @@ -228,6 +263,14 @@
background: linear-gradient(to bottom, #444 0, #444 5px,
rgba(0,0,0,0.5) 5px, transparent 10px) ;
}
#learnocaml-exo-draft-editor::after {
position: absolute;
z-index: 1005;
left: 0; top: 0; height: 10px; width: 100%;
content: "";
background: linear-gradient(to bottom, #444 0, #444 5px,
rgba(0,0,0,0.5) 5px, transparent 10px) ;
}
}
@media (min-width: 550px) and (max-width: 1199px) {
#learnocaml-exo-tab-buttons {
Expand Down Expand Up @@ -335,6 +378,8 @@ tr.exercise-highlight {
}

/* -------------------- ACE overriding ---------------------------- */
/* XXX Erik: this section looks like dead CSS code
See https://github.com/ocaml-sf/learn-ocaml/issues/556 */
#learnocaml-exo-editor-pane {
font-size: 16px;
font-family: 'Inconsolata', monospace;
Expand Down
10 changes: 7 additions & 3 deletions static/student-view.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,27 +53,31 @@
</div>
</div>
<div id="learnocaml-exo-tab-buttons">
<button id="learnocaml-exo-button-list">Exercises</button>
<button id="learnocaml-exo-button-stats" class="front-tab" disabled="">
Stats
</button>
<button id="learnocaml-exo-button-list">Exercises</button>
<button id="learnocaml-exo-button-report">
Report
</button>
<button id="learnocaml-exo-button-editor">Answer</button>
<button id="learnocaml-exo-button-draft">Draft</button>
<button id="learnocaml-exo-button-text">Subject</button>
</div>
<div id="learnocaml-exo-tabs">
<div id="learnocaml-exo-tab-stats" class="front-tab"></div>
<div id="learnocaml-exo-tab-list"></div>
<div id="learnocaml-exo-tab-stats" class="front-tab"></div>
<div id="learnocaml-exo-tab-report"></div>
<div id="learnocaml-exo-tab-editor">
<div id="learnocaml-exo-prelude"></div>
<div id="learnocaml-exo-editor-pane" class="pane"></div>
<div id="learnocaml-exo-editor-toolbar" class="buttons">
</div>
</div>
<div id="learnocaml-exo-tab-draft">
<div id="learnocaml-exo-draft-editor"></div>
</div>
<div id="learnocaml-exo-tab-text"></div>
<div id="learnocaml-exo-tab-report"></div>
</div>
</body>
</html>
Loading

0 comments on commit 48583ba

Please sign in to comment.