Skip to content

WIP: a new rototron engine #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

WIP: a new rototron engine #189

wants to merge 2 commits into from

Conversation

rjbs
Copy link
Member

@rjbs rjbs commented Sep 18, 2022

This is not perfect, or probably even 10 years perfect, but I think it's a big improvement, and should be pretty easy to finish delivering.

The old rototron worked like this:

every week has a Weeks Since Epoch number, WSE

every rotor has a list of workers

for each rotor:
  candidate-list = all workers for rotor, deterministically sorted

  for each day in the week being scheduled:
    CANDIDATE: until scheduled or worker list exhausted:
      candidate = delete candidate-list[ WSE % candidate-list.size ]

      if candidate is unavailable or on leave
        next CANDIDATE

GOOD: Because the WSE number is stable within a week, the first choice for any day in the week is the same, as is the second. That means a week will favor being one person. If that person is not available for 2 days, the same person is likely to be fallback.

BAD: Not too much prevents week 1's first choice from being week 2's second choice, meaning the odds of getting two weeks in a row, when somebody is out for a week, are poorly defined. (Insert link to number theory about coprimality here.)

BAD: Rotors are entirely unaware of each other, so nothing much prevents someone being assigned three rotations in a week.

The new rototron (not fully implemented yet) works like this:

every week has a Weeks Since Epoch number, WSE

every rotor has a map of workers to fixed preference¹

every rotor-worker has a level of rotor-fatigue²

ROTOR: for each rotor:
  for every worker in worker map:
    if worker has any other rotor assignment in this date range:
      mark worker as one level less preferable

  for every level of preferability:
    select set of workers available on the most days³

    for each worker in this set:
      select set of workers with the lowest rotor-fatigue

      candidate = set[ WSE % set.size ]

      schedule candidate for all available dates in range

      redo ROTOR with unscheduled days

1: Preference is a number. The lower the preference number, the more
likely someone is to be scheduled. Thing of preference:1 as being
"first string". For most rotors, the usual plumbers will all have
preference 1, and I (rjbs) might have preference 3. For escalation,
a full-time escalation handler would have preference 1, and most
everyone else preference 2.

2: I bet we'll want to adjust how rotor fatigue works, but right now
your level of rotor fatigue is the number of days you were on
rotation in the last 90 days. This builds up every time you're on
duty, and fades off over time.

3: If everybody is available all week, the set is everybody. If two
people are out one day, it's everybody but those two. If everybody
is out exactly one day, it's everybody again.

Rotor fatigue probably has two problems. First, if a rotation is done by one person for 90d and then a second person is added to rotation with the same preference, they'd be on rotation 45d before getting a break. be on rotation for 45d straight. Second, it's per-rotor, which may or may not be noticeable over time. Still, it should be easy to adjust its behavior over time, and this was easy to implement.

The next likely steps are:

  1. Replace the availability checker callback with something acting kind of or exactly like the AvailabilityChecker class in Rototron1.

  2. Treat the rototron's own schedule (persisted in SQLite or something) as canonical, with the JMAP duty roster calendar treated as a published copy.

@rjbs rjbs force-pushed the main branch 2 times, most recently from b140047 to 945aca5 Compare October 28, 2022 17:19
@rjbs rjbs force-pushed the main branch 6 times, most recently from 15d3cd9 to 3a95bf4 Compare March 10, 2023 18:29
@rjbs rjbs force-pushed the main branch 4 times, most recently from f23a1b1 to 4022b0d Compare October 26, 2023 00:12
@rjbs rjbs force-pushed the main branch 9 times, most recently from 32f29ba to 39ade23 Compare February 6, 2024 20:31
@rjbs rjbs force-pushed the rototron-bis branch 2 times, most recently from 059d588 to 5767a8f Compare February 13, 2024 00:47
@rjbs rjbs force-pushed the main branch 3 times, most recently from aabeec7 to aafd26b Compare July 4, 2024 13:50
@rjbs rjbs force-pushed the main branch 2 times, most recently from 747f6ee to 10212ce Compare July 5, 2024 14:02
rjbs added 2 commits February 6, 2025 12:05
This is not perfect, or probably even 10 years perfect, but I think it's
a big improvement, and should be pretty easy to finish delivering.

The old rototron worked like this:

    every week has a Weeks Since Epoch number, WSE

    every rotor has a list of workers

    for each rotor:
      candidate-list = all workers for rotor, deterministically sorted

      for each day in the week being scheduled:
        CANDIDATE: until scheduled or worker list exhausted:
          candidate = delete candidate-list[ WSE % candidate-list.size ]

          if candidate is unavailable or on leave
            next CANDIDATE

GOOD:  Because the WSE number is stable within a week, the first choice
for any day in the week is the same, as is the second.  That means a
week will favor being one person.  If that person is not available for 2
days, the same person is likely to be fallback.

BAD:  Not too much prevents week 1's first choice from being week 2's
second choice, meaning the odds of getting two weeks in a row, when
somebody is out for a week, are poorly defined.  (Insert link to number
theory about coprimality here.)

BAD:  Rotors are entirely unaware of each other, so nothing much
prevents someone being assigned three rotations in a week.

The new rototron (not fully implemented yet) works like this:

    every week has a Weeks Since Epoch number, WSE

    every rotor has a map of workers to fixed preference¹

    every rotor-worker has a level of rotor-fatigue²

    ROTOR: for each rotor:
      for every worker in worker map:
        if worker has any other rotor assignment in this date range:
          mark worker as one level less preferable

      for every level of preferability:
        select set of workers available on the most days³

        for each worker in this set:
          select set of workers with the lowest rotor-fatigue

          candidate = set[ WSE % set.size ]

          schedule candidate for all available dates in range

          redo ROTOR with unscheduled days

1: Preference is a number.  The lower the preference number, the more
   likely someone is to be scheduled.  Thing of preference:1 as being
   "first string".  For most rotors, the usual plumbers will all have
   preference 1, and I (rjbs) might have preference 3.  For escalation,
   a full-time escalation handler would have preference 1, and most
   everyone else preference 2.

2: I bet we'll want to adjust how rotor fatigue works, but right now
   your level of rotor fatigue is the number of days you were on
   rotation in the last 90 days.  This builds up every time you're on
   duty, and fades off over time.

3: If everybody is available all week, the set is everybody.  If two
   people are out one day, it's everybody but those two.  If everybody
   is out exactly one day, it's everybody again.

Rotor fatigue probably has two problems.  First, if a rotation is done
by one person for 90d and then a second person is added to rotation with
the same preference, they'd be on rotation 45d before getting a break.
be on rotation for 45d straight.  Second, it's per-rotor, which may or
may not be noticeable over time.  Still, it should be easy to adjust its
behavior over time, and this was easy to implement.

The next likely steps are:

1.  Replace the availability checker callback with something acting kind
    of or exactly like the AvailabilityChecker class in Rototron1.

2.  Treat the rototron's own schedule (persisted in SQLite or something)
    as canonical, with the JMAP duty roster calendar treated as a
    published copy.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant