diff --git a/generate_rules.py b/generate_rules.py index 887918d..d75f217 100644 --- a/generate_rules.py +++ b/generate_rules.py @@ -3,27 +3,38 @@ from math import floor import subprocess -data = json.load(open('backup.json')) +# Maximum time for AK: 2 Timeslots SLOT_LEN = 120 -DAYS = 3 -START = 800 -END = 1800 +DAYS = 1 +START = 480 +END = 1080 class WorkGroup: - def __init__(self, id, interest, track, projector, reso, length, leaders): - self.id = id # must be int + def __init__(self, id, interest, track, projector, reso, length, leaders, constraints): + self.id = id # must be int self.interest = interest self.track = track if track is not None else 'none' # haben alle aks einen track? self.projector = 'true' if projector else 'false' self.reso = 'true' if reso else 'false' self.length = floor(length/SLOT_LEN) + 1 self.leaders = leaders + self.after_time = [] + self.before_time = [] + for constraint in constraints: + if constraint["type"] == 'OnlyAfterTime': + if constraint['workGroup'] is None: + self.after_time.append({'day': constraint['day'], 'time': constraint['time']}) + elif constraint["type"] == 'OnlyBeforeTime': + self.before_time.append({'day': constraint['day'], 'time': constraint['time']}) def __str__(self): - return f'ak({self.id}, {self.interest}, "{self.track}", {self.projector}, {self.reso}, {self.length}).' # TODO Leader + leader_rules = '' + for leader in self.leaders: + leader_rules += f'leader({self.id}, "{leader}").\n' + return f'ak({self.id}, {self.interest}, "{self.track}", {self.projector}, {self.reso}, {self.length}).' + leader_rules class Room: @@ -62,23 +73,45 @@ class Schedule: 'workGroup': self.workgroup, 'room': self.room, 'time': self.time, - 'day': self.day + 'day': self.day, + 'lockRoom': False, + 'lockTime': False, } -workgroups = [] -rooms = [] -id_to_workgroup = {} -id_to_room = {} -for i, workgroup in enumerate(data['workGroups']): - workgroups.append(WorkGroup(workgroup['id'], workgroup['interested'], workgroup['track'], workgroup['projector'], - workgroup['resolution'], workgroup['length'], '')) # TODO add leaders - id_to_workgroup[workgroup['id']] = i -for i, room in enumerate(data['rooms']): - rooms.append(Room(room['id'], room['places'], room['projector'])) # TODO internet, whiteboard & co. - id_to_room[room['id']] = i +def parse_workgroups(data): + workgroups = [] + id_to_workgroup = {} + for i, workgroup in enumerate(data['workGroups']): + workgroups.append( + WorkGroup(workgroup['id'], workgroup['interested'], workgroup['track'], workgroup['projector'], + workgroup['resolution'], workgroup['length'], workgroup['leader'], workgroup['constraints'])) + id_to_workgroup[workgroup['id']] = i + return workgroups, id_to_workgroup + +def parse_rooms(data): + rooms = [] + id_to_room = {} + for i, room in enumerate(data['rooms']): + rooms.append(Room(room['id'], room['places'], room['projector'])) # TODO internet, whiteboard & co. + id_to_room[room['id']] = i + return rooms, id_to_room + + +####################### +# READ BACKUP JSON # +####################### + + +data = json.load(open('backup.json')) + +workgroups, id_to_workgroup = parse_workgroups(data) +rooms, id_to_room = parse_rooms(data) + + rules = """ +%LENGTH = { schedule(AK,TIMESLOT,ROOM): timeslot(TIMESLOT,_,_,_,_), room(ROOM,_,_) } :- ak(AK,_,_,_,_,LENGTH). LENGTH = { schedule(AK,TIMESLOT,ROOM): timeslot(TIMESLOT,_,_,_,_), room(ROOM,_,_) } :- ak(AK,_,_,_,_,LENGTH). % forbid 2AKS in the same room at the same time @@ -92,6 +125,10 @@ LENGTH = { schedule(AK,TIMESLOT,ROOM): timeslot(TIMESLOT,_,_,_,_), room(ROOM,_,_ % forbid two AKs with same leader at same timestep :- schedule(AK1,TIMESTEP,_), schedule(AK2,TIMESTEP,_), leader(AK1,LEADER), leader(AK2,LEADER), AK1 != AK2. + +% schedules of longer aks should be in the same room and in consecutive timeslots +schedule(AK,TIMESLOT2,ROOM) :- ak(AK,_,_,_,_,2), schedule(AK,TIMESLOT1,ROOM), timeslot(TIMESLOT1,ORDER1,_,_,DAY), timeslot(TIMESLOT2,ORDER2,_,_,DAY), timeslot(TIMESLOT3,ORDER3,_,_,_), ORDER2 != ORDER1+1, ORDER1 = ORDER3+1, not schedule(AK,TIMESLOT3,ROOM) . + """ for workgroup in workgroups: @@ -101,15 +138,29 @@ for room in rooms: rules += str(room) + '\n' - -timeslots = [(start, start+200, day) for day in range(DAYS) for start in range(START, END, 200)] +timeslots = [(start, start+SLOT_LEN, day) for day in range(DAYS) for start in range(START, END, SLOT_LEN)] for order, timeslot in enumerate(timeslots): rules += str(Timeslot(order, timeslot)) + '\n' +def time_to_id(timeslots, day, time): + timeslots = list(filter(lambda ts: time>= ts[1][0] and time <= ts[1][1] and day == ts[1][2], enumerate(timeslots))) + return timeslots[0][0] + +for workgroup in filter(lambda wg: wg.after_time, workgroups): + for after_time in workgroup.after_time: + timeslot_id = time_to_id(timeslots, after_time['day'], after_time['time']) + timeslot = timeslots[timeslot_id] # after this slot: ok + print(f':- schedule({workgroup.id}, TIMESLOT, _), ak({workgroup.id},_,_,_,_,_), timeslot(TIMESLOT, ORDER, _, _, _), ORDER < {timeslot_id + 1}.') + rules += f':- schedule({workgroup.id}, TIMESLOT, _), ak({workgroup.id},_,_,_,_,_), timeslot(TIMESLOT, ORDER, _, _, _), ORDER < {timeslot_id + 1}.\n' rules += "#show schedule/3." + +####################### +# GENERATE SCHEDULE # +####################### + output_file_name = "generated_rules.pl" open(output_file_name, 'w').write(rules) @@ -121,40 +172,61 @@ process = ['clingo', output_file_name] completed_process = subprocess.run(process, universal_newlines = True, stdout = subprocess.PIPE, stderr = subprocess.DEVNULL) output = completed_process.stdout +####################### +# OUTPUT SCHEDULE # +####################### -if 'SATISFIABLE' not in output: +if 'UNSATISFIABLE' in output: print('UNSATISFIABLE') + print(output) else: - + print('SATISFIABLE, outputting schedule...') import re - - match = re.match('(.|\n)*Answer: (.+)\n(.*)\nSATISFIABLE', output) - schedules_string = match.group(3) + try: + match = re.match('(.|\n)*Answer: (.+)\n(.*)\nSATISFIABLE', output) + schedules_string = match.group(3) + except Exception as e: + print(e) + print(output) schedules = [] + from collections import defaultdict + workgroup_to_schedule = defaultdict(list) for i, schedule in enumerate(schedules_string.split('schedule')[1:]): match = re.match('\((.+),"(.+)#(.+)#.+",(.+)\)', schedule) - workgroup_id = id_to_workgroup[int(match.group(1))] - room_id = id_to_room[int(match.group(4))] - schedules.append( - { - 'id': i, - 'workGroup': data['workGroups'][workgroup_id], - 'room': data['rooms'][room_id], - 'time': match.group(3), - 'day': match.group(2), - 'lockRoom': False, - 'lockTime': False, - }) - s = { - 'id': i, - 'workGroup': data['workGroups'][workgroup_id], - 'room': data['rooms'][room_id], - 'time': match.group(3), - 'day': match.group(2), - 'lockRoom': False, - 'lockTime': False, - } - #json.dumps(s) + try: + workgroup_id = id_to_workgroup[int(match.group(1))] + room_id = id_to_room[int(match.group(4))] + schedules.append(Schedule(i, data['workGroups'][workgroup_id], match.group(2), match.group(3), data['rooms'][room_id])) + # { + # 'id': i, + # 'workGroup': data['workGroups'][workgroup_id], + # 'room': data['rooms'][room_id], + # 'time': match.group(3), + # 'day': match.group(2), + # 'lockRoom': False, + # 'lockTime': False, + # }) + workgroup_to_schedule[workgroup_id].append(i) + except Exception as e: + print(e) + + schedules_filtered = [] + + for workgroup_id in workgroup_to_schedule: + schedules_for_workgroup = workgroup_to_schedule[workgroup_id] + if len(schedules_for_workgroup) == 1: + schedules_filtered.append(schedules[schedules_for_workgroup[0]]) + else: + # more than one schedule contains workgroup, because workgroup is 2 slots long + # find out which one is the first one and remove the second workgroup: + schedules_for_workgroup_0 = schedules[schedules_for_workgroup[0]] + schedules_for_workgroup_1 = schedules[schedules_for_workgroup[1]] + if schedules_for_workgroup_0.time < schedules_for_workgroup_1.time: + schedules_filtered.append(schedules_for_workgroup_0) + else: + schedules_filtered.append(schedules_for_workgroup_1) + + data = {} - data['schedules'] = schedules + data['schedules'] = [s.to_dict() for s in schedules_filtered] json.dump(data, open('output.json', 'w'))