async def create_notification(self, request): request: CreateNotificationRequest = convert_request( CreateNotificationRequest, await request.json(), ) try: conditions = dataclasses.asdict( deserialize.deserialize(ConditionClause, request.conditions)) except deserialize.exceptions.DeserializeException as error: return json_response(reason=f'wrong condition clause {error}', status=400) current_datetime = utc_now() if request.scheduled_at is None: scheduled_at = current_datetime else: scheduled_at = request.scheduled_at if current_datetime > scheduled_at: return json_response( reason=f'scheduled_at should later than current time', status=400) notification = await create_notification( title=request.title, body=request.body, scheduled_at=scheduled_at, deep_link=request.deep_link, image_url=request.image_url, icon_url=request.icon_url, conditions=conditions, ) return json_response(result=notification_model_to_dict(notification))
async def get_notifications(self, request): query_params: FetchNotificationsRequest = convert_request( FetchNotificationsRequest, dict(request.rel_url.query), ) available_order_by_fields = { 'created_at', '-created_at', 'modified_at', '-modified_at', 'scheduled_at', '-scheduled_at', } total, notifications = await find_notifications_by_status( status=query_params.status, start=query_params.start, size=query_params.size, order_bys=list( available_order_by_fields.intersection( query_params.order_bys)), ) return json_response( result={ 'total': total, 'notifications': [ notification_model_to_dict(notification) for notification in notifications ] })
def device_notification_log_model_to_dict(row: DeviceNotificationLog): device_notification_log_dict = { 'id': row.id, 'notification': notification_model_to_dict(row.notification), 'created_at': row.created_at, } return device_notification_log_dict
def device_notification_event_model_to_dict(row: DeviceNotificationEvent): device_notification_event_dict = { 'id': row.id, 'notification': notification_model_to_dict(row.notification), 'event': row.event, 'created_at': row.created_at, } return device_notification_event_dict
async def get_notification(self, request): notification_uuid = request.match_info['notification_uuid'] notification = await find_notification_by_id(uuid=notification_uuid) if notification is None: return json_response( reason=f'notification not found {notification_uuid}', status=404) return json_response(result=notification_model_to_dict(notification))
async def launch_notification(self, request): notification_uuid = request.match_info['notification_uuid'] notification = await find_notification_by_id(uuid=notification_uuid) if notification is None: return json_response( reason=f'notification not found {notification_uuid}', status=404) notification = await change_notification_status( target_notification=notification, status=NotificationStatus.QUEUED, ) conditions = deserialize.deserialize(ConditionClause, notification.conditions) tasks = [{ 'task': NotificationTask.LAUNCH_NOTIFICATION, 'kwargs': { 'notification': { 'id': notification.id, 'uuid': str(notification.uuid), 'title': notification.title, 'body': notification.body, 'image_url': notification.image_url, 'icon_url': notification.icon_url, 'deep_link': notification.deep_link, }, 'conditions': dataclasses.asdict(conditions), }, }] try: await self.notification_task_queue.apply_tasks(tasks=[ TaskIn( task=task, queue_name='NOTIFICATION_JOB_QUEUE', scheduled_at=int(notification.scheduled_at.timestamp()), ) for task in tasks ], ) except Exception as e: # rollback if queue pushing failed logger.warning(f'rollback because of queue pushing failed {e}') notification = await change_notification_status( target_notification=notification, status=NotificationStatus.ERROR, ) return json_response(result=notification_model_to_dict(notification))
async def update_notification_status(self, request): notification_uuid = request.match_info['notification_uuid'] notification = await find_notification_by_id(uuid=notification_uuid) if notification is None: return json_response( reason=f'notification not found {notification_uuid}', status=404) request_body: UpdateNotificationStatusRequest = convert_request( UpdateNotificationStatusRequest, await request.json(), ) notification = await change_notification_status( target_notification=notification, status=request_body.status, ) return json_response(result=notification_model_to_dict(notification))
async def launch_notification(self, request): notification_uuid = request.match_info['notification_uuid'] notification = await find_notification_by_id(uuid=notification_uuid) if notification is None: return json_response( reason=f'notification not found {notification_uuid}', status=404) notification = await change_notification_status( target_notification=notification, status=NotificationStatus.LAUNCHED, ) conditions = deserialize.deserialize(ConditionClause, notification.conditions) device_total = await get_device_total_by_conditions( conditions=conditions) notification_job_capacity = math.ceil(device_total / self.NOTIFICATION_WORKER_COUNT) try: tasks = [] with await self.redis_pool as redis_conn: current_datetime = utc_now() scheduled_at_utc = datetime_to_utc_datetime( notification.scheduled_at) priority = NotificationPriority.IMMEDIATE if scheduled_at_utc > current_datetime: priority = NotificationPriority.SCHEDULED for job_index in range(self.NOTIFICATION_WORKER_COUNT): job: NotificationJob = deserialize.deserialize( NotificationJob, { 'notification': { 'id': notification.id, 'uuid': str(notification.uuid), 'title': notification.title, 'body': notification.body, 'image_url': notification.image_url, 'icon_url': notification.icon_url, 'deep_link': notification.deep_link, 'conditions': object_to_dict(conditions), 'devices': { 'start': job_index * notification_job_capacity, 'size': notification_job_capacity, } }, 'scheduled_at': scheduled_at_utc.isoformat() }) tasks.append( publish_notification_job( redis_conn=redis_conn, job=object_to_dict(job), priority=priority, )) await asyncio.gather(*tasks) except Exception as e: # rollback if queue pushing failed logger.warning(f'rollback because of queue pushing failed {e}') notification = await change_notification_status( target_notification=notification, status=NotificationStatus.ERROR, ) return json_response(result=notification_model_to_dict(notification))