def test_there_should_be_at_least_x_days_between_ops_of_shift_types(
        model, build_run_data, build_expressions, people):
    constraint = ThereShouldBeAtLeastXDaysBetweenOpsOfShiftTypes(
        priority=0, x=8, shift_types=["SPECIAL_A", "SPECIAL_B"])

    history = History.build(past_shifts=[
        AssignedShift("shift", ShiftType.SPECIAL_A, date(2018, 12, 29),
                      people[0]),
        AssignedShift("shift", ShiftType.SPECIAL_B, date(2018, 12, 22),
                      people[0]),
        AssignedShift("shift", ShiftType.SPECIAL_A, date(2018, 12, 23),
                      people[1]),
        AssignedShift("shift", ShiftType.STANDARD, date(2018, 12, 28),
                      people[1]),
    ])
    data = build_run_data(history=history)
    assignments = init_assignments(model, data)
    expressions = build_expressions(constraint, data, assignments)

    # Second person is ok to assign because they had more than 8 free days
    assert evaluate(assignments, ((1, 0, 0, 0), ), expressions)
    assert evaluate(assignments, ((1, 0, 4, 0), ), expressions)
    assert evaluate(assignments, ((1, 0, 5, 0), ), expressions)

    # First person has just been on a special_a shift
    assert evaluate(assignments, ((0, 0, 0, 0), ), expressions)
    assert not evaluate(assignments, ((0, 0, 4, 0), ), expressions)
    assert not evaluate(assignments, ((0, 0, 5, 0), ), expressions)
def test_basic_assignment():
    shifts = [
        Shift(name="shift",
              shift_type=ShiftType.STANDARD,
              day=date(2019, 1, 1)),
        Shift(name="shift",
              shift_type=ShiftType.STANDARD,
              day=date(2019, 1, 2)),
        Shift(name="shift",
              shift_type=ShiftType.STANDARD,
              day=date(2019, 1, 3)),
        Shift(name="shift",
              shift_type=ShiftType.STANDARD,
              day=date(2019, 1, 4)),
        Shift(name="shift",
              shift_type=ShiftType.STANDARD,
              day=date(2019, 1, 5)),
        Shift(name="shift",
              shift_type=ShiftType.STANDARD,
              day=date(2019, 1, 6)),
        Shift(name="shift",
              shift_type=ShiftType.STANDARD,
              day=date(2019, 1, 7)),
    ]
    config = Config.build(
        people=[Person(name=f"person_{index}") for index in range(7)],
        max_shifts_per_person=1,
        shifts_by_day={shift.day: [shift]
                       for shift in shifts},
        history=History.build(),
    )
    solution = solve(config)
    assert len(list(solution)) == 7
 def build(history=History.build()):
     run_data = Config.build(
         people=people,
         max_shifts_per_person=2,
         shifts_by_day=shifts_per_day,
         history=history,
     )
     return run_data
Exemple #4
0
def _parse_history(history) -> History:
    offsets = []
    shifts = []

    for offset in history["offsets"]:
        offsets.append(PastShiftOffset.from_json(offset))

    for shift in history["shifts"]:
        shifts.append(AssignedShift.from_json(shift))

    return History.build(past_shifts=shifts, offsets=offsets)
def test_date_last_on_shift():
    person_a = Person("a")
    person_b = Person("b")
    person_c = Person("c")
    person_d = Person("d")

    history = History.build([
        AssignedShift(
            "shift",
            ShiftType.SPECIAL_A,
            date(2019, 8, 31),
            person_a,
        ),
        AssignedShift(
            "shift",
            ShiftType.SPECIAL_B,
            date(2019, 9, 1),
            person_b,
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            date(2019, 9, 2),
            person_a,
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            date(2019, 9, 3),
            person_b,
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            date(2019, 9, 4),
            person_a,
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            date(2019, 9, 5),
            person_c,
        ),
    ])

    metrics = HistoryMetrics.build(history, [person_a, person_b, person_d],
                                   date(2019, 9, 8))

    assert metrics.date_last_on_shift == {
        person_a: date(2019, 9, 4),
        person_b: date(2019, 9, 3),
        person_d: NEVER,
    }
Exemple #6
0
def test_objective_function_for_weekdays(model, build_run_data, people, now):
    history = History.build(past_shifts=[
        # A has done 2 past shifts, most recent is 4 days ago
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=4),
            people[0],
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=5),
            people[0],
        ),
        # B has done 2 past shifts, most recent is 3 days ago
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=3),
            people[1],
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=7),
            people[1],
        ),
        # C has done 1 past shifts, most recent is 6 days ago
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=6),
            people[2],
        ),
        # D has done no past shifts
    ])

    data = build_run_data(history=history)
    assignments = init_assignments(model, data)
    objective = RankingWeight()

    assert evaluate(
        assignments,
        ((3, 0, 0, 0), (2, 1, 1, 0), (1, 0, 2, 0)),
        objective.objective(assignments, data),
    ) == (107 + 2 + 104)
def test_there_should_be_at_least_x_days_between_ops(model, build_run_data,
                                                     build_expressions, people,
                                                     shifts_per_day):
    constraint = ThereShouldBeAtLeastXDaysBetweenOps(priority=0, x=1)

    history = History.build(past_shifts=[
        AssignedShift("shift", ShiftType.STANDARD, date(2018, 12, 31),
                      people[0])
    ])
    data = build_run_data(history=history)
    assignments = init_assignments(model, data)
    expressions = build_expressions(constraint, data, assignments)

    # One day gap between shifts
    assert evaluate(assignments, ((0, 0, 1, 0), ), expressions)

    # Shifts are back to back
    assert not evaluate(assignments, ((0, 0, 0, 0), ), expressions)
def test_num_of_shifts():
    person_a = Person("a")
    person_b = Person("b")
    person_c = Person("c")

    history = History.build([
        AssignedShift(
            "shift",
            ShiftType.SPECIAL_A,
            date(2019, 8, 31),
            person_a,
        ),
        AssignedShift(
            "shift",
            ShiftType.SPECIAL_B,
            date(2019, 9, 1),
            person_b,
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            date(2019, 9, 2),
            person_a,
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            date(2019, 9, 3),
            person_b,
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            date(2019, 9, 4),
            person_a,
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            date(2019, 9, 5),
            person_c,
        ),
    ])

    metrics = HistoryMetrics.build(history, [person_a, person_b],
                                   date(2019, 9, 5))

    assert metrics.num_of_shifts == {
        ShiftType.STANDARD: {
            person_a: 2,
            person_b: 1
        },
        ShiftType.SPECIAL_A: {
            person_a: 1,
            person_b: 0
        },
        ShiftType.SPECIAL_B: {
            person_a: 0,
            person_b: 1
        },
    }
def test_num_of_shifts_with_offsets():
    person_a = Person("a")
    person_b = Person("b")
    person_c = Person("c")

    history = History.build(
        past_shifts=[
            AssignedShift(
                "shift",
                ShiftType.SPECIAL_A,
                date(2019, 8, 31),
                person_a,
            ),
            AssignedShift(
                "shift",
                ShiftType.SPECIAL_B,
                date(2019, 9, 1),
                person_b,
            ),
            AssignedShift(
                "shift",
                ShiftType.STANDARD,
                date(2019, 9, 2),
                person_a,
            ),
            AssignedShift(
                "shift",
                ShiftType.STANDARD,
                date(2019, 9, 3),
                person_b,
            ),
            AssignedShift(
                "shift",
                ShiftType.STANDARD,
                date(2019, 9, 4),
                person_a,
            ),
            AssignedShift(
                "shift",
                ShiftType.STANDARD,
                date(2019, 9, 5),
                person_c,
            ),
        ],
        offsets=[
            PastShiftOffset(person=person_a,
                            shift_type=ShiftType.STANDARD,
                            offset=2),
            PastShiftOffset(person=person_b,
                            shift_type=ShiftType.SPECIAL_A,
                            offset=1),
            PastShiftOffset(person=person_c,
                            shift_type=ShiftType.SPECIAL_B,
                            offset=5),
        ],
    )

    metrics = HistoryMetrics.build(history, [person_a, person_b],
                                   date(2019, 9, 5))

    assert metrics.num_of_shifts == {
        ShiftType.STANDARD: {
            person_a: 4,
            person_b: 1
        },
        ShiftType.SPECIAL_A: {
            person_a: 1,
            person_b: 1
        },
        ShiftType.SPECIAL_B: {
            person_a: 0,
            person_b: 1,
            person_c: 5
        },
    }
Exemple #10
0
def test_objective_function_for_entire_week(model, build_run_data, people,
                                            now):
    history = History.build(past_shifts=[
        # STANDARD
        # A has done 2 past shifts, most recent is 4 days ago
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=4),
            people[0],
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=5),
            people[0],
        ),
        # B has done 2 past shifts, most recent is 3 days ago
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=3),
            people[1],
        ),
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=7),
            people[1],
        ),
        # C has done 1 past shifts, most recent is 6 days ago
        AssignedShift(
            "shift",
            ShiftType.STANDARD,
            now - timedelta(days=6),
            people[2],
        ),
        # D has done no past shifts
        # SPECIAL_A
        # A has done 1 past shift, most recent is 2 days ago
        AssignedShift(
            "shift",
            ShiftType.SPECIAL_A,
            now - timedelta(days=2),
            people[0],
        ),
        # B has done no past shifts
        # C has done no past shifts
        # D has done 1 past shift, most recent is 9 days ago
        AssignedShift(
            "shift",
            ShiftType.SPECIAL_A,
            now - timedelta(days=9),
            people[3],
        ),
        # SPECIAL_B
        # A has done no past shifts
        # B has done no past shifts
        # C has done 1 past shift, most recent is 1 days ago
        AssignedShift(
            "shift",
            ShiftType.SPECIAL_B,
            now - timedelta(days=1),
            people[2],
        ),  # Sun
        # D has done no past shifts
    ])

    data = build_run_data(history=history)
    assignments = init_assignments(model, data)
    objective = RankingWeight()

    expected_standard_weight = 107 + 2 + 105
    expected_special_a_weight = 105
    expected_special_b_weight = 104

    assert (evaluate(
        assignments,
        (
            # Standard
            (2, 1, 1, 0),
            (1, 0, 2, 0),
            (3, 0, 3, 0),
        ),
        objective.objective(assignments, data),
    ) == expected_standard_weight)

    assert (evaluate(
        assignments,
        (
            # Special A
            (3, 0, 4, 1), ),
        objective.objective(assignments, data),
    ) == expected_special_a_weight)

    assert (evaluate(
        assignments,
        (
            # Special B
            (2, 0, 5, 2), ),
        objective.objective(assignments, data),
    ) == expected_special_b_weight)

    assert (evaluate(
        assignments,
        (
            # Standard
            (2, 1, 1, 0),
            (1, 0, 2, 0),
            (3, 0, 3, 0),
            # Special A
            (3, 0, 4, 1),
            # Special B
            (2, 0, 5, 2),
        ),
        objective.objective(assignments, data),
    ) == expected_standard_weight + expected_special_a_weight +
            expected_special_b_weight)