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)
Exemple #2
0
    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]])
Exemple #4
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]])