260 lines
8.2 KiB
Python
260 lines
8.2 KiB
Python
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
from sql import Literal
|
|
from sql.aggregate import Count, Min
|
|
from sql.conditionals import NullIf
|
|
from sql.functions import DateTrunc, Round
|
|
|
|
from trytond.model import ModelSQL, ModelView, fields
|
|
from trytond.pool import Pool
|
|
from trytond.pyson import Eval, If
|
|
from trytond.transaction import Transaction
|
|
|
|
|
|
class Context(ModelView):
|
|
__name__ = 'marketing.automation.reporting.context'
|
|
|
|
from_date = fields.Date("From Date",
|
|
domain=[
|
|
If(Eval('to_date') & Eval('from_date'),
|
|
('from_date', '<=', Eval('to_date')),
|
|
()),
|
|
])
|
|
to_date = fields.Date("To Date",
|
|
domain=[
|
|
If(Eval('from_date') & Eval('to_date'),
|
|
('to_date', '>=', Eval('from_date')),
|
|
()),
|
|
])
|
|
period = fields.Selection([
|
|
('year', "Year"),
|
|
('month', "Month"),
|
|
('day', "Day"),
|
|
], "Period", required=True)
|
|
|
|
@classmethod
|
|
def default_from_date(cls):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
context = Transaction().context
|
|
if 'from_date' in context:
|
|
return context['from_date']
|
|
return Date.today() - relativedelta(years=1)
|
|
|
|
@classmethod
|
|
def default_to_date(cls):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
context = Transaction().context
|
|
if 'to_date' in context:
|
|
return context['to_date']
|
|
return Date.today()
|
|
|
|
@classmethod
|
|
def default_period(cls):
|
|
return Transaction().context.get('period', 'month')
|
|
|
|
|
|
class Abstract(ModelSQL, ModelView):
|
|
|
|
date = fields.Date("Date")
|
|
|
|
@classmethod
|
|
def table_query(cls):
|
|
from_item, tables, withs = cls._joins()
|
|
return from_item.select(*cls._columns(tables, withs),
|
|
where=cls._where(tables, withs),
|
|
group_by=cls._group_by(tables, withs),
|
|
with_=withs.values())
|
|
|
|
@classmethod
|
|
def _columns(cls, tables, withs):
|
|
return [
|
|
cls._column_id(tables, withs).as_('id'),
|
|
cls._column_date(tables, withs).as_('date'),
|
|
]
|
|
|
|
|
|
class Scenario(Abstract):
|
|
__name__ = 'marketing.automation.reporting.scenario'
|
|
|
|
scenario = fields.Many2One(
|
|
'marketing.automation.scenario', "Scenario")
|
|
record_count = fields.Integer("Records")
|
|
record_count_blocked = fields.Integer("Records Blocked")
|
|
block_rate = fields.Float("Block Rate")
|
|
|
|
@classmethod
|
|
def _joins(cls):
|
|
pool = Pool()
|
|
Record = pool.get('marketing.automation.record')
|
|
tables = {}
|
|
tables['record'] = record = Record.__table__()
|
|
withs = {}
|
|
return record, tables, withs
|
|
|
|
@classmethod
|
|
def _columns(cls, tables, withs):
|
|
record = tables['record']
|
|
|
|
record_count = Count(Literal('*'))
|
|
record_count_blocked = Count(Literal('*'), filter_=record.blocked)
|
|
|
|
return super()._columns(tables, withs) + [
|
|
record.scenario.as_('scenario'),
|
|
record_count.as_('record_count'),
|
|
record_count_blocked.as_('record_count_blocked'),
|
|
cls.block_rate.sql_cast(
|
|
Round(record_count_blocked / NullIf(record_count, 0), 2)).as_(
|
|
'block_rate'),
|
|
]
|
|
|
|
@classmethod
|
|
def _column_id(cls, tables, withs):
|
|
record = tables['record']
|
|
return Min(record.id)
|
|
|
|
@classmethod
|
|
def _column_date(cls, tables, withs):
|
|
context = Transaction().context
|
|
record = tables['record']
|
|
|
|
date = DateTrunc(context.get('period', 'month'), record.create_date)
|
|
return cls.date.sql_cast(date)
|
|
|
|
@classmethod
|
|
def _group_by(cls, tables, withs):
|
|
record = tables['record']
|
|
return [record.scenario, cls._column_date(tables, withs)]
|
|
|
|
@classmethod
|
|
def _where(cls, tables, withs):
|
|
context = Transaction().context
|
|
record = tables['record']
|
|
|
|
where = Literal(True)
|
|
from_date = context.get('from_date')
|
|
if from_date:
|
|
where &= record.create_date >= from_date
|
|
to_date = context.get('to_date')
|
|
if to_date:
|
|
where &= record.create_date <= to_date
|
|
return where
|
|
|
|
|
|
class Activity(Abstract):
|
|
__name__ = 'marketing.automation.reporting.activity'
|
|
|
|
activity = fields.Many2One(
|
|
'marketing.automation.activity', "Activity")
|
|
activity_action = fields.Function(
|
|
fields.Selection('get_activity_actions', "Activity Action"),
|
|
'on_change_with_activity_action')
|
|
record_count = fields.Integer("Records")
|
|
email_opened = fields.Integer(
|
|
"Emails Opened",
|
|
states={
|
|
'invisible': Eval('activity_action') != 'send_email',
|
|
})
|
|
email_clicked = fields.Integer(
|
|
"Emails Clicked",
|
|
states={
|
|
'invisible': Eval('activity_action') != 'send_email',
|
|
})
|
|
email_open_rate = fields.Float(
|
|
"Email Open Rate",
|
|
states={
|
|
'invisible': Eval('activity_action') != 'send_email',
|
|
})
|
|
email_click_rate = fields.Float(
|
|
"Email Click Rate",
|
|
states={
|
|
'invisible': Eval('activity_action') != 'send_email',
|
|
})
|
|
email_click_through_rate = fields.Float(
|
|
"Email Click-Through Rate",
|
|
states={
|
|
'invisible': Eval('activity_action') != 'send_email',
|
|
})
|
|
|
|
@classmethod
|
|
def get_activity_actions(cls):
|
|
pool = Pool()
|
|
Activity = pool.get('marketing.automation.activity')
|
|
return Activity.fields_get(['action'])['action']['selection']
|
|
|
|
@fields.depends('activity')
|
|
def on_change_with_activity_action(self, name=None):
|
|
if self.activity:
|
|
return self.activity.action
|
|
|
|
@classmethod
|
|
def _joins(cls):
|
|
pool = Pool()
|
|
RecordActivity = pool.get('marketing.automation.record.activity')
|
|
tables = {}
|
|
tables['record_activity'] = record = RecordActivity.__table__()
|
|
withs = {}
|
|
return record, tables, withs
|
|
|
|
@classmethod
|
|
def _columns(cls, tables, withs):
|
|
record_activity = tables['record_activity']
|
|
|
|
record_count = Count(
|
|
Literal('*'), filter_=record_activity.state == 'done')
|
|
email_opened = Count(
|
|
Literal('*'), filter_=record_activity.email_opened)
|
|
email_clicked = Count(
|
|
Literal('*'), filter_=record_activity.email_clicked)
|
|
|
|
return super()._columns(tables, withs) + [
|
|
record_activity.activity.as_('activity'),
|
|
record_count.as_('record_count'),
|
|
email_opened.as_('email_opened'),
|
|
email_clicked.as_('email_clicked'),
|
|
cls.email_open_rate.sql_cast(
|
|
Round(email_opened / NullIf(record_count, 0), 2)).as_(
|
|
'email_open_rate'),
|
|
cls.email_click_rate.sql_cast(
|
|
Round(email_clicked / NullIf(record_count, 0), 2)).as_(
|
|
'email_click_rate'),
|
|
cls.email_click_through_rate.sql_cast(
|
|
Round(email_clicked / NullIf(email_opened, 0), 2)).as_(
|
|
'email_click_through_rate'),
|
|
]
|
|
|
|
@classmethod
|
|
def _column_id(cls, tables, withs):
|
|
record_activity = tables['record_activity']
|
|
return Min(record_activity.id)
|
|
|
|
@classmethod
|
|
def _column_date(cls, tables, withs):
|
|
context = Transaction().context
|
|
record_activity = tables['record_activity']
|
|
|
|
date = DateTrunc(context.get('period', 'month'), record_activity.at)
|
|
return cls.date.sql_cast(date)
|
|
|
|
@classmethod
|
|
def _group_by(cls, tables, withs):
|
|
record_activity = tables['record_activity']
|
|
return [record_activity.activity, cls._column_date(tables, withs)]
|
|
|
|
@classmethod
|
|
def _where(cls, tables, withs):
|
|
context = Transaction().context
|
|
record_activity = tables['record_activity']
|
|
|
|
where = Literal(True)
|
|
from_date = context.get('from_date')
|
|
if from_date:
|
|
where &= record_activity.at >= from_date
|
|
to_date = context.get('to_date')
|
|
if to_date:
|
|
where &= record_activity.at <= to_date
|
|
return where
|