def __allocate_timeslot__(self, constraint_level, yesterday_profile, current_timeslot, holidays): if ConstraintStrategy.contains(ConstraintStrategy.ALLOCATE_MORNING_DIALYSIS.value, constraint_level): if current_timeslot == TimeSlot.FIRST_SHIFT and yesterday_profile is not None and TimeSlot.THIRD_SHIFT in yesterday_profile: yesterday_obligations = list(set(self.obligation_activities) & set([x for x in yesterday_profile[TimeSlot.THIRD_SHIFT]])) if len(yesterday_obligations) == 1 and yesterday_profile[TimeSlot.THIRD_SHIFT][yesterday_obligations[0]] is not None: today_activities = list(set([Activity.DIALYSIS] + self.obligation_activities) & set([x for x in self.profile[current_timeslot]])) if len(today_activities) == 1: self.__slot__(current_timeslot, today_activities[0], yesterday_profile[TimeSlot.THIRD_SHIFT][yesterday_obligations[0]]) # problem.addConstraint(lambda nep, act: act in [Activity.DIALYSIS] + obligation_activities and nep.id == yesterday_profile[TimeSlot.THIRD_SHIFT][yesterday_obligations[0]].id, (self.nephrologist_key, self.activity_key)) else: current_team, problem = self.__team_problem__(current_timeslot, holidays) if problem is not None: if ConstraintStrategy.contains(ConstraintStrategy.FOCUS_ON_PREFERENCES.value, constraint_level): problem.addConstraint(lambda nep, act: nep.score(self.weekday, current_timeslot, act) > 0, (self.nephrologist_key, self.activity_key)) else: # avoid strict equality to take into account that more than one nephrologist could have the same preference... problem.addConstraint(lambda nep, act: nep.score(self.weekday, current_timeslot, act) >= 0, (self.nephrologist_key, self.activity_key)) if not ConstraintStrategy.contains(ConstraintStrategy.DISCARD_COUNTERS.value, constraint_level): problem.addConstraint(lambda nep, act: self.__is_most_rested_nephrologist__(current_team, nep, act), (self.nephrologist_key, self.activity_key)) pass solutions = problem.getSolutions() for solution in solutions: if solution[self.nephrologist_key].id not in self.__currently_allocated_nephrologists__(current_timeslot): if self.__slot__(current_timeslot, solution[self.activity_key], solution[self.nephrologist_key]): self.__allocate_timeslot__(constraint_level, yesterday_profile, current_timeslot, holidays)
def __allocate_timeslot__(self, constraint_level, yesterday_profile, current_timeslot, holidays): if ConstraintStrategy.contains( ConstraintStrategy.ALLOCATE_MORNING_DIALYSIS.value, constraint_level): if current_timeslot == TimeSlot.FIRST_SHIFT and yesterday_profile is not None and TimeSlot.THIRD_SHIFT in yesterday_profile: yesterday_obligations = list( set(self.obligation_activities) & set([x for x in yesterday_profile[TimeSlot.THIRD_SHIFT]])) if len(yesterday_obligations) == 1 and yesterday_profile[ TimeSlot.THIRD_SHIFT][ yesterday_obligations[0]] is not None: today_activities = list( set([Activity.DIALYSIS] + self.obligation_activities) & set([x for x in self.profile[current_timeslot]])) if len(today_activities) == 1: self.__slot__( current_timeslot, today_activities[0], yesterday_profile[TimeSlot.THIRD_SHIFT][ yesterday_obligations[0]]) # problem.addConstraint(lambda nep, act: act in [Activity.DIALYSIS] + obligation_activities and nep.id == yesterday_profile[TimeSlot.THIRD_SHIFT][yesterday_obligations[0]].id, (self.nephrologist_key, self.activity_key)) else: current_team, problem = self.__team_problem__( current_timeslot, holidays) if problem is not None: if ConstraintStrategy.contains( ConstraintStrategy.FOCUS_ON_PREFERENCES.value, constraint_level): problem.addConstraint( lambda nep, act: nep.score(self.weekday, current_timeslot, act) > 0, (self.nephrologist_key, self.activity_key)) else: # avoid strict equality to take into account that more than one nephrologist could have the same preference... problem.addConstraint( lambda nep, act: nep.score(self.weekday, current_timeslot, act) >= 0, (self.nephrologist_key, self.activity_key)) if not ConstraintStrategy.contains( ConstraintStrategy.DISCARD_COUNTERS.value, constraint_level): problem.addConstraint( lambda nep, act: self.__is_most_rested_nephrologist__( current_team, nep, act), (self.nephrologist_key, self.activity_key)) pass solutions = problem.getSolutions() for solution in solutions: if solution[ self. nephrologist_key].id not in self.__currently_allocated_nephrologists__( current_timeslot): if self.__slot__(current_timeslot, solution[self.activity_key], solution[self.nephrologist_key]): self.__allocate_timeslot__(constraint_level, yesterday_profile, current_timeslot, holidays)
def __allocate_whole_day__(self, constraint_level, yesterday_profile, holidays): for current_timeslot in self.profile: if ConstraintStrategy.contains(ConstraintStrategy.ALLOCATE_WEEKEND_DAYS.value, constraint_level): ''' N must have clearance for all the activities: [[OTHERS, DIALYSIS, OBLIGATION]|[OBLIGATION_HOLIDAY] + [OBLIGATION_WEEKEND]] N must minimize counters for OBLIGATION_WEEKEND, then optionally for [OTHERS, DIALYSIS, OBLIGATION] or [OBLIGATION_HOLIDAY] N does OTHERS on FIRST_SHIFT (or OBLIGATION_HOLIDAY if DAY is holiday) N does DIALYSIS on SECOND_SHIFT (or OBLIGATION_HOLIDAY if DAY is holiday) N does OBLIGATION on THIRD_SHIFT (or OBLIGATION_HOLIDAY if DAY is holiday) N does OBLIGATION_WEEKEND on all SHIFTS, DAY+1 N does OBLIGATION_WEEKEND on all SHIFTS, DAY+2 Where: - N: the chosen nephrologist - DAY: Friday ''' if self.weekday == 4: # friday if current_timeslot == TimeSlot.FIRST_SHIFT: current_team, problem = self.__team_problem__(current_timeslot, holidays, range(1, 4)) # print(current_team) if problem is not None: # nephrologist has to have clearance for following activities: Activity.OTHERS, Activity.DIALYSIS, Activity.OBLIGATION problem.addConstraint(lambda nep: Activity.OTHERS in nep.activities and Activity.DIALYSIS in nep.activities and Activity.OBLIGATION in nep.activities, (self.nephrologist_key)) # nephrologist with lesser contribution to weekend obligation activity is selected problem.addConstraint(lambda nep: True in [self.__is_most_rested_nephrologist__(current_team, nep, Activity.OBLIGATION_WEEKEND)], (self.nephrologist_key)) solutions = problem.getSolutions() if len(solutions) > 0: current_shift_activities = list(set([Activity.OTHERS] + self.obligation_activities) & set([x for x in self.profile[current_timeslot]])) if len(current_shift_activities) == 1: # extracting the nephrologist resolved from the constraint problem solving eligible_one = solutions[0][self.nephrologist_key] # allocating the nephrologist on his TimeSlot.FIRST_SHIFT for Activity.OTHERS self.__slot__(current_timeslot, current_shift_activities[0], eligible_one) # allocating the recovery shift to nephrologist under obligation for next week. recovery_shift = None recovery_preferences = [(x, y) for x in eligible_one.preferences for y in eligible_one.preferences[x] if Activity.OBLIGATION_RECOVERY in eligible_one.preferences[x][y]] if len(recovery_preferences) == 0: # eligible nephrologist has no preferences for obligation recovery # generating all possible recovery shifts for the eligible nephrologist within day [0, 4] for Activity.OBLIGATION_RECOVERY that has not a negative score recovery_possibilities = [(x, y) for x in [0, 1, 2, 3, 4] for y in [TimeSlot.FIRST_SHIFT, TimeSlot.SECOND_SHIFT] if eligible_one.score(x, y, Activity.OBLIGATION_RECOVERY) == 0] # picking one randomly out of all the possible shifts list if len(recovery_possibilities) > 0: recovery_shift = recovery_possibilities[randint(0, len(recovery_possibilities)-1)] # recovery_shift = recovery_possibilities[0] else: # eligible nephrologist has at least one preferential shift for Activity.OBLIGATION_RECOVERY # picking one preference out of the preference list in a random fashion recovery_shift = recovery_preferences[randint(0, len(recovery_preferences)-1)] # recovery_shift = recovery_preferences[0] # TODO: recompute holidays to take modification into account! # print(str(eligible_one) + ": " + str(recovery_shift)) if recovery_shift is not None: recovery_index_day, recovery_slot = recovery_shift eligible_one.holidays.append(FreeSlots(self.date + timedelta(days=3+recovery_index_day), [recovery_slot])) else: # retrieving the nephrologist allocated for imminent weekend obligation last_shift_activities = list(set([Activity.OTHERS] + self.obligation_activities) & set([x for x in self.profile[TimeSlot.FIRST_SHIFT]])) if len(last_shift_activities) == 1: if current_timeslot == TimeSlot.SECOND_SHIFT: current_shift_activities = list(set([Activity.DIALYSIS] + self.obligation_activities) & set([x for x in self.profile[current_timeslot]])) if len(current_shift_activities) == 1: # allocating the nephrologist on his TimeSlot.SECOND_SHIFT for Activity.DIALYSIS self.__slot__(current_timeslot, current_shift_activities[0], self.profile[TimeSlot.FIRST_SHIFT][last_shift_activities[0]]) elif current_timeslot == TimeSlot.THIRD_SHIFT: current_shift_activities = list(set([Activity.OBLIGATION] + self.obligation_activities) & set([x for x in self.profile[current_timeslot]])) if len(current_shift_activities) == 1: # allocating the nephrologist on his TimeSlot.THIRD_SHIFT for Activity.OBLIGATION self.__slot__(current_timeslot, current_shift_activities[0], self.profile[TimeSlot.FIRST_SHIFT][last_shift_activities[0]]) elif self.weekday in [5, 6] and yesterday_profile is not None and TimeSlot.THIRD_SHIFT in yesterday_profile: # saturday, sunday yesterday_obligations = list(set(self.obligation_activities) & set([x for x in yesterday_profile[TimeSlot.THIRD_SHIFT]])) if len(yesterday_obligations) == 1 and yesterday_profile[TimeSlot.THIRD_SHIFT][yesterday_obligations[0]] is not None: today_activities = list(set(self.obligation_activities) & set([x for x in self.profile[current_timeslot]])) if len(today_activities) == 1: self.__slot__(current_timeslot, today_activities[0], yesterday_profile[TimeSlot.THIRD_SHIFT][yesterday_obligations[0]]) elif ConstraintStrategy.contains(ConstraintStrategy.ALLOCATE_HOLIDAYS.value, constraint_level): current_team, problem = self.__team_problem__(current_timeslot, holidays) if current_timeslot == TimeSlot.FIRST_SHIFT: if problem is not None: # nephrologist with lesser contribution to weekend obligation activity is selected problem.addConstraint(lambda nep: True in [self.__is_most_rested_nephrologist__(current_team, nep, Activity.OBLIGATION_HOLIDAY)], (self.nephrologist_key)) solutions = problem.getSolutions() if len(solutions) > 0: current_shift_activities = list(set([Activity.OBLIGATION_HOLIDAY]) & set([x for x in self.profile[current_timeslot]])) if len(current_shift_activities) == 1: self.__slot__(current_timeslot, current_shift_activities[0], solutions[0][self.nephrologist_key]) else: last_shift_activities = list(set([Activity.OBLIGATION_HOLIDAY]) & set([x for x in self.profile[TimeSlot.FIRST_SHIFT]])) if len(last_shift_activities) == 1: current_shift_activities = list(set([Activity.OBLIGATION_HOLIDAY]) & set([x for x in self.profile[current_timeslot]])) if len(current_shift_activities) == 1: self.__slot__(current_timeslot, current_shift_activities[0], self.profile[TimeSlot.FIRST_SHIFT][last_shift_activities[0]])
def __allocate_whole_day__(self, constraint_level, yesterday_profile, holidays): for current_timeslot in self.profile: if ConstraintStrategy.contains( ConstraintStrategy.ALLOCATE_WEEKEND_DAYS.value, constraint_level): ''' N must have clearance for all the activities: [[OTHERS, DIALYSIS, OBLIGATION]|[OBLIGATION_HOLIDAY] + [OBLIGATION_WEEKEND]] N must minimize counters for OBLIGATION_WEEKEND, then optionally for [OTHERS, DIALYSIS, OBLIGATION] or [OBLIGATION_HOLIDAY] N does OTHERS on FIRST_SHIFT (or OBLIGATION_HOLIDAY if DAY is holiday) N does DIALYSIS on SECOND_SHIFT (or OBLIGATION_HOLIDAY if DAY is holiday) N does OBLIGATION on THIRD_SHIFT (or OBLIGATION_HOLIDAY if DAY is holiday) N does OBLIGATION_WEEKEND on all SHIFTS, DAY+1 N does OBLIGATION_WEEKEND on all SHIFTS, DAY+2 Where: - N: the chosen nephrologist - DAY: Friday ''' if self.weekday == 4: # friday if current_timeslot == TimeSlot.FIRST_SHIFT: current_team, problem = self.__team_problem__( current_timeslot, holidays, range(1, 4)) # print(current_team) if problem is not None: # nephrologist has to have clearance for following activities: Activity.OTHERS, Activity.DIALYSIS, Activity.OBLIGATION problem.addConstraint( lambda nep: Activity.OTHERS in nep.activities and Activity.DIALYSIS in nep.activities and Activity.OBLIGATION in nep.activities, (self.nephrologist_key)) # nephrologist with lesser contribution to weekend obligation activity is selected problem.addConstraint( lambda nep: True in [ self.__is_most_rested_nephrologist__( current_team, nep, Activity. OBLIGATION_WEEKEND) ], (self.nephrologist_key)) solutions = problem.getSolutions() if len(solutions) > 0: current_shift_activities = list( set([Activity.OTHERS] + self.obligation_activities) & set([ x for x in self.profile[current_timeslot] ])) if len(current_shift_activities) == 1: # extracting the nephrologist resolved from the constraint problem solving eligible_one = solutions[0][ self.nephrologist_key] # allocating the nephrologist on his TimeSlot.FIRST_SHIFT for Activity.OTHERS self.__slot__(current_timeslot, current_shift_activities[0], eligible_one) # allocating the recovery shift to nephrologist under obligation for next week. recovery_shift = None recovery_preferences = [ (x, y) for x in eligible_one.preferences for y in eligible_one.preferences[x] if Activity.OBLIGATION_RECOVERY in eligible_one.preferences[x][y] ] if len( recovery_preferences ) == 0: # eligible nephrologist has no preferences for obligation recovery # generating all possible recovery shifts for the eligible nephrologist within day [0, 4] for Activity.OBLIGATION_RECOVERY that has not a negative score recovery_possibilities = [ (x, y) for x in [0, 1, 2, 3, 4] for y in [ TimeSlot.FIRST_SHIFT, TimeSlot.SECOND_SHIFT ] if eligible_one.score( x, y, Activity. OBLIGATION_RECOVERY) == 0 ] # picking one randomly out of all the possible shifts list if len(recovery_possibilities) > 0: recovery_shift = recovery_possibilities[ randint( 0, len(recovery_possibilities) - 1)] # recovery_shift = recovery_possibilities[0] else: # eligible nephrologist has at least one preferential shift for Activity.OBLIGATION_RECOVERY # picking one preference out of the preference list in a random fashion recovery_shift = recovery_preferences[ randint( 0, len(recovery_preferences) - 1)] # recovery_shift = recovery_preferences[0] # TODO: recompute holidays to take modification into account! # print(str(eligible_one) + ": " + str(recovery_shift)) if recovery_shift is not None: recovery_index_day, recovery_slot = recovery_shift eligible_one.holidays.append( FreeSlots( self.date + timedelta(days=3 + recovery_index_day), [recovery_slot])) else: # retrieving the nephrologist allocated for imminent weekend obligation last_shift_activities = list( set([Activity.OTHERS] + self.obligation_activities) & set([ x for x in self.profile[TimeSlot.FIRST_SHIFT] ])) if len(last_shift_activities) == 1: if current_timeslot == TimeSlot.SECOND_SHIFT: current_shift_activities = list( set([Activity.DIALYSIS] + self.obligation_activities) & set([ x for x in self.profile[current_timeslot] ])) if len(current_shift_activities) == 1: # allocating the nephrologist on his TimeSlot.SECOND_SHIFT for Activity.DIALYSIS self.__slot__( current_timeslot, current_shift_activities[0], self.profile[TimeSlot.FIRST_SHIFT][ last_shift_activities[0]]) elif current_timeslot == TimeSlot.THIRD_SHIFT: current_shift_activities = list( set([Activity.OBLIGATION] + self.obligation_activities) & set([ x for x in self.profile[current_timeslot] ])) if len(current_shift_activities) == 1: # allocating the nephrologist on his TimeSlot.THIRD_SHIFT for Activity.OBLIGATION self.__slot__( current_timeslot, current_shift_activities[0], self.profile[TimeSlot.FIRST_SHIFT][ last_shift_activities[0]]) elif self.weekday in [ 5, 6 ] and yesterday_profile is not None and TimeSlot.THIRD_SHIFT in yesterday_profile: # saturday, sunday yesterday_obligations = list( set(self.obligation_activities) & set([ x for x in yesterday_profile[TimeSlot.THIRD_SHIFT] ])) if len(yesterday_obligations) == 1 and yesterday_profile[ TimeSlot.THIRD_SHIFT][ yesterday_obligations[0]] is not None: today_activities = list( set(self.obligation_activities) & set([x for x in self.profile[current_timeslot]])) if len(today_activities) == 1: self.__slot__( current_timeslot, today_activities[0], yesterday_profile[TimeSlot.THIRD_SHIFT][ yesterday_obligations[0]]) elif ConstraintStrategy.contains( ConstraintStrategy.ALLOCATE_HOLIDAYS.value, constraint_level): current_team, problem = self.__team_problem__( current_timeslot, holidays) if current_timeslot == TimeSlot.FIRST_SHIFT: if problem is not None: # nephrologist with lesser contribution to weekend obligation activity is selected problem.addConstraint( lambda nep: True in [ self.__is_most_rested_nephrologist__( current_team, nep, Activity. OBLIGATION_HOLIDAY) ], (self.nephrologist_key)) solutions = problem.getSolutions() if len(solutions) > 0: current_shift_activities = list( set([Activity.OBLIGATION_HOLIDAY]) & set([ x for x in self.profile[current_timeslot] ])) if len(current_shift_activities) == 1: self.__slot__( current_timeslot, current_shift_activities[0], solutions[0][self.nephrologist_key]) else: last_shift_activities = list( set([Activity.OBLIGATION_HOLIDAY]) & set([x for x in self.profile[TimeSlot.FIRST_SHIFT]])) if len(last_shift_activities) == 1: current_shift_activities = list( set([Activity.OBLIGATION_HOLIDAY]) & set([x for x in self.profile[current_timeslot]])) if len(current_shift_activities) == 1: self.__slot__( current_timeslot, current_shift_activities[0], self.profile[TimeSlot.FIRST_SHIFT][ last_shift_activities[0]])