Saltar al contenido principal

wiki-Documentación

Ver en Git


Documentación

Notification Center

Intro

El Notification Center (NC) busca centralizar comunicaciones al usuario.

Se basa en eventos que se comunican por diversos canales. Cada evento tiene un suceso en la plataforma que lo dispara, datos asociados y una lógica que determina a quienes notificar y cómo.

Los canales de notificación o Notificators implementados hasta ahora son 2:

  1. Server Sent Events (SSE)
  2. Inbox

Cada evento tiene su event_name y su event_payload. El cliente debe implementar una lógica para cada evento, ya que el payload y procesamiento de los distintos eventos es distinto.

Por ejemplo, un evento de lote compartido incluirá un field_id para indicar el lote y el cliente deberá recargar los lotes y mostrárselo al usuario. Por otro lado un evento de nueva imagen deberá indicar el field_id, layer_name y layer_date y el cliente deberá recargar las imágenes de ese lote y mostrar esa imagen en el selector de fechas.

Stackable events

Hay eventos que por su frecuencia de ocurrencia, se deben apilar o agrupar. Por ejemplo, imágenes nuevas. Si un usuario tiene 50 lotes cercanos, cuando el satélite pase por estos, se dispararán 50 eventos simultáneos, inundando los canales de notificacioń. Entonces se busca apilarlos para enviar una única notificación.

Cada evento tiene su lógica para apilar. Por ejemplo, apilar todo lo que ocurra el mismo día, o todo lo que ocurra el mismo día para un determinado lote.

Esto además cambia el contenido de las notificaciones ya que en el ejemplo de las imágenes, se enviará el payload de los 50 eventos y el mensaje a mostrar en vez de ser "Tienes una nueva imagen" será "Hay 50 nuevas imágenes".

Expiración

Cada tipo de evento tiene definido un intervalo de expiración. Cada evento tiene una fecha de expiración. Cuando se produce un evento, se registra como fecha de expiración el momento de creación + el intervalo de expiración. Cuando se obtenga el listado de eventos, sólo se obtendrán aquellos que no estén expirados en el momento de la consulta.

Inbox

Es el listado de eventos de un usuario.

Cada item del Inbox representa un evento o varios eventos apilados. Cada item tiene:

  • event_name str: nombre del evento.
  • message str: mensaje ya traducido para mostrar al usuario.
  • payload object: objeto con el payload del item. Cada evento formatea distinto su payload, por lo que hay que ver la especificación de cada uno para implementarlo.
  • timestamp str: fecha del último evento en formato YYYY-MM-DD"T"HH24:MI:ssZ.
  • dissmisible bool: indica si se puede eliminar el item.
  • pinned bool: indica que el item deberá ser destacado en el inbox. Los items pinneados se deberán mostrar primeros en la lista.
  • item_id str: uuid del item.

SSE

De manera asyncrona se suscribe a un usuario a un topico de redis. Cada topico recibe el nombre de canal y cada evento que utilice el notificador por SSE debe especificar un canal. La idea es que cada canal agrupe eventos asociados a un mismo tema, y los clientes se suscriban solo a los canales que les interesen.

Funcionamiento del Código

El NC consiste de una API HTTP y un worker de celery.

Los eventos que se disparan desde los distintos servicios los recibe el worker con la funcion source.service.events.event_trigger.

El evento se crea a partir de la informacion que recibe y ejecuta su logica. Ahi define a quien se debe notificar (EventTarget), el contenido del evento (EventPayload) y los canales de notificacion. En el caso del Inbox, tambien define si es un evento agrupable y su condicion de agrupamiento.

La API HTTP tiene:

  • un endpoint de event-stream para el notificador SSE README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations deja a un usuario escuchando multiples topicos de redis. Los eventos que invocan al notificador del SSE publican mensajes en estos topicos.
  • endpoints asociados al inbox README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations consulta en la BD los items del inbox y los datos del evento.

Cada evento consiste en:

  • un event_name unico que sirve de codigo para identificar el evento.
  • un schema de creacion que define los datos que debe enviar cada servicio, necesarios para crear el evento. README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations Los datos del schema son atributos de la instancia del evento (se definen en el init). Notar que esto no es el payload
  • una clase asociada que herede de EventPayload, que defina el payload del evento. README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations Si el evento se guarda en la BD, su payload se guarda. Este debe incluir todos los datos necesarios para rearmar la notificacion. README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations Ademas, el EventPayload define como se formatea el payload que se entrega al cliente, en caso que no se quieran entregar todos los datos (_format_single_payload_message). README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations Tambien puede definir deep o soft links README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations Si el evento es agrupable, se debe definir una clase asociada que herede de EventStackedPayload para formatear los payload de los eventos de cara al cliente (_format_stacked_payload_message).
  • una clase asociada que herede de EventMessage, que defina los mensajes a mostrar al usuario. README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations el mensaje que se define es un template, que se traducira. README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations el template se formateara con el contenido del EventPayload README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations Si el evento es agrupable, se debe definir tambien un mensaje para el EventStackedPayload
  • una fecha de expiracion expires_at. Cada evento tienen un intervalo de expiracion definido en la BD, y por defecto se expirara pasado ese tiempo desde su creacion.
  • un metodo load README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations toma los datos del evento y realiza lo necesario para definir los targets y el payload. README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations opcionalmente, guarda el evento en la BD (necesario si se implementa el InboxNotificator) README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations define los notificadores: README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations En caso del SSE, solo necesita indicar el canal README.md celery-health.sh core docker docker-compose.yml docs features model.env requirements.dev.txt requirements.txt run_api.py run_celery.sh source test.html translations En caso del Inbox, configura si es agrupable y su condicion, si es borrable y su duracion.

Nuevo evento

Para crear un evento:

  1. Crear la definición del evento en source.events.models.NEW_EVENT.py
  2. Crear el payload asociado, heredando de EventPayload.
  3. Definir el __init__ con los **kwargs que forman el payload
  4. Definir el _format_single_payload_message, si aplica, para entregar al cliente. Por defecto, entrega todos los atributos.
  5. Si aplica, crear el payload apilado asociado, heredando de StackedEventPayload.
  6. Definir el _format_stacked_payload_message, si aplica, para entregar al cliente, que agrupe los payloads de la manera deseada. Por defecto entrega el primer EventPayload._format_single_payload_message del array si sólo hay uno, o un diccionario con la key payload y un array de EventPayload._format_single_payload_message por cada payload del array.
  7. Crear el mensaje asociado, heredando de EventMessage.
  8. Definir el message_template, formateable con los attributos del EventPayload definido.
  9. Si aplica, definir el stacked_message_template, formateable con los attributos del payload.
  10. Si es necesario, definir un nuevo format_for_client. Este método, según el payload recibido, formateará y traducirá los mensajes. Por defecto, si se trata de un EventPayload o de un StackedEventPayload de largo 1, utilizará el message_template formateado con los atributos del payload. Si se trata de un StackedEventPayload de largo > 1, formateará el stacked_message_template con el parámetro n_values cuyo valor es el largo del array de payloads.
  11. Creal la clase del evento, heredando de AuraEvent:
  12. definir el event_name
  13. definir el creation_schema, en formato jsonschema, con los parámetros necesarios para crear un evento.
  14. definir el __init__(**kwargs) que recibirá los atributos del creation_schema y defina los atributos del evento.
  15. asignarle los payload_class, stacked_payload_class y message_class
  16. definir el método load. Este es el método principal y debe (en este orden):
  17. definir self.payload: instancia de la EventPayload definida para el evento
  18. guardar en evento en la base de datos
  19. definir self.targets: array de EventTarget con los destinos a recibir las notificaciones. Si la cantidad de destinos puede ser muy grande, utilizar las clases de iteradores para reducir la carga de memoria.
  20. definir self.notificators: array de notificadores, inicializados con sus configuraciones.
  21. Agregarlo al registro de eventos con el decorador @EventFactory.register()
  22. Agregar el event_name en la Base de Datos, tabla event_definitions
  23. Crear el canal SSE (si aplica)
  24. En source.notificators.sse.channels.NEW_CHANNEL.py, herdando de SSEChannel
  25. Definirle name con el nombre del canal.
  26. Definirle el redis_queue_template con el nombre del tópico
  27. Definir el método get_redis_queue que formatee redis_queue_template con los atributos de un EventTarget
  28. Agregarlo al registro de canales con el decorador @ChannelFactory.register()
  29. El front debera contemplar este canal

Traducciones

Las traducciones se encuentran en translations/files, en los archivos .po de cada lenguaje. Las traducciones se realizan con los siguientes pasos:

  1. Marcar los textos a traducir. en caso que el texto se defina en algún lugar donde el locale no está disponible, marcarlo así:
from core.translations import translatable
s = translatable('texto a traducir')

luego donde se deba traducir:

from core.translations import TRANSLATOR
TRANSLATOR.gettext(variable_con_texto, locale)

en caso que el texto esé definido en el lugar donde está definido el locale:

from core.translations import TRANSLATOR
TRANSLATOR.gettext('texto a traducir', locale)
  1. correr el script para actualizar los archivos. Asegurarse tener Babel en el entorno.
source translations/update_translations
  1. Traducir los .po
  2. correr el script para generar los binarios .mo
source translations/update_binaries

El requirements.dev.txt incluye las dependencias para realizar las traducciones.