import json import typing import liquid from flask import url_for, current_app from core_notifications.analytics import emails_events from core_notifications.consts import SystemEvents from core_notifications.helpers.services import BaseLogicService from core_notifications.logs import logger from core_notifications.models import DNAAccount, Template, TemplateVersion from core_notifications.tasks import send_email_task class CreateTemplateService(BaseLogicService): """Create new template instance and saves into DB.""" def process( self, name: str, subject: str, source: str, context: str, dna_account: DNAAccount, is_active: bool, **kwargs, ) -> Template: template = Template() template.name = name template.subject = subject template.source = source template.context = context template.is_active = is_active template.created_by = dna_account template.updated_by = dna_account self.session.add(template) template.versions.append(TemplateVersion.from_template(template)) return template class UpdateTemplateService(BaseLogicService): """Updates existing template instance and saves into DB.""" def process( self, template: Template, name: str, subject: str, source: str, context: str, dna_account: DNAAccount, is_active: bool, **kwargs, ) -> Template: need_version = ( template.name != name or template.subject != subject or template.source != source or template.context != context ) template.name = name template.subject = subject template.source = source template.context = context template.updated_by = dna_account template.is_active = is_active if need_version: self.session.add(TemplateVersion.from_template(template)) return template class EnableDisableTemplateService(BaseLogicService): """Enables or disables existing template instance and saves into DB.""" def process( self, template: Template, is_active: bool, dna_account: DNAAccount, **kwargs, ) -> Template: template.is_active = is_active template.updated_by = dna_account return template class RenderLiquidService(BaseLogicService): """ Renders the template with Liquid engine. Fails silently on eny errors in source or context. The data is intended to be prevalidated. """ commit = False def process( self, source: str, context: typing.Union[typing.Mapping, str], **kwargs ) -> str: if not isinstance(context, typing.Mapping): try: context = json.loads(context) except Exception as e: logger.bind(context=context, error=e).warning( "Invalid context data" ) context = {} try: result = liquid.Template(source).render(context) except Exception as e: logger.bind(error=e).warning("Invalid template source") result = "" return result class SendTestEmailService(BaseLogicService): """Sending test email for specified template.""" commit = False def process(self, template: Template, email: str): subject = RenderLiquidService.execute( template.subject, template.context ) body = RenderLiquidService.execute(template.source, template.context) task_id = send_email_task.delay( subject=subject, to=email, body=body, analytic_url=url_for( "analytics.link_click", _external=True, ), analytics={ "subject": subject, "template_id": template.id, "template_name": template.name, "user_id": current_app.config.get("ANALYTICS_USER_ID"), "user_name": current_app.config.get("ANALYTICS_USER_NAME"), }, track_clicks=True, ).task_id emails_events.log( SystemEvents.sending_email, id=task_id, template_id=template.id, template_name=template.name, to=email, subject=subject, user_id=current_app.config.get("ANALYTICS_USER_ID"), user_name=current_app.config.get("ANALYTICS_USER_NAME"), )