Подсчет типов событий в день для разных часовых поясов с использованием Django ORM - PullRequest
0 голосов
/ 05 апреля 2020

У нас есть таблица, которая содержит несколько событий и когда они были добавлены. Часовой пояс по умолчанию, используемый для хранения событий: UT C. Например:

class Events:
    type = models.CharField(max_length=45, null=False)
    date_added = models.DateTimeField(auto_now_add=True)

Теперь мы хотим получить количество различных типов событий в день между двумя датами - start_date и end_date. Например: for start_date = "2020-03-1" и end_date = "2020-03-31", вывод должен быть -

[{
    "date" : "2020-03-1",
    "event1" : 200,
    "event2" : 606,
    "event3" : 595
},
{
    "date" : "2020-03-2",
    "event1" : 357,
    "event2" : 71,
    "event3" : 634
},
{
    "date" : "2020-03-3",
    "event1" : 106,
    "event2" : 943,
    "event3" : 315
},
{
    "date" : "2020-03-4",
    "event1" : 187,
    "event2" : 912,
    "event3" : 743
},
.
.
.
.
{
    "date" : "2020-03-31",
    "event1" : 879,
    "event2" : 292,
    "event3" : 438
}]

Поскольку пользователи находятся в разных часовых поясах (Америка, Европа, Азия, et c), мы хотим преобразовать часовой пояс в соответствии с пользователем перед подсчетом событий. Подсчет в UT C будет иметь неправильный счет в день в часовом поясе пользователя. Например, событие, созданное 3 марта в 1:30 по IST, будет показано 2 марта, в 20:00 в UT C и подсчитано соответственно.

Это будет очень дорого, если мы сделаем это с использованием для l oop. Поэтому мы хотим сделать это на уровне БД, используя Django ORM. Если невозможно полностью зависеть от Django ORM, мы хотим сделать его максимально эффективным.

Лучший запрос, который мы могли бы сформулировать, был:

 Events.objects.filter(
    pk = user_pk, date__range = (
        (end_date - time_delta).strftime("%Y-%m-%d"), 
        end_date.strftime("%Y-%m-%d")
        )
    ).extra({
        "date_added" : "date(date_added)"
    }).values(
        "date_added", 
        "type"
    ).annotate(
        models.Count("type")
    ) 

Где мы получаем результаты, такие как:

<QuerySet [{'date_added': datetime.date(2020, 3, 6), 'type': 'event1', 'type__count': 30}, 
{'date_added': datetime.date(2020, 3, 6), 'type': 'event2', 'type__count': 189}, 
{'date_added': datetime.date(2020, 3, 6), 'type': 'event3', 'type__count': 1}, 
{'date_added': datetime.date(2020, 3, 6), 'type': 'event4', 'type__count': 3}, 
{'date_added': datetime.date(2020, 3, 9), 'type': 'event2', 'type__count': 57}, 
{'date_added': datetime.date(2020, 3, 9), 'type': 'event1', 'type__count': 23}, 
{'date_added': datetime.date(2020, 3, 9), 'type': 'event4', 'type__count': 1}, 
{'date_added': datetime.date(2020, 3, 10), 'type': 'event1', 'type__count': 5}, 
{'date_added': datetime.date(2020, 3, 10), 'type': 'event2', 'type__count': 21}, 
{'date_added': datetime.date(2020, 3, 11), 'type': 'event2', 'type__count': 9}, 
{'date_added': datetime.date(2020, 3, 11), 'type': 'event1', 'type__count': 15}, 
{'date_added': datetime.date(2020, 3, 12), 'type': 'event2', 'type__count': 49}, 
{'date_added': datetime.date(2020, 3, 13), 'type': 'event2', 'type__count': 8}, 
{'date_added': datetime.date(2020, 3, 13), 'type': 'event1', 'type__count': 3}, 
{'date_added': datetime.date(2020, 3, 17), 'type': 'event1', 'type__count': 16}, 
{'date_added': datetime.date(2020, 3, 17), 'type': 'event2', 'type__count': 26}, 
{'date_added': datetime.date(2020, 3, 17), 'type': 'event4', 'type__count': 1}, 
{'date_added': datetime.date(2020, 3, 17), 'type': 'event3', 'type__count': 1}, 
{'date_added': datetime.date(2020, 3, 18), 'type': 'event2', 'type__count': 64}, 
{'date_added': datetime.date(2020, 3, 18), 'type': 'event1', 'type__count': 11}, 
'...(remaining elements truncated)...']>

Для этого все равно потребуется для l oop, чтобы все события с одинаковой датой были добавлены в один диктовку, но проблема с часовым поясом все еще сохраняется.

Как это решить?

1 Ответ

0 голосов
/ 07 апреля 2020

Мы смогли решить это наконец. Мы по-прежнему используем для l oop для получения данных в требуемом формате, но мы смогли перенести тяжелую работу на БД. Сначала несколько вещей:

  1. Я использую MySQL DB. (Проверьте точку ниже, чтобы понять, почему это важно.)

  2. Если вы хотите преобразовать часовой пояс, используя разницу во времени напрямую («+05: 30» для IST или «+00: 00 "для UT C), вам не нужно будет запускать какие-либо команды.

  3. Однако вам потребуется запустить команду для поддержки часовых поясов по имени в MySQL DB. Например: если вы собираетесь использовать именованные часовые пояса («ASIA / KOLKATA» или «UT C»), вам нужно будет выполнить одну команду:
    • mysql_tzinfo_to_ sql / usr / share / zoneinfo | mysql -D mysql -u root -p
  4. Эта команда для MySQL БД, работающей в Ubuntu. Существуют аналогичные команды
    для других БД, таких как Postgresql и платформ, таких как Windows.

Вот запрос Django ORM:

events_list = Events.objects.all().extra(
        {
            "date_added" : "date(CONVERT_TZ(date_added, 'UTC', 'America/Chicago'))"
        }
    ).values(
        "date_added",
        "type"
    ).annotate(
        models.Count(
            "type"
        )
    )

Это будет укажите данные в формате:

<QuerySet [{'date_added': datetime.date(2020, 3, 6), 'type': 'event1', 'type__count': 31}, 
{'date_added': datetime.date(2020, 3, 6), 'type': 'event2', 'type__count': 189}, 
{'date_added': datetime.date(2020, 3, 6), 'type': 'event3', 'type__count': 1}, 
{'date_added': datetime.date(2020, 3, 6), 'type': 'event4', 'type__count': 3}, 
{'date_added': datetime.date(2020, 3, 9), 'type': 'event2', 'type__count': 58}, 
{'date_added': datetime.date(2020, 3, 9), 'type': 'event1', 'type__count': 21}, 
{'date_added': datetime.date(2020, 3, 9), 'type': 'event4', 'type__count': 1}, 
{'date_added': datetime.date(2020, 3, 10), 'type': 'event1', 'type__count': 1}, 
{'date_added': datetime.date(2020, 3, 10), 'type': 'event2', 'type__count': 23}, 
{'date_added': datetime.date(2020, 3, 11), 'type': 'event2', 'type__count': 10}, 
{'date_added': datetime.date(2020, 3, 11), 'type': 'event1', 'type__count': 16}, 
{'date_added': datetime.date(2020, 3, 12), 'type': 'event2', 'type__count': 50}, 
{'date_added': datetime.date(2020, 3, 13), 'type': 'event2', 'type__count': 10}, 
{'date_added': datetime.date(2020, 3, 13), 'type': 'event1', 'type__count': 1}, 
{'date_added': datetime.date(2020, 3, 17), 'type': 'event1', 'type__count': 19}, 
{'date_added': datetime.date(2020, 3, 17), 'type': 'event2', 'type__count': 27}, 
{'date_added': datetime.date(2020, 3, 17), 'type': 'event4', 'type__count': 3}, 
{'date_added': datetime.date(2020, 3, 17), 'type': 'event3', 'type__count': 1}, 
{'date_added': datetime.date(2020, 3, 18), 'type': 'event2', 'type__count': 61}, 
{'date_added': datetime.date(2020, 3, 18), 'type': 'event1', 'type__count': 13}, 
'...(remaining elements truncated)...']>

Здесь номер определенного события подсчитывается после преобразования часовых поясов. Теперь, когда у нас есть счетчики для события после преобразования часового пояса, остается только получить эти данные в требуемом формате, что можно легко сделать с помощью для l oop.

PS:

  1. Если у вас есть какие-либо условия для запроса, вы можете использовать filter () вместо all. Например:

    from django.utils import timezone
    Events.objects.filter(
        type__in = ["event1", "event2"], 
        date__gt = (
            timezone.now() - timezone.timedelta(days =  30)
        )
    ).extra(
        {
            "date_added" : "date(CONVERT_TZ(date_added, 'UTC', 'ASIA/KOLKATA'))"
        }
    ).values(
        "date_added",
        "type"
    ).annotate(
        models.Count(
            "type"
        )
    ) 
    

    Это даст данные за последние 30 дней для типов событий 1 и 2.

  2. Вы можете использовать "+00: 00" для UT C и "+05: 30" для АЗИИ / КОЛКАТЫ. Например: "date_added": "date (CONVERT_TZ (date_added, '+00: 00', '+05: 30'))"

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