@@ -994,6 +994,187 @@ def test_bof_session_tag(self):
994994 self .assertIn ('BoF' , bof_tags .eq (0 ).text (),
995995 'BoF tag should contain text "BoF"' )
996996
997+ def _setup_for_swap_timeslots (self ):
998+ """Create a meeting, rooms, and schedule for swap_timeslots testing
999+
1000+ Creates two groups of rooms with disjoint timeslot sets, modeling the room grouping in
1001+ the edit_meeting_schedule view.
1002+ """
1003+ # Meeting must be in the future so it can be edited
1004+ meeting = MeetingFactory (
1005+ type_id = 'ietf' ,
1006+ date = datetime .date .today () + datetime .timedelta (days = 7 ),
1007+ populate_schedule = False ,
1008+ )
1009+ meeting .schedule = ScheduleFactory (meeting = meeting )
1010+ meeting .save ()
1011+
1012+ # Create room groups
1013+ room_groups = [
1014+ RoomFactory .create_batch (2 , meeting = meeting ),
1015+ RoomFactory .create_batch (2 , meeting = meeting ),
1016+ ]
1017+
1018+ # Set up different sets of timeslots
1019+ t0 = datetime .datetime .combine (meeting .date , datetime .time (11 , 0 ))
1020+ dur = datetime .timedelta (hours = 2 )
1021+ for room in room_groups [0 ]:
1022+ TimeSlotFactory (meeting = meeting , location = room , duration = dur , time = t0 )
1023+ TimeSlotFactory (meeting = meeting , location = room , duration = dur , time = t0 + datetime .timedelta (days = 1 , hours = 2 ))
1024+ TimeSlotFactory (meeting = meeting , location = room , duration = dur , time = t0 + datetime .timedelta (days = 2 , hours = 4 ))
1025+
1026+ for room in room_groups [1 ]:
1027+ TimeSlotFactory (meeting = meeting , location = room , duration = dur , time = t0 + datetime .timedelta (hours = 1 ))
1028+ TimeSlotFactory (meeting = meeting , location = room , duration = dur , time = t0 + datetime .timedelta (days = 1 , hours = 3 ))
1029+ TimeSlotFactory (meeting = meeting , location = room , duration = dur , time = t0 + datetime .timedelta (days = 2 , hours = 5 ))
1030+
1031+ # And now put sessions in the timeslots
1032+ for ts in meeting .timeslot_set .all ():
1033+ SessionFactory (
1034+ meeting = meeting ,
1035+ name = str (ts .pk ), # label to identify where it started
1036+ add_to_schedule = False ,
1037+ ).timeslotassignments .create (
1038+ timeslot = ts ,
1039+ schedule = meeting .schedule ,
1040+ )
1041+ return meeting , room_groups
1042+
1043+ def test_swap_timeslots (self ):
1044+ """Schedule timeslot groups should swap properly
1045+
1046+ This tests the case currently exercised by the UI - where the rooms are grouped according to
1047+ entirely equivalent sets of timeslots. Thus, there is always a matching timeslot for every (or no)
1048+ room as long as the rooms parameter to the ajax call includes only one group.
1049+ """
1050+ meeting , room_groups = self ._setup_for_swap_timeslots ()
1051+
1052+ url = urlreverse ('ietf.meeting.views.edit_meeting_schedule' , kwargs = dict (num = meeting .number ))
1053+ username = meeting .schedule .owner .user .username
1054+ self .client .login (username = username , password = username + '+password' )
1055+
1056+ # Swap group 0's first and last sessions
1057+ r = self .client .post (
1058+ url ,
1059+ dict (
1060+ action = 'swaptimeslots' ,
1061+ origin_timeslot = str (room_groups [0 ][0 ].timeslot_set .first ().pk ),
1062+ target_timeslot = str (room_groups [0 ][0 ].timeslot_set .last ().pk ),
1063+ rooms = ',' .join ([str (room .pk ) for room in room_groups [0 ]]),
1064+ )
1065+ )
1066+ self .assertEqual (r .status_code , 302 )
1067+
1068+ # Validate results
1069+ for index , room in enumerate (room_groups [0 ]):
1070+ timeslots = list (room .timeslot_set .all ())
1071+ self .assertEqual (timeslots [0 ].session .name , str (timeslots [- 1 ].pk ),
1072+ 'Session from last timeslot in room (0, {}) should now be in first' .format (index ))
1073+ self .assertEqual (timeslots [- 1 ].session .name , str (timeslots [0 ].pk ),
1074+ 'Session from first timeslot in room (0, {}) should now be in last' .format (index ))
1075+ self .assertEqual (
1076+ [ts .session .name for ts in timeslots [1 :- 1 ]],
1077+ [str (ts .pk ) for ts in timeslots [1 :- 1 ]],
1078+ 'Sessions in middle timeslots should be unchanged'
1079+ )
1080+ for index , room in enumerate (room_groups [1 ]):
1081+ timeslots = list (room .timeslot_set .all ())
1082+ self .assertFalse (
1083+ any (ts .session is None for ts in timeslots ),
1084+ "Sessions in other room group's timeslots should still be assigned"
1085+ )
1086+ self .assertEqual (
1087+ [ts .session .name for ts in timeslots ],
1088+ [str (ts .pk ) for ts in timeslots ],
1089+ "Sessions in other room group's timeslots should be unchanged"
1090+ )
1091+
1092+ def test_swap_timeslots_handles_unmatched (self ):
1093+ """Sessions in unmatched timeslots should be unassigned when swapped
1094+
1095+ This more generally tests the back end by exercising the situation where a timeslot in the
1096+ affected rooms does not have an equivalent timeslot target. This is not used by the UI as of
1097+ now (2021-06-22), but should function correctly.
1098+ """
1099+ meeting , room_groups = self ._setup_for_swap_timeslots ()
1100+
1101+ # Remove a timeslot and session from only one room in group 0
1102+ ts_to_remove = room_groups [0 ][1 ].timeslot_set .last ()
1103+ ts_to_remove .session .delete ()
1104+ ts_to_remove .delete () # our object still exists but has no db object
1105+
1106+ # Add a matching timeslot to group 1 so we can be sure it's being ignored.
1107+ # If not, this session will be unassigned when we swap timeslots on group 0.
1108+ new_ts = TimeSlotFactory (
1109+ meeting = meeting ,
1110+ location = room_groups [1 ][0 ],
1111+ duration = ts_to_remove .duration ,
1112+ time = ts_to_remove .time ,
1113+ )
1114+ SessionFactory (
1115+ meeting = meeting ,
1116+ name = str (new_ts .pk ),
1117+ add_to_schedule = False ,
1118+ ).timeslotassignments .create (
1119+ timeslot = new_ts ,
1120+ schedule = meeting .schedule ,
1121+ )
1122+
1123+ url = urlreverse ('ietf.meeting.views.edit_meeting_schedule' , kwargs = dict (num = meeting .number ))
1124+ username = meeting .schedule .owner .user .username
1125+ self .client .login (username = username , password = username + '+password' )
1126+
1127+ # Now swap between first and last timeslots in group 0
1128+ r = self .client .post (
1129+ url ,
1130+ dict (
1131+ action = 'swaptimeslots' ,
1132+ origin_timeslot = str (room_groups [0 ][0 ].timeslot_set .first ().pk ),
1133+ target_timeslot = str (room_groups [0 ][0 ].timeslot_set .last ().pk ),
1134+ rooms = ',' .join ([str (room .pk ) for room in room_groups [0 ]]),
1135+ )
1136+ )
1137+ self .assertEqual (r .status_code , 302 )
1138+
1139+ # Validate results
1140+ for index , room in enumerate (room_groups [0 ]):
1141+ timeslots = list (room .timeslot_set .all ())
1142+ if index == 1 :
1143+ # special case - this has no matching timeslot because we deleted it above
1144+ self .assertIsNone (timeslots [0 ].session , 'Unmatched timeslot should be empty after swap' )
1145+ session_that_should_be_unassigned = Session .objects .get (name = str (timeslots [0 ].pk ))
1146+ self .assertEqual (session_that_should_be_unassigned .timeslotassignments .count (), 0 ,
1147+ 'Session that was in an unmatched timeslot should now be unassigned' )
1148+ # check from 2nd timeslot to the last since we deleted the original last timeslot
1149+ self .assertEqual (
1150+ [ts .session .name for ts in timeslots [1 :]],
1151+ [str (ts .pk ) for ts in timeslots [1 :]],
1152+ 'Sessions in middle timeslots should be unchanged'
1153+ )
1154+ else :
1155+ self .assertEqual (timeslots [0 ].session .name , str (timeslots [- 1 ].pk ),
1156+ 'Session from last timeslot in room (0, {}) should now be in first' .format (index ))
1157+ self .assertEqual (timeslots [- 1 ].session .name , str (timeslots [0 ].pk ),
1158+ 'Session from first timeslot in room (0, {}) should now be in last' .format (index ))
1159+ self .assertEqual (
1160+ [ts .session .name for ts in timeslots [1 :- 1 ]],
1161+ [str (ts .pk ) for ts in timeslots [1 :- 1 ]],
1162+ 'Sessions in middle timeslots should be unchanged'
1163+ )
1164+
1165+ # Still should have no effect on other rooms, even if they matched a timeslot
1166+ for index , room in enumerate (room_groups [1 ]):
1167+ timeslots = list (room .timeslot_set .all ())
1168+ self .assertFalse (
1169+ any (ts .session is None for ts in timeslots ),
1170+ "Sessions in other room group's timeslots should still be assigned"
1171+ )
1172+ self .assertEqual (
1173+ [ts .session .name for ts in timeslots ],
1174+ [str (ts .pk ) for ts in timeslots ],
1175+ "Sessions in other room group's timeslots should be unchanged"
1176+ )
1177+
9971178
9981179class ReorderSlidesTests (TestCase ):
9991180
0 commit comments