Архитектура для моделей Django для реализации функциональности расписания (планирования)

Вопрос:Я создаю приложение Django, предназначенное для поддержания расписания (расписание) для 10K + лиц (и многое другое в будущем). В основном, проблема заключается в следующем: у каждого человека есть отдельное расписание со свободными слотами на следующий год. Он дискретный с шагом 15 минут. Мне нужно разработать архитектуру моделей (которая будет подразумевать структуру базы данных внизу), чтобы

Вопрос:

Я создаю приложение Django, предназначенное для поддержания расписания (расписание) для 10K + лиц (и многое другое в будущем). В основном, проблема заключается в следующем: у каждого человека есть отдельное расписание со свободными слотами на следующий год. Он дискретный с шагом 15 минут. Мне нужно разработать архитектуру моделей (которая будет подразумевать структуру базы данных внизу), чтобы сделать следующее:

  • Запросить все свободные временные интервалы для данного человека.
  • Запросить всех лиц, которые свободны в течение определенного времени.

Например, у меня есть Джон, который свободен от 8 AM-14PM 14 ноября и Сара, которая свободна с 10 утра до 11 утра 14 ноября. Если я запрошу у Джона свободные временные интервалы, я хочу получить “8 AM-14PM 14 ноября”. Если я запрошу “свободных лиц с 8 утра до 11 утра”, я получаю Джона, поскольку Сара не свободна до 10 утра. Если я запрошу “свободных лиц с 10 утра до 11 утра”, я хочу получить как Джона, так и Сару.
Я думал об этой проблеме, и мои идеи ниже.

Решение №1: Мы создаем модель FreeTimeSlot, которая будет хранить информацию о каждом 15-минутном интервале времени и строить по отношению к нему от человека.

class Person(models.Model): name = models.CharField(max_length=32, null=False, blank=False) free_slots = models.ManyToManyField(FreeTimeSlot, related_name=’tutor_set’, null=True, blank=True, through=’PersonSlot’) class TimeSlot(models.Model): time = models.DateTimeField(db_index=True) #perhaps other field type class PersonSlot(models.Model): person = models.ForeignKey(Person) timeslot = models.ForeignKey(Slot) class Meta: db_table = ‘person_free_slots’ unique_together = ((‘timeslot’, ‘person’))

Мы создаем модели TimeSlot 365 * 24 * 4 для каждых 15-минутного интервала в предстоящем году, и если человек укажет в своем расписании свободное время, мы добавим отношение к этому TimeSlot.
С такой архитектурой получение бесплатных временных интервалов для человека так же просто, как и управление менеджером: person.free_time_slots
Получение всех людей бесплатно в определенное время (например, 10-10: 45) также довольно легко, сглаживание, например:

timeslots = TimeSlot.objects.filter(time__in=[’10:00′, ’10:15′, ’10:30′]) PersonSlot.objects.filter(timeslot__in=timeslots).values(‘person’)

Решение №2:
Мы избегаем создания модели для каждого временного интервала, но сохраняем дату в самой модели PersonTime:

class Person(models.Model): name = models.CharField(max_length=32, null=False, blank=False) class TimeSlot(models.Model): person = models.ForeignKey(Person, related_name=’slots’) time_start = models.DateTimeField(db_index=True) time_end = models.DateTimeField(db_index=True)

Получение списка бесплатных временных интервалов также легко (person.slots). Получение всех свободных лиц в определенное время (например, 10-10: 45) было бы следующим:

TimeSlot.objects.filter(time_start__gte=»10:00″, time_end__lte=»10:45″).values(‘person’)

Это решение не будет работать с пересекающимися интервалами, и я не уверен, что запрос на индексированное время для интервала (с использованием сопоставлений gte и lte в одном и том же поле) будет работать и будет работать быстро. Я использую Postgres, если это может иметь значение. Я также писал временные запросы в псевдокоде, чтобы сделать код более простым.

Итак, мой вопрос заключается в следующем: как хорошие разработчики django могли бы реализовать эту функцию, чтобы обеспечить скорость для обоих запросов на больших данных? Я был бы признателен за советы по возможным оговоркам/преимуществам моих текущих решений или новых идей.

Ответ №1

Разделим этот вопрос на две части.

Содержание

  1. Часть 1 – Кодирование данных
  2. Часть 2 – Запрос
  3. Сохранение процесса обработки данных
  4. Pros
  5. Против

Часть 1 – Кодирование данных

Рассмотрим кодирование данных, относящихся к временным интервалам. Если вам нужна точность в 15 минут, у вас есть 96 слотов (4 слота в 1 час * 24 часа в день) с продолжительностью 15 минут в любой день. Каждый слот может иметь одно из двух возможных состояний: 1 – слот свободен, 0 – слот занят (или наоборот, если хотите). Таким образом, вы можете представлять ежедневное расписание со строкой 0 и 1 s. Например, строка (пробелы, добавленные только для удобства чтения) 0000 0000 0000 0000 0000 0000 0000 0000 0000 1110 0000 … представляет собой занятый временной интервал между 00:00 и 09:00 (никто не работает ночью), а затем свободный промежуток времени между 9:00 и 9: 45AM (три 1 подряд), за которым следует загруженный временной интервал, начинающийся с 9:45 утра.

Итак, вы можете написать свои модели следующим образом:

class Person(models.Model): name = models.CharField(max_length=32) class DailySchedule(models.Model): person = models.ForeignKey(Person, related_name=’day_schedule’) date = models.DateField() schedule = models.CharField(max_length=96)

Часть 2 – Запрос

Итак, мы закодировали информацию о доступных слотах времени ожидания/занятости, но как мы можем извлечь ее из базы данных? К счастью, Django имеет возможность поиска полей regex! И, к счастью, он поддерживается в Django 1.4!!

Таким образом, чтобы найти того, кто доступен в течение определенного временного интервала, вы можете использовать DailySchedule.objects.filter(date=date, schedule__regex=r'<expression>’). Поскольку не очевидно, какое выражение использовать для извлечения разных таймфреймов, нам понадобится вспомогательная функция:

def time_slot_to_regex(start_time, end_time): # times should be in HH:MM format start_hour, start_minutes = start_time.split(‘:’) end_hour, end_minutes = end_time.split(‘:’) slots_before_needed_time = (int(start_hour)*4 + int(start_minutes)/15) # compute how many hours are between given times and find out nr of slots hour_duration_slots = (int(end_hour) — int(start_hour)) * 4 # 4 slots in each hour # adjust nr of slots according to minutes in provided times. # e.g. 9:30 to 10:45 — we have 10-9=1 hour, which is 4 time slots, # but we need to subtract 2 time slots, because we don’t have 9:00 to 10:00, # but 9:30 to 10:00 so we subtract 30/15=2 timeslots and add what is left # from the incomplete hour of 10:45 time, which is 45/15 minutes = 3 slots minute_duration_slots = int(end_minutes)/15 — int(start_minutes)/15 total_duration = hour_duration_slots + minute_duration_slots regular_expression = r’^[01]{%d}1{%d}’ % (slots_before_needed_time, total_duration) return regular_expression

Определите, как работает эта функция

Предположим, мы хотим узнать, кто доступен между 9:15 и 9:45. Назовем slots_expression = time_slot_to_regex(‘9:15’, ‘9:45’), который вычисляет:

  • slots_before_needed_time = 37, который мы получили, умножив 9 на 4 + 15/15. Это количество слотов, которые нам не нужны, которые войдут в первую часть нашей строки regular_expression – ‘^[01]{37}’
  • hour_duration_slots = 0, потому что час в обоих значениях времени одинаковый
  • minute_duration_slots = 2, который мы получили путем вычитания 15/15 из 45/15
  • previous 2 добавляет вместе 2 слота, которые нам нужно установить в 1 в нашем regular_expression, получив ‘^[01]{37}1{2}’

Теперь мы можем предоставить это регулярное выражение нашему фильтру, получив DailySchedule.objects.filter(schedule__regex=slots_expression) и voila!, результат получим.

Сохранение процесса обработки данных

Я описал принцип кодирования данных, но не упомянул процесс его кодирования. Это можно сделать легко, используя другую функцию поддержки, которая берет строку существующих занятых/доступных слотов и start_date и end_date, для которых необходимо обновить существующее расписание. Если вам также нужен этот метод, сообщите мне.

Pros

  • нет отношения M2M, что приводит к более быстрым запросам
  • может искать несколько свободных временных интервалов в течение дня, используя другое регулярное выражение (например, ^[01]{36}1{4}[01]{24}1{4} будет искать людей, которые доступны с 9AM до 10AM и с 4PM до 5PM
  • относительно простая реализация
  • в качестве побочного эффекта у вас будет более легкий доступ, чтобы узнать занятые временные интервалы, поскольку у вас будет необходимая информация в базе данных, и для этого не потребуется выполнять вычисления

Против

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