55from urlparse import urljoin
66import copy
77import os
8+ import sys
89import re
910import string
1011
@@ -168,6 +169,17 @@ def sessions_that_can_meet(self):
168169 qs = qs .filter (type__slug = 'session' )
169170 return qs
170171
172+ def sessions_that_can_be_placed (self ):
173+ log .unreachable ()
174+ from django .db .models import Q
175+ donotplace_groups = Q (group__acronym = "edu" )
176+ donotplace_groups |= Q (group__acronym = "tools" )
177+ donotplace_groups |= Q (group__acronym = "iesg" )
178+ donotplace_groups |= Q (group__acronym = "ietf" )
179+ donotplace_groups |= Q (group__acronym = "iepg" )
180+ donotplace_groups |= Q (group__acronym = "iab" )
181+ return self .sessions_that_can_meet .exclude (donotplace_groups )
182+
171183 def json_url (self ):
172184 return "/meeting/%s.json" % (self .number , )
173185
@@ -639,6 +651,14 @@ def public_token(self):
639651 def is_official (self ):
640652 return (self .meeting .agenda == self )
641653
654+ @property
655+ def official_class (self ):
656+ log .unreachable ()
657+ if self .is_official :
658+ return "agenda_official"
659+ else :
660+ return "agenda_unofficial"
661+
642662 # returns a dictionary {group -> [schedtimesessassignment+]}
643663 # and it has [] if the session is not placed.
644664 # if there is more than one session for that group,
@@ -682,6 +702,26 @@ def group_mapping(self):
682702 assignments ,sessions ,total ,scheduled = self .group_session_mapping
683703 return assignments
684704
705+ @property
706+ def group_session_mapping (self ):
707+ log .unreachable ()
708+ assignments = dict ()
709+ sessions = dict ()
710+ total = 0
711+ scheduled = 0
712+ allschedsessions = self .qs_assignments_with_sessions .filter (timeslot__type = "session" ).all ()
713+ for sess in self .meeting .sessions_that_can_meet .all ():
714+ assignments [sess .group ] = []
715+ sessions [sess ] = None
716+ total += 1
717+
718+ for ss in allschedsessions :
719+ assignments [ss .session .group ].append (ss )
720+ # XXX can not deal with a session in two slots
721+ sessions [ss .session ] = ss
722+ scheduled += 1
723+ return assignments ,sessions ,total ,scheduled
724+
685725 @property
686726 def sessions_that_can_meet (self ):
687727 if not hasattr (self , "_cached_sessions_that_can_meet" ):
@@ -695,6 +735,15 @@ def calc_badness(self):
695735 assignments = self .group_mapping
696736 return self .calc_badness1 (assignments )
697737
738+ # calculate badness of entire schedule
739+ def calc_badness1 (self , assignments ):
740+ log .unreachable ()
741+ badness = 0
742+ for sess in self .sessions_that_can_meet :
743+ badness += sess .badness (assignments )
744+ self .badness = badness
745+ return badness
746+
698747 def delete_schedule (self ):
699748 self .assignments .all ().delete ()
700749 self .delete ()
@@ -751,6 +800,19 @@ def area(self):
751800 return ""
752801 return self .session .group .parent .acronym
753802
803+ @property
804+ def group_type_str (self ):
805+ log .unreachable ()
806+ if not self .session or not self .session .group :
807+ return ""
808+ if self .session .group and self .session .group .type_id == "wg" :
809+ if self .session .group .state_id == "bof" :
810+ return "BOF"
811+ else :
812+ return "WG"
813+
814+ return ""
815+
754816 @property
755817 def slottype (self ):
756818 log .unreachable ()
@@ -853,13 +915,38 @@ def brief_display(self):
853915 elif not self .target and self .person :
854916 return u"%s " % (self .person )
855917
918+
919+
920+ @property
921+ def person_conflicted (self ):
922+ log .unreachable ()
923+ if self .person is None :
924+ return "unknown person"
925+ return self .person .name
926+
856927 def status (self ):
857928 log .unreachable ()
858929 if self .active_status is not None :
859930 return self .active_status
860931 else :
861932 return True
862933
934+ def __lt__ (self , y ):
935+ log .unreachable ()
936+ #import sys
937+ #sys.stdout.write("me: %s y: %s\n" % (self.name.slug, y.name.slug))
938+ if self .name .slug == 'conflict' and y .name .slug == 'conflic2' :
939+ return True
940+ if self .name .slug == 'conflict' and y .name .slug == 'conflic3' :
941+ return True
942+ if self .name .slug == 'conflic2' and y .name .slug == 'conflic3' :
943+ return True
944+ return False
945+
946+ def constraint_cost (self ):
947+ log .unreachable ()
948+ return self .name .penalty ;
949+
863950 def json_url (self ):
864951 return "/meeting/%s/constraint/%s.json" % (self .meeting .number , self .id )
865952
@@ -1179,3 +1266,201 @@ def agenda_file(self):
11791266 self ._agenda_file = "%s/agenda/%s" % (self .meeting .number , filename )
11801267
11811268 return self ._agenda_file
1269+ def badness_test (self , num ):
1270+ log .unreachable ()
1271+ from settings import BADNESS_CALC_LOG # pylint: disable=import-error
1272+ #sys.stdout.write("num: %u / BAD: %u\n" % (num, BADNESS_CALC_LOG))
1273+ return BADNESS_CALC_LOG >= num
1274+
1275+ def badness_log (self , num , msg ):
1276+ log .unreachable ()
1277+ if self .badness_test (num ):
1278+ sys .stdout .write (msg )
1279+
1280+ # this evaluates the current session based upon the constraints
1281+ # given, in the context of the assignments in the array.
1282+ #
1283+ # MATH.
1284+ # each failed conflic3 is worth 1000 points
1285+ # each failed conflic2 is worth 10000 points
1286+ # each failed conflic1 is worth 100000 points
1287+ # being in a room too small than asked is worth 200,000 * (size/50)
1288+ # being in a room too big by more than 100 is worth 200,000 once.
1289+ # a conflict where AD must be in two places is worth 500,000.
1290+ # not being scheduled is worth 10,000,000 points
1291+ #
1292+ def badness (self , assignments ):
1293+ log .unreachable ()
1294+ badness = 0
1295+
1296+ if not (self .group in assignments ):
1297+ return 0
1298+
1299+ conflicts = self .unique_constraints ()
1300+
1301+ if self .badness_test (2 ):
1302+ self .badness_log (2 , "badness for group: %s has %u constraints\n " % (self .group .acronym , len (conflicts )))
1303+ from settings import BADNESS_UNPLACED , BADNESS_TOOSMALL_50 , BADNESS_TOOSMALL_100 , BADNESS_TOOBIG , BADNESS_MUCHTOOBIG # pylint: disable=import-error
1304+ count = 0
1305+ myss_list = assignments [self .group ]
1306+ # for each constraint of this sessions' group, by group
1307+ if len (myss_list )== 0 :
1308+ if self .badness_test (2 ):
1309+ self .badness_log (2 , " 0group: %s is unplaced\n " % (self .group .acronym ))
1310+ return BADNESS_UNPLACED
1311+
1312+ for myss in myss_list :
1313+ if self .attendees is None or myss .timeslot is None or myss .timeslot .location .capacity is None :
1314+ continue
1315+ mismatch = self .attendees - myss .timeslot .location .capacity
1316+ if mismatch > 100 :
1317+ # the room is too small by 100
1318+ badness += BADNESS_TOOSMALL_100
1319+ elif mismatch > 50 :
1320+ # the room is too small by 50
1321+ badness += BADNESS_TOOSMALL_50
1322+ elif mismatch < 50 :
1323+ # the room is too big by 50
1324+ badness += BADNESS_TOOBIG
1325+ elif mismatch < 100 :
1326+ # the room is too big by 100 (not intimate enough)
1327+ badness += BADNESS_MUCHTOOBIG
1328+
1329+ for group ,constraint in conflicts .items ():
1330+ if group is None :
1331+ # must not be a group constraint.
1332+ continue
1333+ count += 1
1334+ # get the list of sessions for other group.
1335+ sess_count = 0
1336+ if group in assignments :
1337+ sess_count = len (assignments [group ])
1338+ if self .badness_test (4 ):
1339+ self .badness_log (4 , " [%u] 1group: %s session_count: %u\n " % (count , group .acronym , sess_count ))
1340+
1341+ # see if the other group which is conflicted, has an assignment,
1342+ if group in assignments :
1343+ other_sessions = assignments [group ]
1344+ # and if it does, see if any of it's sessions conflict with any of my sessions
1345+ # (each group could have multiple slots)
1346+ #if self.badness_test(4):
1347+ # self.badness_log(4, " [%u] 9group: other sessions: %s\n" % (count, other_sessions))
1348+ for ss in other_sessions :
1349+ # this causes additional database dips
1350+ #if self.badness_test(4):
1351+ # self.badness_log(4, " [%u] 9group: ss: %s %s\n" % (count, ss, ss.faked))
1352+ if ss .session is None :
1353+ continue
1354+ if ss .timeslot is None :
1355+ continue
1356+ if self .badness_test (3 ):
1357+ self .badness_log (3 , " [%u] 2group: %s vs ogroup: %s\n " % (count , self .group .acronym , ss .session .group .acronym ))
1358+ if ss .session .group .acronym == self .group .acronym :
1359+ continue
1360+ if self .badness_test (3 ):
1361+ self .badness_log (3 , " [%u] 3group: %s sessions: %s\n " % (count , group .acronym , ss .timeslot .time ))
1362+ # see if they are scheduled at the same time.
1363+ conflictbadness = 0
1364+ for myss in myss_list :
1365+ if myss .timeslot is None :
1366+ continue
1367+ if self .badness_test (3 ):
1368+ self .badness_log (3 , " [%u] 4group: %s my_sessions: %s vs %s\n " % (count , group .acronym , myss .timeslot .time , ss .timeslot .time ))
1369+ if ss .timeslot .time == myss .timeslot .time :
1370+ newcost = constraint .constraint_cost ()
1371+ if self .badness_test (2 ):
1372+ self .badness_log (2 , " [%u] 5group: %s conflict(%s): %s on %s cost %u\n " % (count , self .group .acronym , constraint .name_id , ss .session .group .acronym , ss .timeslot .time , newcost ))
1373+ # yes accumulate badness.
1374+ conflictbadness += newcost
1375+ ss .badness = conflictbadness
1376+ ss .save ()
1377+ badness += conflictbadness
1378+ # done
1379+ if self .badness_test (1 ):
1380+ self .badness_log (1 , "badgroup: %s badness = %u\n " % (self .group .acronym , badness ))
1381+ return badness
1382+
1383+ def setup_conflicts (self ):
1384+ log .unreachable ()
1385+ conflicts = self .unique_constraints ()
1386+
1387+ self .session_conflicts = []
1388+
1389+ for group ,constraint in conflicts .items ():
1390+ if group is None :
1391+ # must not be a group constraint, people constraints TBD.
1392+ continue
1393+
1394+ # get the list of sessions for other group.
1395+ for session in self .meeting .session_set .filter (group = group ):
1396+ # make a tuple...
1397+ conflict = (session .pk , constraint )
1398+ self .session_conflicts .append (conflict )
1399+
1400+ # This evaluates the current session based upon the constraints
1401+ # given. The conflicts have first been shorted into an array (session_conflicts)
1402+ # as a tuple, and include the constraint itself.
1403+ #
1404+ # While the conflicts are listed by group, the conflicts listed here
1405+ # have been resolved into pk of session requests that will conflict.
1406+ # This is to make comparison be a straight integer comparison.
1407+ #
1408+ # scheduleslot contains the list of sessions which are at the same time as
1409+ # this item.
1410+ #
1411+ # timeslot is where this item has been scheduled.
1412+ #
1413+ # MATH.
1414+ # each failed conflic3 is worth 1000 points
1415+ # each failed conflic2 is worth 10000 points
1416+ # each failed conflic1 is worth 100000 points
1417+ # being in a room too small than asked is worth 200,000 * (size/50)
1418+ # being in a room too big by more than 100 is worth 200,000 once.
1419+ # a conflict where AD must be in two places is worth 500,000.
1420+ # not being scheduled is worth 10,000,000 points
1421+ #
1422+ def badness_fast (self , timeslot , scheduleslot , session_pk_list ):
1423+ log .unreachable ()
1424+ from settings import BADNESS_UNPLACED , BADNESS_TOOSMALL_50 , BADNESS_TOOSMALL_100 , BADNESS_TOOBIG , BADNESS_MUCHTOOBIG # pylint: disable=import-error
1425+
1426+ badness = 0
1427+
1428+ # see if item has not been scheduled
1429+ if timeslot is None :
1430+ return BADNESS_UNPLACED
1431+
1432+ # see if this session is in too small a place.
1433+ if self .attendees is not None and timeslot .location .capacity is not None :
1434+ mismatch = self .attendees - timeslot .location .capacity
1435+ if mismatch > 100 :
1436+ # the room is too small by 100
1437+ badness += BADNESS_TOOSMALL_100
1438+ elif mismatch > 50 :
1439+ # the room is too small by 50
1440+ badness += BADNESS_TOOSMALL_50
1441+ elif mismatch < 50 :
1442+ # the room is too big by 50
1443+ badness += BADNESS_TOOBIG
1444+ elif mismatch < 100 :
1445+ # the room is too big by 100 (not intimate enough)
1446+ badness += BADNESS_MUCHTOOBIG
1447+
1448+ # now go through scheduleslot items and see if any are conflicts
1449+ # inner loop is the shorter one, usually max 8 rooms.
1450+ for conflict in self .session_conflicts :
1451+ for pkt in session_pk_list :
1452+ pk = pkt [0 ]
1453+ if pk == self .pk : # ignore conflicts with self.
1454+ continue
1455+
1456+ if conflict [0 ] == pk :
1457+ ss = pkt [1 ]
1458+ if ss .timeslot is not None and ss .timeslot .location == timeslot .location :
1459+ continue # ignore conflicts when two sessions in the same room
1460+ constraint = conflict [1 ]
1461+ badness += constraint .constraint_cost ()
1462+
1463+ if self .badness_test (1 ):
1464+ self .badness_log (1 , "badgroup: %s badness = %u\n " % (self .group .acronym , badness ))
1465+ return badness
1466+
0 commit comments