Сделайте свою сложную планировку простым с помощью timeboard, библиотеки Python

sdelajte svoyu slozhnuyu planirovku prostym s pomoshhyu timeboard biblioteki python?v=1656665660

Максим Мамаев

ekZGGobKFGNKpwXZfb56yvwxB3P5aiZtNjGU

timeboard это библиотека Python, которая создает графики рабочих периодов и выполняет календарные вычисления по ним. Вы можете создавать стандартные календари рабочих дней, а также разные другие расписания, простые или сложные.

Вы можете найти документацию здесь.

Просмотрите хранилище GitHub здесь.

Найдите его на PyPI здесь.

История

Началось дело о численности персонала. Наша компания ввела KPI, включающую доход на одного работника, поэтому нам нужно было знать среднегодовую численность персонала каждой команды. Я уже писал скрипты на Python, потому меня это не пугало.

Чтобы получить количество работников, мне пришлось подсчитать количество рабочих дней, каждый работник провел в компании в течение года. Я подумал, что панды справятся с этим через секунду. Но оказалось, что панды не могут.

Российский деловой громоздкий календарь. Они меняют рабочие дни по субботам или воскресеньям, чтобы заполнить промежутки между праздниками и выходными. Например, вы должны прийти на работу в субботу февраля, чтобы получить компенсацию за свободный понедельник перед праздничным вторником где-то в мае.

Схема для каждого года уникальна. Календарь рабочих дней Pandas поддерживает только односторонние изменения для праздничных наблюдений. Следовательно, я мог превратить рабочий день в выходной, но не наоборот.

Потом были операторы в колл-центре, и моя тревога перекинулась в другую сторону. Они работают в изменении разной продолжительности, одна смена входит, а затем три смены. Для получения статистики колл-центра мне не нужен был календарь рабочих дней. Но мне приходилось подсчитывать количество смен конкретного оператора за определенный промежуток времени.

И, наконец, неординарная проблема. В моем местном дилерском центре Honda механики работают по альтернативному недельному графику: понедельник, вторник, суббота и воскресенье на этой неделе и со среды по пятницу на следующей неделе. Мне хотелось, чтобы меня всегда обслуживал механик, потому что тот когда-то испортил тормоза. Я хотел простой способ определить следующее изменение «моего» механика.

Эти дела имеют общую базу. Их решения будут базироваться на графике «очередных» и «нерабочих» периодов времени. Мы должны иметь возможность строить разнообразные структурированные графики, пригодные для различных бизнес-случаев. Запросы и вычисления, выполняемые по графику, должны различать периоды чередования и нерабочий.

Не удалось найти пакет Python, который предоставляет средства для построения и запроса таких расписаний. Так случилось, что у меня было немного свободного времени, чтобы написать его самостоятельно.

i7RmM4xORdvi4kS1KEjvqxeq5lZT9OI9KAjo

Концепция

timeboard это библиотека Python, которая создает графики рабочих периодов и выполняет календарные вычисления по ним. Сами эти объекты называют таймбордами.

Есть три основных шага в рассуждении о таймборде.

Вы начинаете с интервала времени, устанавливающего границы вашего календаря. Всё будет ограничено этим интервалом. Она называется системой отсчета. Каркас состоит из базовых блоков. Базовая единица — это наименьший промежуток времени, необходимый для измерения календаря. К примеру, если вы рассуждаете в сроках рабочих дней, то базовой единицей является день. В противном случае, если вы строите график многочасовых смен, то базовой единицей является один час.

sI-5OZ10OfBSQP2EOH3I738uzuJA1bXjHufU

На следующем шаге вы определяете правила разметки рамы на рабочие смены. Рабочие смены – это интересующие вас периоды времени. Они составляют ваш календарь. Это рабочие смены, которые вы хотите запланировать или подсчитать. В стандартном календаре рабочих дней рабочая смена – это день (и базовая единица тоже день, поэтому они совпадают).

В колл-центре рабочая смена – это несколькочасовой период, когда дежурит определенная смена операторов. Базовая единица, вероятно, будет составлять один час, и каждая рабочая смена включает (вероятно, разное) количество базовых единиц.

Последовательность рабочих смен, заполняющих кадр, называется шкалой времени.

Наконец, вы создаете одно или несколько расписаний. Расписание похоже на трафарет, наложенный на шкалу времени. Его предназначение – отличить очередные рабочие смены от внеслужебных.

Чтобы объявить рабочую смену во время дежурства или вне дежурства, для графика нужно что-то работать. Вот почему вы предоставляете метку для каждой рабочей смены, или, скорее, правило для их обозначения, когда кадр размечен на шкале времени. Каждое расписание определяет функцию выбора, которая проверяет метку рабочей смены и возвращает True для очередных рабочих смен и False в противном случае. Если вы не изменяете его, временная шкала сопровождается расписанием по умолчанию, селектор которого возвращает логическое значение метки.

Иногда нужно определить несколько расписаний для одной шкалы времени. К примеру, в колл-центре будет расписание для колл-центра в целом, и отдельное расписание для каждой бригады операторов. Одна и та же рабочая смена по одним графикам может быть чередованием, а по другим – свободным.

MlUNZdQm8rr9PwqqoqCm5YLfoeXz99sR6i-q

Таймборд=хронология+графики. Вернее, таймборд является сборником трудов разложения на основе конкретного шкала времени с рабочие смены построен на основе ссылки рамка.

Получив табличку времени, вы можете выполнять полезную работу: делать календарные расчеты, чтобы решать задачи, подобные описанным в прологе.

Каждое вычисление, выполненное с помощью таймборда, производится с учетом обязанностей. Вызванный метод «видит» только рабочие смены с указанной обязанностью и игнорирует другие. Чтобы выявить чередование рабочих смен, методике необходимо предоставить график. Поэтому каждое вычисление на временной табличке параметризировано обязанностью и расписанием.

По умолчанию чередование включено, а расписание – это расписание по умолчанию на табло. Например, если звонить count() без аргументов на определенном интервале таймборда вы получите количество рабочих смен в интервале, которые объявлены на дежурстве по расписанию по умолчанию. Эти параметры по умолчанию облегчают жизнь, поскольку на практике вы захотите иметь дело в основном с рабочими сменами.

API

Полная документация о временной табличке доступна на сайте Read the Docs.

Пакет можно установить с обычным pip install timeboard.

Настройте табличку времени

Самый простой способ начать — использовать предварительно настроенный календарь, поставляемый вместе с пакетом. Возьмем обычный календарь рабочих дней для США.

 >>> import timeboard.calendars.US as US >>> clnd = US.Weekly8x5()

clnd объект является временной шкалой. timeboard.Timeboard класс). Он имеет только одно расписание по умолчанию, которое выбирает будние дни как рабочие смены, а выходные, а также наблюдения за федеральными праздниками США объявляются нерабочими.

Инструменты для создания собственной хронометражной доски будут кратко рассмотрены позже после того, как мы рассмотрим, что вы можете сделать с хронометражной доской.

Играйте с рабочими сменами

Вызов экземпляра таймборда clnd() с одним моментом времени получает рабочую смену, содержащую эту точку. Как у вас есть рабочая смена, вы можете спросить ее обязанность:

Есть ли дата рабочим днем?

>>> ws = clnd('27 May 2017')>>> ws.is_on_duty()False

Действительно, была суббота.

Вы также можете заглянуть в будущее или в прошлое с текущей рабочей смены:

Когда был следующий рабочий день?

>>> ws.rollforward()Workshift(6359) of 'D' at 2017–05–30

Возвращенная рабочая смена имеет порядковый номер 6359 и представляет день 30 мая 2017, который, кстати, был вторником после выходного дня памяти.

Если бы мы завершили проект за 22 рабочих дня, начиная с 1 мая 2017 года, когда бы был наш конечный срок?

>>> clnd('01 May 2017') + 22Workshift(6361) of 'D' at 2017–06–01

Это то же, что:

>>> clnd('01 May 2017').rollforward(22)Workshift(6361) of 'D' at 2017–06–01

Играйте с интервалами

Звонок clnd() с другим набором параметров создает объект, представляющий интервал в календаре. Интервал ниже содержит все рабочие смены за май 2017 года:

>>> may2017 = clnd('May 2017', period='M')

Сколько рабочих дней было в мае?

>>> may2017.count()22

Сколько выходных?

>>> may2017.count(duty='off')9

Сколько рабочих часов?

>>> may2017.worktime()176

Сотрудник находился в штате с 03.04.2017 по 15.05.2017. Какую часть апрельской зарплаты им задолжало предприятие?

Обратите внимание, что вызов clnd() с кортежем из двух точек времени создает интервал, содержащий все рабочие смещения между этими точками включительно.

>>> time_in_company = clnd(('03 Apr 2017','15 May 2017'))>>> time_in_company.what_portion_of(clnd('Apr 2017', period='M'))1.0

Действительно, 1 и 2 апреля 2017 пришлись на выходные, поэтому, начав 3 числа, работница отписала все рабочие дни месяца.

А какая часть мая?

>>> time_in_company.what_portion_of(may2017)0.5

Сколько дней работал работник в мае?

Оператор умножения возвращает пересечение двух интервалов.

>>> (time_in_company * may2017).count()11

Сколько часов?

>>> (time_in_company * may2017).worktime()88

Работник находился в штате с 01 января 2016 по 15 июля 2017 года. Сколько лет этот человек работал в компании?

>>> clnd(('01 Jan 2016', '15 Jul 2017')).count_periods('A')1.5421686746987953
jsj2ogpaBTEwK2iz4RVJmVLuwBi4jyp1cPvW

Создайте собственную табличку времени

С целью вступления я остановлюсь на двух примерах. Если это кажется слишком крутым, найдите подробное обсуждение строительных инструментов в проектной документации.

Оператор импорта для этого раздела:

>>> import timeboard as tb

Возвращаюсь к расписанию рабочих смен в автосалоне, о котором я упоминал в прологе. Механик работает в понедельник, вторник, субботу и воскресенье на этой неделе, а также в среду, четверг и пятницу на следующей неделе; затем двухнедельный цикл повторяется. Таймборд создается по следующему коду:

>>> biweekly = tb.Organizer(marker="W",...     structure=[[1,1,0,0,0,1,1], [0,0,1,1,1,0,0]])>>> clnd = tb.Timeboard(base_unit_freq='D', ...     start="01 Oct 2017", end='31 Dec 2018', ...     layout=biweekly)

Имеет смысл сначала рассмотреть последнее утверждение. Он создает часовую доску с названием clnd. Первые три параметра определяют кадр как последовательность дней (‘D‘) с 01 октября 2017 года по 31 декабря 2018 года layout Параметр указывает, как организовать кадр во временной шкале рабочих смен. Эта работа поручена Organizer назван biweekly.

Первое утверждение создает это Organizer принимающий два параметра: marker и structure. Мы используем amarker поставить отметки на раме. Отметки представляют собой своеобразные вехи, которые делят кадр на подрамники, или «пролеты». В примере marker=’W’ ставит метку в начале каждой календарной недели. Таким образом, каждый промежуток означает неделю.

The structure Параметр указывает, как создавать рабочие изменения внутри каждого диапазона. Первый элемент из structureсписок [1,1,0,0,0,1,1], применяется к первому промежутку (т.е. к первой неделе нашего календаря). Каждая базовая единица (т.е. каждый день) в пределах промежутка становится рабочей сменой. Рабочие смены получают метки из списка по порядку.

Второй элемент structureсписок [0,0,1,1,1,0,0], аналогично применимому ко второму пролету (вторая неделя). После этого, поскольку мы не получили больше элементов, a structure воспроизводится циклами. Следовательно, третья неделя обслуживается первым элементом structureчетвертую неделю за второй и так далее.

В результате наша шкала времени становится последовательностью дней, обозначенных номером. 1 при дежурстве механика и с номером 0 когда он или она не есть. Мы не указывали никакого расписания, потому что расписание, построенное по умолчанию, нас устраивает. Расписание по умолчанию учитывает логическое значение метки, следовательно 1 переводится как «дежурный», а ноль – как «вне чередования».

С этой шкалой времени мы можем выполнять любые типы расчетов, которые мы делали ранее с помощью бизнес-календаря. Например, если лицо было принято на работу по этому графику с 4 ноября 2017, а зарплата выплачивается ежемесячно, какую часть зарплаты за ноябрь заработал работник?

>>> time_in_company = clnd(('4 Nov 2017', None))>>> nov2017 = clnd('Nov 2017', period='M')>>> time_in_company.what_portion_of(nov2017)0.8125

Во втором примере мы построим табличку времени для колл-центра. Колл-центр работает круглосуточно в переменную продолжительность: с 08:00 до 18:00 (10 часов), с 18:00 до 02:00 (8 часов), с 02:00 до 08:00 (6 часов). ). График работы оператора состоит из одной очередной смены и трех свободных смен. Поэтому требуются четыре бригады операторов. Они обозначаются буквами «A», «B», «C» и «D».

>>> day_parts = tb.Marker(each="D", ...     at=[{'hours':2}, {'hours':8}, {'hours':18}])>>> shifts = tb.Organizer(marker=day_parts, ...     structure=['A', 'B', 'C', 'D'])>>> clnd = tb.Timeboard(base_unit_freq='H', ...     start="01 Jan 2009 02:00", end='01 Jan 2019 01:59',...     layout=shifts)>>> clnd.add_schedule(name="team_A", ...    selector=lambda label: label=='A')

Есть четыре ключевых отличия от дилерского дела. Мы рассмотрим их поочередно.

Во-первых, базовая единица фрейма теперь составляет один час (base_unit_freq='H') вместо однодневного периода дилерского календаря.

Во-вторых, значение marker Параметр Организатора теперь является сложным объектом вместо единой частоты, как это было ранее. Этот объект является экземпляром Marker класс. Используется для определения правил размещения меток на рамке, когда простого разделения рамки на единые календарные единицы недостаточно. Подпись Маркера наверху почти читабельная — написано: поставить отметку каждый день (‘D’) в 02:00, 08:00 и 18:00.

В-третьих, значение structure теперь проще: это одноуровневый список меток команд. Когда элемент в structure это не итерация меток, а только одна метка, ее применение к диапазону создает один рабочий смещение, буквально охватывающий диапазон.

В нашем примере самый первый промежуток времени включает шесть одночасовых базовых единиц, начиная с 2, 3, 4…7 часов утра 1 января 2009 года. Все эти базовые единицы объединены в одно рабочее изменение с отметкой «А». . Второй промежуток состоит из десяти часовых базовых единиц, начиная с 8, 9, 10…17 часов. Эти базовые единицы объединены в одно рабочее изменение с отметкой «B» и так далее. Когда все метки заняты, структура воспроизводится, поэтому пятый промежуток (08:00:00–17:59:59 1 января 2009 г.) становится рабочей сменой с меткой «A».

Суммируя, если элемент structure это список меток, каждая базовая единица диапазона становится рабочим изменением и получает метку из списка. Если элемент structure является одной меткой, все базовые единицы диапазона объединяются, чтобы образовать одно рабочее изменение, получающее эту метку.

И, наконец, мы явно создали расписание для команды А. Расписание по умолчанию не соответствует нашей цели, поскольку оно возвращает «всегда на дежурстве». Это справедливо для колл-центра в целом, но не для отдельной команды. Для нового расписания мы предоставляем название и функцию выбора, которая возвращает True для всех рабочих смен, обозначенных буквой «A». Для практического использования вы также хотите создать расписания для других команд.

С этой табличкой времени так же хорошо работать, как и с любой другой. Однако в этот раз нам придется явно указать расписание, которое мы хотим использовать.

>>> schedule_A = clnd.schedules['team_A']

Сколько смен сидели операторы бригады А в ноябре 2017 года?

>>> nov2017 = clnd('Nov 2017', period='M', schedule=schedule_A)>>> nov2017.count()22

А сколько всего было часов?

>>> nov2017.worktime()176

Лицо принято на должность оператора в бригаду А с 04.11.2017. Заработная плата выплачивается ежемесячно. Какую часть заработной платы за ноябрь заработал работник?

>>> time_in_company = clnd(('4 Nov 2017',None), schedule=schedule_A)>>> time_in_company.what_portion_of(nov2017)0.9090909090909091

Больше случаев использования

Вы можете найти больше случаев использования (почти из реальной жизни) в блокноте Jupyter, который является частью проектной документации.

Пожалуйста, не стесняйтесь использовать timeboard и не стесняйтесь оставлять отзывы или открытые проблемы на GitHub.

Добавить комментарий

Ваш адрес email не будет опубликован.