diff --git a/src/app/learnocaml_teacher_tab.ml b/src/app/learnocaml_teacher_tab.ml index 9d3b1ad2e..b9bc2e5d5 100644 --- a/src/app/learnocaml_teacher_tab.ml +++ b/src/app/learnocaml_teacher_tab.ml @@ -1032,73 +1032,23 @@ let rec teacher_tab token _select _params () = current_assignment) in let set_assignment ?assg ?students ?exos ?default id = + (* - get the currently saved assignment *) let (assg0, students0, exos0, default0) = Hashtbl.find assignments_tbl id in + (* - update the assignment fields if related arg <> None *) let dft x0 = function Some x -> x | None -> x0 in let start, stop = dft assg0 assg in let students = dft students0 students in let exos = dft exos0 exos in let default = dft default0 default in + (* - update the assignment *) Hashtbl.replace assignments_tbl id ((start, stop), students, exos, default); (match Manip.by_id (assg_line_id id) with | Some l -> Manip.replaceSelf l (assignment_line id) | None -> failwith "Assignment line not found"); - let status = ES.(Assigned {start; stop}) in + (* - update (!status_changes : ES.t SMap.t) (initially empty) *) let exercise_status_changes = - SSet.fold (fun ex_id acc -> - let st = get_status ex_id in - let assg = st.ES.assignments in - let old_default = assg.ES.default in - let new_default = - if default then status - else if default0 then ES.Closed - else old_default - in - let add tk st tmap = - if st = new_default then tmap - else Token.Map.add tk st tmap - in - let token_map = - Token.Map.fold (fun tk _ acc -> - if Token.Set.mem tk students then - if default then acc - else Token.Map.add tk status acc - else if Token.Set.mem tk students0 then - if default then Token.Map.add tk ES.Closed acc - else Token.Map.remove tk acc - else add tk (ES.get_status tk assg) acc) - !students_map Token.Map.empty - in - SMap.add ex_id - ES.{st with assignments = { - token_map; - default = new_default; - }} - acc) - exos - !status_changes - in - let exercise_status_changes = - SSet.fold (fun ex_id acc -> - let st = get_status ex_id in - let assg = st.ES.assignments in - let dft_status = - if default0 then ES.Closed else ES.default_assignment assg - in - let token_map = - Token.Set.fold Token.Map.remove students0 assg.ES.token_map - in - let token_map = - Token.Map.filter (fun _ a -> a <> dft_status) token_map - in - SMap.add ex_id - ES.{st with assignments = { - token_map; - default = dft_status - }} - acc) - (SSet.diff exos0 exos) - exercise_status_changes + ES.update_exercise_assignments get_status (students0, exos0, default0) (students, exos, default) (start, stop) !students_map !status_changes in status_changes := exercise_status_changes; fill_exercises_pane (); diff --git a/src/state/learnocaml_data.ml b/src/state/learnocaml_data.ml index 9b94d7fda..0b73156a7 100644 --- a/src/state/learnocaml_data.ml +++ b/src/state/learnocaml_data.ml @@ -626,7 +626,75 @@ module Exercise = struct | GloballyOpen -> a | GloballyInconsistent -> fix_open_close ~close:false a - (* Note/Erik: we may also want to implement set_assigned_globally *) + let update_exercise_assignments (get_status_t : string -> t) (students0, exos0, default0) (students, exos, default) (start, stop) students_map status_map = + let status = Assigned {start; stop} in + (* - walk accross the new list of exos *) + let status_map = + SSet.fold (fun ex_id acc -> + let st = get_status_t ex_id in + let assg = st.assignments in + let global_status = is_open_or_assigned_globally assg in + let old_default = assg.default in + let new_default = + if default then status + else if default0 (* the future_students flag was removed *) + then match global_status with + | GloballyOpenOrAssigned | GloballyOpen -> Open + | _ -> Closed + else old_default + in + let add tk st tmap = + if st = new_default then tmap (* normalize the token map *) + else Token.Map.add tk st tmap + in + let token_map = + Token.Map.fold (fun tk _ acc -> + if Token.Set.mem tk students then + if default then acc + else Token.Map.add tk status acc + else if Token.Set.mem tk students0 then (* the student was unassigned *) + if default + then match global_status with + | GloballyOpenOrAssigned | GloballyOpen -> Token.Map.add tk Open acc + | _ -> Token.Map.add tk Closed acc + else (* Token.Map.remove tk (unneeded call) *) acc + else add tk (get_status tk assg) acc) + students_map Token.Map.empty + in + SMap.add ex_id + {st with assignments = { + token_map; + default = new_default; + }} + acc) + exos + status_map + in + (* - walk accross the list of removed exos *) + SSet.fold (fun ex_id acc -> + let st = get_status_t ex_id in + let assg = st.assignments in + let dft_status = + if default0 (* the old default was Assigned(_, _) *) + then match is_open_or_assigned_globally assg with + | GloballyOpenOrAssigned | GloballyOpen -> Open + | _ -> Closed + else default_assignment assg + in + let token_map = + Token.Set.fold Token.Map.remove students0 assg.token_map + in + let token_map = + Token.Map.filter (fun _ a -> a <> dft_status) token_map + in + SMap.add ex_id + {st with assignments = { + token_map; + default = dft_status + }} + acc) + (SSet.diff exos0 exos) + status_map let is_open_assignment token a = match get_status token a with diff --git a/src/state/learnocaml_data.mli b/src/state/learnocaml_data.mli index b80bb7ec5..2af6ed0a3 100644 --- a/src/state/learnocaml_data.mli +++ b/src/state/learnocaml_data.mli @@ -251,6 +251,18 @@ module Exercise: sig (** Call [check_open_close] then (if need be) [fix_open_close] *) val check_and_fix_open_close: assignments -> assignments + (** Update [status_map: Exercise.Status.t SMap.t] if an assignment changes: + [update_exercise_assignments g (s0,e0,d0) (s,e,d) (t0,t1) m status_map] + turns [(stu0,exo0,dft0)] to [(s,e,d) + Assigned{t0;t1}] in [status_map] + from [g = get_status_t: string -> t] (status of exo) and [m = stud_map] *) + val update_exercise_assignments: (string -> t) -> + Token.Set.t * SSet.t * bool -> + Token.Set.t * SSet.t * bool -> + float * float -> + Student.t Token.Map.t -> + t SMap.t -> + t SMap.t + val is_open_assignment: Token.t -> assignments -> [> `Open | `Closed | `Deadline of float] diff --git a/src/state/learnocaml_data_test.ml b/src/state/learnocaml_data_test.ml index ee4cf5d45..8bae7c0be 100644 --- a/src/state/learnocaml_data_test.ml +++ b/src/state/learnocaml_data_test.ml @@ -10,11 +10,12 @@ See *) open Learnocaml_data -open Exercise.Status -let%test_module _ = +let%test_module "Exercise.Status" = (module struct + open Exercise.Status + let testdata_str_of_globalstatus = function | GloballyOpen -> "GloballyOpen" | GloballyClosed -> "GloballyClosed" @@ -22,44 +23,71 @@ let%test_module _ = | GloballyClosedOrAssigned -> "GloballyClosedOrAssigned" | GloballyInconsistent -> "GloballyInconsistent" - let testdata_map_of list = + + let testdata_smap_of list = + List.fold_right (fun (e, st) res -> SMap.add e st res) list SMap.empty + + let testdata_list_of_smap map = + let list = + SMap.fold (fun e st res -> (e, st) :: res) map [] + in + List.sort (fun (e1, _) (e2, _) -> compare e1 e2) list + + let testdata_tokmap_of list = List.fold_right (fun (tok, st) res -> Token.Map.add tok st res) list Token.Map.empty + let testdata_list_of_tokmap map = + let list = + Token.Map.fold (fun tok st res -> (tok, st) :: res) map [] + in + List.sort (fun (tok1, _) (tok2, _) -> compare tok1 tok2) list + + let testdata_tokset_of list = + List.fold_right (fun (tok) res -> Token.Set.add tok res) list Token.Set.empty + + let testdata_sset_of list = + List.fold_right (fun (tok) res -> SSet.add tok res) list SSet.empty + let testdata_token : Token.t * Token.t = - (Token.parse "KOK-W3L-NE1-SVY", Token.parse "JBG-B9B-3S1-NZO") + (Token.parse "JBG-B9B-3S1-NZO", Token.parse "KOK-W3L-NE1-SVY") + + let testdata_assigned_7d = Assigned {start=1691877600.; stop=1692482400.} - let testdata_assigned = Assigned {start=1691877600.; stop=1692482400.} + let testdata_assigned_8d = Assigned {start=1691877600.; stop=1692568800.} + + let testdata_7d = (1691877600., 1692482400.) + + let testdata_8d = (1691877600., 1692568800.) let testdata_wrong_assignments = List.map (fun (mp, dflt) -> make_assignments mp dflt) - [(testdata_map_of [fst testdata_token, Open], Closed); - (testdata_map_of [fst testdata_token, Closed], Open); - (testdata_map_of [fst testdata_token, Open; - snd testdata_token, Closed], testdata_assigned)] + [(testdata_tokmap_of [fst testdata_token, Open], Closed); + (testdata_tokmap_of [fst testdata_token, Closed], Open); + (testdata_tokmap_of [fst testdata_token, Open; + snd testdata_token, Closed], testdata_assigned_7d)] let testdata_fixed_assignments = List.map (fun (mp, dflt) -> make_assignments mp dflt) (* Replace Open with Closed in testdata_wrong_assignments *) - [(testdata_map_of [fst testdata_token, Closed], Closed); - (testdata_map_of [fst testdata_token, Closed], Closed); - (testdata_map_of [fst testdata_token, Closed; - snd testdata_token, Closed], testdata_assigned)] + [(testdata_tokmap_of [fst testdata_token, Closed], Closed); + (testdata_tokmap_of [fst testdata_token, Closed], Closed); + (testdata_tokmap_of [fst testdata_token, Closed; + snd testdata_token, Closed], testdata_assigned_7d)] let testdata_good_assignments = List.map (fun (mp, dflt) -> make_assignments mp dflt) - [(*1*) (testdata_map_of [], Open); - (*2*) (testdata_map_of [], Closed); - (*3*) (testdata_map_of [], testdata_assigned); - (*4*) (testdata_map_of [fst testdata_token, Open], testdata_assigned); - (*5*) (testdata_map_of [fst testdata_token, Closed], testdata_assigned); - (*6*) (testdata_map_of [fst testdata_token, testdata_assigned], Open); - (*7*) (testdata_map_of [fst testdata_token, testdata_assigned], Closed); - (*8*) (testdata_map_of [fst testdata_token, Open], Open); - (*9*) (testdata_map_of [fst testdata_token, Closed], Closed); - (*10*)(testdata_map_of [fst testdata_token, - testdata_assigned], testdata_assigned)] - - let%expect_test "is_open_or_assigned_globally" = + [(*1*) (testdata_tokmap_of [], Open); + (*2*) (testdata_tokmap_of [], Closed); + (*3*) (testdata_tokmap_of [], testdata_assigned_7d); + (*4*) (testdata_tokmap_of [fst testdata_token, Open], testdata_assigned_7d); + (*5*) (testdata_tokmap_of [fst testdata_token, Closed], testdata_assigned_7d); + (*6*) (testdata_tokmap_of [fst testdata_token, testdata_assigned_7d], Open); + (*7*) (testdata_tokmap_of [fst testdata_token, testdata_assigned_7d], Closed); + (*8*) (testdata_tokmap_of [fst testdata_token, Open], Open); + (*9*) (testdata_tokmap_of [fst testdata_token, Closed], Closed); + (*10*)(testdata_tokmap_of [fst testdata_token, testdata_assigned_7d], testdata_assigned_7d)] + + let%expect_test "is_open_or_assigned_globally/full" = List.iter (fun glob -> Printf.printf "%s\n" @@ testdata_str_of_globalstatus @@ is_open_or_assigned_globally glob) @@ -101,4 +129,414 @@ let%test_module _ = List.for_all (fun a -> check_and_fix_open_close a = a) testdata_good_assignments + let testdata_make_t id a dflt = + {id; + skills_prereq = []; (* independent of the SUT *) + skills_focus = []; (* independent of the SUT *) + assignments = make_assignments a dflt} + + (* - initial status_map *) + let testdata_s0 : t SMap.t = + testdata_smap_of + [(* testdata_t0_a7d *) + ("exo1", testdata_make_t "exo1" + (testdata_tokmap_of [snd testdata_token, Open]) testdata_assigned_7d); + ("exo2", testdata_make_t "exo2" + (testdata_tokmap_of [snd testdata_token, Closed]) testdata_assigned_7d); + ("exo3", testdata_make_t "exo3" + (testdata_tokmap_of []) testdata_assigned_7d); + (* testdata_t0_a8d *) + ("exo4", testdata_make_t "exo4" + (testdata_tokmap_of [fst testdata_token, testdata_assigned_8d]) Open); + ("exo5", testdata_make_t "exo5" + (testdata_tokmap_of [fst testdata_token, testdata_assigned_8d]) Closed); + (* testdata_t0_noa *) + ("exo6", testdata_make_t "exo6" + (testdata_tokmap_of []) Open); + ("exo7", testdata_make_t "exo7" + (testdata_tokmap_of []) Closed); + ] + + (* let testdata_s00 : t SMap.t = SMap.empty *) + + type triple = Token.Set.t * SSet.t * bool + + let testdata_t0_a7d : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo1"; "exo2"; "exo3"], + true) + + let testdata_t0_a8d : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo4"; "exo5"], + false) + + (* let testdata_t0_noa : triple = + (testdata_tokset_of [], + testdata_sset_of [], + false) *) + + let%expect_test "is_open_or_assigned_globally/testdata_s0" = + List.iter (fun glob -> Printf.printf "%s\n" + @@ testdata_str_of_globalstatus + @@ is_open_or_assigned_globally glob) + (List.map (fun (_e, s) -> s.assignments) @@ testdata_list_of_smap testdata_s0); + [%expect {| + GloballyOpenOrAssigned + GloballyClosedOrAssigned + GloballyClosedOrAssigned + GloballyOpenOrAssigned + GloballyClosedOrAssigned + GloballyOpen + GloballyClosed |}] + + let testdata_str_of_status = function + | Open -> "Open" + | Closed -> "Closed" + | Assigned {start; stop} -> Printf.sprintf "Assigned(%s,%s)" + (string_of_float start) + (string_of_float stop) + + let testdata_str_of_toklist list = + List.fold_right (fun (tok, st) r -> + Printf.sprintf "(%s, %s); %s" + (Token.to_string tok) + (testdata_str_of_status st) + r) list "" + + let testdata_str_of_assignments (s : assignments) = + Printf.sprintf "{default = %s; token_map = { %s}}" + (testdata_str_of_status s.default) + (testdata_str_of_toklist @@ + testdata_list_of_tokmap s.token_map) + + let testdata_str_of_t (s : t) = + Printf.sprintf "{id = %s; assignments = %s}" + s.id + (testdata_str_of_assignments s.assignments) + + let testdata_str_of_smap ?(globalstatus=true) (s : t SMap.t) = + let list = + SMap.fold (fun eid tt acc -> + if eid <> tt.id + then failwith (Printf.sprintf "eid(%s) <> tt.id(%s)" eid tt.id) + else (eid, testdata_str_of_t tt ^ + (if globalstatus + then " -> " ^ + testdata_str_of_globalstatus + @@ is_open_or_assigned_globally + @@ tt.assignments + else "")) :: acc) s [] + in + let list = + List.sort (fun (eid1, _) (eid2, _) -> compare eid1 eid2) list + in + List.fold_right (fun (_, st) r -> st ^ "\n" ^ r) list "" + + let testdata_get_status_t status_map id = + try SMap.find id status_map with Not_found -> + default id + + let testdata_stud_map = + let stud_of_tok tk = (tk, Student.default tk) in + testdata_tokmap_of [stud_of_tok @@ fst testdata_token; + stud_of_tok @@ snd testdata_token] + + (* Test cases candidates : + Usage of function set_assignment in learnocaml_teacher_tab.ml + - set_assignment aid ~exos (*selected exos*) + - set_assignment aid ~students ~default (*selected students*) + - set_assignment aid ~assg:(start,stop) (*change assignment*) + - set_assignment aid ~students:() ~exos:() (*assignment_remove*) + *) + + let testdata_t1_a7d_rm_exo1 : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo2"; "exo3"],(*<-*) + true) + let testdata_t1_a7d_rm_exo2 : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo1"; "exo3"],(*<-*) + true) + let testdata_t1_a7d_rm_exo3 : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo1"; "exo2"],(*<-*) + true) + let testdata_t1_a7d_add_exo6_7 : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo1"; "exo2"; "exo3"; "exo6"; "exo7"],(*<-*) + true) + + let testdata_t1_a8d_rm_exo4 : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo5"],(*<-*) + false) + let testdata_t1_a8d_rm_exo5 : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo4"],(*<-*) + false) + let testdata_t1_a8d_add_exo6_7 : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo4"; "exo5"; "exo6"; "exo7"],(*<-*) + false) + + let%expect_test "update_exercise_assignments/set_exos" = + Printf.printf "# initial status_map:\n%s\n" @@ + testdata_str_of_smap testdata_s0; + List.iter (fun (label, tri0, tri, dates) -> + Printf.printf "# %s:\n%s\n" + label (testdata_str_of_smap @@ + update_exercise_assignments (testdata_get_status_t testdata_s0) + tri0 tri dates testdata_stud_map testdata_s0)) + ["a7d_rm_exo1", testdata_t0_a7d, testdata_t1_a7d_rm_exo1, testdata_7d; + "a7d_rm_exo2", testdata_t0_a7d, testdata_t1_a7d_rm_exo2, testdata_7d; + "a7d_rm_exo3", testdata_t0_a7d, testdata_t1_a7d_rm_exo3, testdata_7d; + "a7d_add_exo6_7_true", testdata_t0_a7d, testdata_t1_a7d_add_exo6_7, testdata_7d; + "a8d_rm_exo4", testdata_t0_a8d, testdata_t1_a8d_rm_exo4, testdata_8d; + "a8d_rm_exo5", testdata_t0_a8d, testdata_t1_a8d_rm_exo5, testdata_8d; + "a8d_add_exo6_7_false", testdata_t0_a8d, testdata_t1_a8d_add_exo6_7, testdata_8d; + ]; + [%expect {| + # initial status_map: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a7d_rm_exo1: + {id = exo1; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a7d_rm_exo2: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a7d_rm_exo3: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a7d_add_exo6_7_true: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo7; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + + # a8d_rm_exo4: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a8d_rm_exo5: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a8d_add_exo6_7_false: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo7; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned |}] + + let testdata_t2_a7d_add_stu2 : triple = + (testdata_tokset_of [fst testdata_token; snd testdata_token],(*<-*) + testdata_sset_of ["exo1"; "exo2"; "exo3"], + true) + + let testdata_t2_a7d_rm_stu1 : triple = + (testdata_tokset_of [],(*<-*) + testdata_sset_of ["exo1"; "exo2"; "exo3"], + true) + + let testdata_t2_a7d_rm_dflt : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo1"; "exo2"; "exo3"], + false(*<-*)) + + let testdata_t2_a8d_add_stu2 : triple = + (testdata_tokset_of [fst testdata_token; snd testdata_token],(*<-*) + testdata_sset_of ["exo4"; "exo5"], + false) + + let testdata_t2_a8d_add_dflt : triple = + (testdata_tokset_of [fst testdata_token], + testdata_sset_of ["exo4"; "exo5"], + true(*<-*)) + + let%expect_test "update_exercise_assignments/set_students" = + Printf.printf "# initial status_map:\n%s\n" @@ + testdata_str_of_smap testdata_s0; + List.iter (fun (label, tri0, tri, dates) -> + Printf.printf "# %s:\n%s\n" + label (testdata_str_of_smap @@ + update_exercise_assignments (testdata_get_status_t testdata_s0) + tri0 tri dates testdata_stud_map testdata_s0)) + ["a7d_add_stu2", testdata_t0_a7d, testdata_t2_a7d_add_stu2, testdata_7d; + "a7d_rm_stu1", testdata_t0_a7d, testdata_t2_a7d_rm_stu1, testdata_7d; + "a7d_rm_dflt", testdata_t0_a7d, testdata_t2_a7d_rm_dflt, testdata_7d; + "a8d_add_stu2", testdata_t0_a8d, testdata_t2_a8d_add_stu2, testdata_8d; + "a8d_add_dflt", testdata_t0_a8d, testdata_t2_a8d_add_dflt, testdata_8d; + ]; + [%expect {| + # initial status_map: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a7d_add_stu2: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a7d_rm_stu1: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (JBG-B9B-3S1-NZO, Open); (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (JBG-B9B-3S1-NZO, Closed); (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (JBG-B9B-3S1-NZO, Closed); }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a7d_rm_dflt: + {id = exo1; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692482400.)); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692482400.)); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692482400.)); (KOK-W3L-NE1-SVY, Assigned(1691877600.,1692482400.)); }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a8d_add_stu2: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); (KOK-W3L-NE1-SVY, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); (KOK-W3L-NE1-SVY, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a8d_add_dflt: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Assigned(1691877600.,1692568800.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Assigned(1691877600.,1692568800.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed |}] + + let testdata_t3_a7d_rm_assignment : triple = + (testdata_tokset_of [],(*<-*) + testdata_sset_of [],(*<-*) + true) + + let testdata_t3_a8d_rm_assignment : triple = + (testdata_tokset_of [],(*<-*) + testdata_sset_of [],(*<-*) + false) + + let%expect_test "update_exercise_assignments/rm_assignment" = + Printf.printf "# initial status_map:\n%s\n" @@ + testdata_str_of_smap testdata_s0; + List.iter (fun (label, tri0, tri, dates) -> + Printf.printf "# %s:\n%s\n" + label (testdata_str_of_smap @@ + update_exercise_assignments (testdata_get_status_t testdata_s0) + tri0 tri dates testdata_stud_map testdata_s0)) + ["a7d_rm_assignment", testdata_t0_a7d, testdata_t3_a7d_rm_assignment, testdata_7d; + "a8d_rm_assignment", testdata_t0_a8d, testdata_t3_a8d_rm_assignment, testdata_8d]; + [%expect {| + # initial status_map: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a7d_rm_assignment: + {id = exo1; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo2; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + {id = exo3; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a8d_rm_assignment: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo5; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed |}] + + let%expect_test "update_exercise_assignments/set_assignment" = + Printf.printf "# initial status_map:\n%s\n" @@ + testdata_str_of_smap testdata_s0; + List.iter (fun (label, tri0, tri, dates) -> + Printf.printf "# %s:\n%s\n" + label (testdata_str_of_smap @@ (* let _ = *) + update_exercise_assignments (testdata_get_status_t testdata_s0) + tri0 tri dates testdata_stud_map testdata_s0 (* in testdata_s0 *))) + ["a7d_to_a8d", testdata_t0_a7d, testdata_t0_a7d, testdata_8d]; + [%expect {| + # initial status_map: + {id = exo1; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692482400.); token_map = { }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed + + # a7d_to_a8d: + {id = exo1; assignments = {default = Assigned(1691877600.,1692568800.); token_map = { (KOK-W3L-NE1-SVY, Open); }}} -> GloballyOpenOrAssigned + {id = exo2; assignments = {default = Assigned(1691877600.,1692568800.); token_map = { (KOK-W3L-NE1-SVY, Closed); }}} -> GloballyClosedOrAssigned + {id = exo3; assignments = {default = Assigned(1691877600.,1692568800.); token_map = { (KOK-W3L-NE1-SVY, Assigned(1691877600.,1692482400.)); }}} -> GloballyClosedOrAssigned + {id = exo4; assignments = {default = Open; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyOpenOrAssigned + {id = exo5; assignments = {default = Closed; token_map = { (JBG-B9B-3S1-NZO, Assigned(1691877600.,1692568800.)); }}} -> GloballyClosedOrAssigned + {id = exo6; assignments = {default = Open; token_map = { }}} -> GloballyOpen + {id = exo7; assignments = {default = Closed; token_map = { }}} -> GloballyClosed |}] + end)