[ADD] base modules

This commit is contained in:
Muhammad
2024-04-07 12:43:39 +05:00
parent 311598a929
commit fa3d921e2d
276 changed files with 51186 additions and 0 deletions

6
web_cohort/models/__init__.py Executable file
View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import ir_action_act_window
from . import ir_ui_view
from . import models

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
from odoo import fields, models
class ActWindowView(models.Model):
_inherit = 'ir.actions.act_window.view'
view_mode = fields.Selection(selection_add=[
('cohort', 'Cohort')
], ondelete={'cohort': 'cascade'})

View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from odoo import fields, models
class View(models.Model):
_inherit = 'ir.ui.view'
type = fields.Selection(selection_add=[('cohort', 'Cohort')])

181
web_cohort/models/models.py Executable file
View File

@@ -0,0 +1,181 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from datetime import datetime
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
from odoo.osv import expression
DISPLAY_FORMATS = {
'day': '%d %b %Y',
'week': 'W%W %Y',
'month': '%B %Y',
'year': '%Y',
}
class Base(models.AbstractModel):
_inherit = 'base'
@api.model
def get_cohort_data(self, date_start, date_stop, measure, interval, domain, mode, timeline):
"""
Get all the data needed to display a cohort view
:param date_start: the starting date to use in the group_by clause
:param date_stop: the date field which mark the change of state
:param measure: the field to aggregate
:param interval: the interval of time between two cells ('day', 'week', 'month', 'year')
:param domain: a domain to limit the read_group
:param mode: the mode of aggregation ('retention', 'churn') [default='retention']
:param timeline: the direction to display data ('forward', 'backward') [default='forward']
:return: dictionary containing a total amount of records considered and a
list of rows each of which contains 16 cells.
"""
rows = []
columns_avg = defaultdict(lambda: dict(percentage=0, count=0))
total_value = 0
initial_churn_value = 0
measure_is_many2one = self._fields.get(measure) and self._fields.get(measure).type == 'many2one'
field_measure = (
[measure + ':count_distinct']
if measure_is_many2one
else ([measure] if self._fields.get(measure) else [])
)
row_groups = self._read_group_raw(
domain=domain,
fields=[date_start] + field_measure,
groupby=date_start + ':' + interval
)
for group in row_groups:
dates = group['%s:%s' % (date_start, interval)]
if not dates:
continue
# Split with space for smoothly format datetime field
clean_start_date = dates[0].split('/')[0].split(' ')[0]
cohort_start_date = fields.Datetime.from_string(clean_start_date)
if measure == '__count__':
value = float(group[date_start + '_count'])
else:
value = float(group[measure] or 0.0)
total_value += value
sub_group = self._read_group_raw(
domain=group['__domain'],
fields=[date_stop] + field_measure,
groupby=date_stop + ':' + interval
)
sub_group_per_period = {}
for g in sub_group:
d_stop = g["%s:%s" % (date_stop, interval)]
if d_stop:
date_group = fields.Datetime.from_string(d_stop[0].split('/')[0])
group_interval = date_group.strftime(DISPLAY_FORMATS[interval])
sub_group_per_period[group_interval] = g
columns = []
initial_value = value
col_range = range(-15, 1) if timeline == 'backward' else range(0, 16)
for col_index, col in enumerate(col_range):
col_start_date = cohort_start_date
if interval == 'day':
col_start_date += relativedelta(days=col)
col_end_date = col_start_date + relativedelta(days=1)
elif interval == 'week':
col_start_date += relativedelta(days=7 * col)
col_end_date = col_start_date + relativedelta(days=7)
elif interval == 'month':
col_start_date += relativedelta(months=col)
col_end_date = col_start_date + relativedelta(months=1)
else:
col_start_date += relativedelta(years=col)
col_end_date = col_start_date + relativedelta(years=1)
if col_start_date > datetime.today():
columns_avg[col_index]
columns.append({
'value': '-',
'churn_value': '-',
'percentage': '',
})
continue
significative_period = col_start_date.strftime(DISPLAY_FORMATS[interval])
col_group = sub_group_per_period.get(significative_period, {})
if not col_group:
col_value = 0.0
elif measure == '__count__':
col_value = col_group[date_stop + '_count']
else:
col_value = col_group[measure] or 0.0
# In backward timeline, if columns are out of given range, we need
# to set initial value for calculating correct percentage
if timeline == 'backward' and col_index == 0:
outside_timeline_domain = expression.AND(
[
group['__domain'],
['|',
(date_stop, '=', False),
(date_stop, '>=', fields.Datetime.to_string(col_start_date)),
]
]
)
col_group = self._read_group_raw(
domain=outside_timeline_domain,
fields=field_measure,
groupby=[]
)
if measure == '__count__':
initial_value = float(col_group[0]['__count'])
else:
initial_value = float(col_group[0][measure] or 0.0)
initial_churn_value = value - initial_value
previous_col_remaining_value = initial_value if col_index == 0 else columns[-1]['value']
col_remaining_value = previous_col_remaining_value - col_value
percentage = value and (col_remaining_value) / value or 0
if mode == 'churn':
percentage = 1 - percentage
percentage = round(100 * percentage, 1)
columns_avg[col_index]['percentage'] += percentage
columns_avg[col_index]['count'] += 1
# For 'week' interval, we display a better tooltip (range like : '02 Jul - 08 Jul')
if interval == 'week':
period = "%s - %s" % (col_start_date.strftime('%d %b'), (col_end_date - relativedelta(days=1)).strftime('%d %b'))
else:
period = col_start_date.strftime(DISPLAY_FORMATS[interval])
if mode == 'churn':
domain = [
(date_stop, '<', col_end_date.strftime(DEFAULT_SERVER_DATE_FORMAT)),
]
else:
domain = ['|',
(date_stop, '>=', col_end_date.strftime(DEFAULT_SERVER_DATE_FORMAT)),
(date_stop, '=', False),
]
columns.append({
'value': col_remaining_value,
'churn_value': col_value + (columns[-1]['churn_value'] if col_index > 0 else initial_churn_value),
'percentage': percentage,
'domain': domain,
'period': period,
})
rows.append({
'date': dates[1],
'value': value,
'domain': group['__domain'],
'columns': columns,
})
return {
'rows': rows,
'avg': {'avg_value': total_value / len(rows) if rows else 0, 'columns_avg': columns_avg},
}