Система уведомлений пользователей¶
1. Общее описание системы¶
Уведомления пользователей организованы посредствам стандартной модели Django ORM, описание модели распололожено в uzantoj.models.UzantojSciigoj. Для унификации способов создания и получения доступа к уведомлениям создан специальный функционал, который позволяет создавать мультиязычные уведомления на стороне сервера.
2. Создание нового уведомления о событии в системе¶
Для создания уведомления необзодимо использовать функцию uzantoj.sciigoj.sendu_sciigon. Импорт функции выглядит так:
from uzantoj.sciigoj import sendu_sciigon
Функция имеет следующие параметры:
sendu_sciigon(teksto, pluralo=None, teksto_parametroj=None, to=None, objektoj=None)
teksto- непосредственно текст уведомления (по договоренности на эсперанто), который должен быть обёрнут в стандартную функцию локализацииgettext_lazy.pluralo- текст во множественном числе, обёрнутый вngettext_lazyteksto_parametroj- список (list, tuple, set) с параметрами подстановки в текст уведомления. Если один из параметров списка является словарем (dict), то это должно быть описание реального объекта Django ORM в виде{'obj': object, 'field': field}, гдеobject- реальный объект Django ORM,field- текстовое название поля модели. Например нужно передать имя пользователя из моделиmain.models.Uzanto:
uzanto = Uzanto.objects.get(id=1) # Получаили объект
# Тогда описание параметра для извлечения значения поля будет выглядеть так:
{
'obj': uzanto,
'field': 'unua_nomo'
}
Таким образом при выводе уведомления обеспечавается достоверная на текущий момент времени информация при подстановке полей в текст уведомления. Ведь пользователь может изменит своё имя, а тогда при выводе уведомления и его текст в части имени пользователя тоже будет изменён.
to- список (list, tuple, set) ID пользователей для рассылки. Каждый элемент списка должен иметь тип intobjektoj- список (list, tuple, set) объектов Django ORM, функция самостоятельно сериализует их для хранения в модели уведомлений. Передавать объекты в уведомлении полезно, т.к. на стороне фронтэнда нужно как-то делать ссылки на объекты уведомлений (пользователей, записи стены, картинки и т.п.).
Следует отметить, что рассылка уведомлений пользователям осуществляется итерациями по 100 пользователей за раз (функции можно передать и 10 000 ID, она авоматически будет разбивать их по 100). Однако при большом количестве пользователей задержки выполнения кода в текущем потоке всё же заметны и существены. Для устаренения задержек исполнения пользовательских запросов рекомендуется выносить действия рассылки уведомлений во внешний поток сервиса Celery. Это можно сделать так:
- Убедиться, что в вашем приложении уже существует модуль
tasks.py, если его нет, то нужно создать и объявить о нем в модулеapps.pyваше приложения таким образом (на примере приложенияmuroj):
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class MuroConfig(AppConfig):
# Название модуля
name = 'muroj'
# Визуальное название, будет отображаться в админке, потому делаем переводимым
verbose_name = _('Muroj')
# Метод срабатывает при регистрации приложения в системе django
def ready(self):
# Импортом сообщаем django, что надо проанализировать кодв в модуле tasks
# При этом происходит регистрация функций заданий celery
import muroj.tasks
- Описать функцию-задание celery:
# Импортируем декоратор для объявления задания
from celery import shared_task
# описываем функцию задание
[@shared_task](profile/shared_task)
def test_task(arg1, arg2):
print '%s %s' % (arg1, arg2)
print True
Параметры функции задаются по необходимости и на усмотрение разработчика. Однако, следует помнить о том, что нельзя в качестве параметра передавать какие-либо объекты, кроме объекто встроенных классов Python, т.е. нельзя передавать объекты Django ORM, вместо этого передавайте их pk (primary key) или параметры выборки для их нахождения. Соответственно сами объекты перед использованием в функции-задании должны быть найдены чрезе запросы Django QuerySet.
В качестве результата функция может возвращать любые данные, которые могут быть сериализованы в json штатными средствами python, можно вернуть просто True, или количество отправленных уведомлений. Результат будет сохранеён в сервисе Celery и может быть потом проанализирован.
- Внутри описанной функции-задания следует уже вызывать sendu_sciigon
- Описанную функцию задание в тексте программы необходимо вызывать следующим образом:
# Допустим, что это часть кода мутации
def mutate(root, info, **kwargs):
...
test_task.delay('Hello', 'World')
...
Если вызвать функцию test_task без delay(), то выполнение задание будет проводится в текущем потоке и могут возникнуть задержки в выполнении пользовательского запроса. Вызов delay() обеспечивает создание задания в сервисе Celery и его выполнение в отдельном потоке.
3. Описание объектов уведомлений для корректной работы API¶
Т.к. каждый тип уведомления может содержать совершенно разные объекты Django ORM, то возникают сложности с отдачей этих объектов через GraphQL API. Для решения подобной проблемы в GraphQL существует тип graphene.Union, который позволяет в результате одного запроса возвращать совершенно разные типы данных.
Если вы сделали собственный тип уведомления (например, публикация комментария в конференции), и вам необходимо выдать с уведомлением сам объект комментария (для того, чтобы на стороне фронтэнда смогли сформировать ссылку на него или даже взять кусочек его тектса), то надо описать тип передаваемого объекта в модуле uzantoj.api.sciigoj в классе SciigajObjektoj:
...
class SciigajObjektoj(Union):
class Meta:
types = (UzantoNode, MurojUzantoEnskriboNode, MuroEnskriboNode, MurojUzantoEnskriboKomentoNode,
MuroEnskriboKomentoNode)
...
Как видно из текста программы, в метаклассе есть параметр types, который представляет собой кортеж (tuple), каждый элемент которого - это узел GraphQL API, описывающий ту или иную модель Django ORM (подробнее об узлах можно посмотреть здесь). Соответственно, необходимо просто добавить в данный кортеж свой класс-узел (предварительно его импортировав).
Этого достаточно, чтобы пользовательский запрос корректно обработался и вернул необходимые данные пользователю.
4. Виды уведомлений¶
4.1 Модель Сообщества (muroj)¶
Sur la muro de la komunumo publikigis novan eniron (На стене сообщества опубликована новая запись) afiŝis novan eniron sur sian muron (разместил новую запись на своей стене)
4.2 Модель Конференции (konferencoj)¶
- Пришло новое сообщение в теме конференции <названиетемыконференции>
teksto = „Новый комментарий в теме конференции %(nomo)s“ parametroj = {„nomo“: {„obj“: temo, „field“: „nomo“}} objektoj=(temo,)
4.3 Модель Наград (premioj)¶
- Получена новая награда за <условия награждения>
teksto = „Получена новая награда за %(nomo)s“ parametroj = {„nomo“: {„obj“: kondicho, „field“: „nomo“}} objektoj=(premiado,kondicho,)
4.4 Модель Тамагочи (tamagocxi)¶
- Получен новый Тамагочи за <условия награждения>
teksto = „Получен новый Тамагочи за %(nomo)s“ parametroj = {„nomo“: {„obj“: kondicho, „field“: „nomo“}} objektoj=(premiado,kondicho,)
4.5 Модель Мессенджер (mesagxilo)¶
- Пришло новое сообщение в чате <название_чата>
teksto = „Nova mesaĝo en konversacio %(nomo)s“ parametroj = {„nomo“: {„obj“: babilejo, „field“: „nomo“}} objektoj=(babilejo,)