255 lines
9.7 KiB
Python
255 lines
9.7 KiB
Python
from datetime import date
|
|
from random import randint
|
|
|
|
from babel.dates import format_date
|
|
from dateutil.relativedelta import relativedelta
|
|
from werkzeug.exceptions import Forbidden
|
|
|
|
from odoo.http import Controller, request, route
|
|
from odoo.tools.misc import get_lang
|
|
|
|
|
|
class EsgDashboard(Controller):
|
|
def _build_data_per_scope(self, start_date, end_date):
|
|
# Quantity of carbon emissions per scope during end_date year
|
|
data_per_scope = dict(request.env['esg.carbon.emission.report']._read_group(
|
|
domain=[
|
|
('esg_emission_factor_id', '!=', False),
|
|
('date', '<=', end_date),
|
|
('date', '>=', start_date),
|
|
],
|
|
groupby=['scope'],
|
|
aggregates=['esg_emissions_value_t:sum'],
|
|
))
|
|
scope_names = {
|
|
'direct': request.env._('Scope 1'),
|
|
'indirect': request.env._('Scope 2'),
|
|
'indirect_others': request.env._('Scope 3'),
|
|
}
|
|
|
|
# If no data found from start of current year to now, build sample data
|
|
if not data_per_scope:
|
|
data_per_scope = {
|
|
scope: randint(50, 100)
|
|
for scope in scope_names.values()
|
|
}
|
|
is_sample = True
|
|
else:
|
|
data_per_scope = dict(sorted({
|
|
scope_names.get(scope): round(value, 2)
|
|
for scope, value in data_per_scope.items()
|
|
}.items()))
|
|
is_sample = False
|
|
|
|
return {
|
|
'data': data_per_scope,
|
|
'is_sample': is_sample,
|
|
}
|
|
|
|
def _build_graph_config_per_scope(self, data, title, is_sample=False):
|
|
return {
|
|
'type': 'bar',
|
|
'data': {
|
|
'labels': list(data.keys()),
|
|
'datasets': [
|
|
{
|
|
'label': self.env._("tCO₂e"),
|
|
'data': list(data.values()),
|
|
'backgroundColor': 'rgba(78, 167, 242, 1)' if not is_sample else 'rgba(235, 235, 235, 1)',
|
|
},
|
|
],
|
|
},
|
|
'options': {
|
|
'scales': {
|
|
'y': {
|
|
'display': False,
|
|
},
|
|
'x': {
|
|
'ticks': {
|
|
'color': 'black',
|
|
},
|
|
},
|
|
},
|
|
'plugins': {
|
|
'legend': {
|
|
'display': False,
|
|
},
|
|
'title': {
|
|
'display': not is_sample,
|
|
'text': title,
|
|
'color': '#4F4F4F',
|
|
'font': {
|
|
'size': 18,
|
|
},
|
|
},
|
|
'tooltip': {
|
|
'enabled': not is_sample,
|
|
'intersect': False,
|
|
'position': 'nearest',
|
|
'caretSize': 0,
|
|
},
|
|
},
|
|
'aspectRatio': 4,
|
|
},
|
|
}
|
|
|
|
def _get_carbon_analytics_section_data(self):
|
|
has_unassigned_emissions = request.env['account.move.line'].search_count([
|
|
('esg_emission_factor_id', '=', False),
|
|
('quantity', '>', 0),
|
|
('parent_state', '=', 'posted'),
|
|
('account_id.account_type', 'in', request.env['account.account'].ESG_VALID_ACCOUNT_TYPES),
|
|
], limit=1)
|
|
return {
|
|
'has_unassigned_emissions': has_unassigned_emissions,
|
|
}
|
|
|
|
def _build_data_per_month(self, start_date, end_date):
|
|
# Cumulative distribution of carbon emissions per month, displayed from start_date to end_date
|
|
data = dict(request.env['esg.carbon.emission.report']._read_group(
|
|
domain=[
|
|
('esg_emission_factor_id', '!=', False),
|
|
('date', '<=', end_date),
|
|
('date', '>=', start_date),
|
|
],
|
|
groupby=['date:month'],
|
|
aggregates=['esg_emissions_value_t:sum'],
|
|
))
|
|
|
|
# Generate a dictionary with all months from start_date to end_date
|
|
start_date = start_date.replace(day=1)
|
|
end_date = end_date.replace(day=1)
|
|
current_date = start_date
|
|
data_per_month = {}
|
|
while current_date <= end_date:
|
|
data_per_month[current_date] = 0.0
|
|
next_month = current_date.month % 12 + 1
|
|
next_year = current_date.year + (1 if next_month == 1 else 0)
|
|
current_date = current_date.replace(month=next_month, year=next_year)
|
|
|
|
if not data: # If no data, build sample data
|
|
for month in data_per_month:
|
|
data_per_month[month] = randint(50, 100)
|
|
is_sample = True
|
|
|
|
else:
|
|
is_sample = False
|
|
accumulated_value = 0.0
|
|
for month in data_per_month:
|
|
if month in data:
|
|
data_per_month[month] = data[month] + accumulated_value
|
|
accumulated_value += data[month]
|
|
else:
|
|
data_per_month[month] = accumulated_value
|
|
|
|
locale = get_lang(request.env).code
|
|
data_per_month = {format_date(month, 'LLLL Y', locale=locale): round(value, 2) for month, value in data_per_month.items()}
|
|
|
|
return {
|
|
'data': data_per_month,
|
|
'is_sample': is_sample,
|
|
}
|
|
|
|
def _build_graph_config_per_month(self, data, is_sample=False):
|
|
return {
|
|
'type': 'line',
|
|
'data': {
|
|
'labels': list(data.keys()),
|
|
'datasets': [
|
|
{
|
|
'label': self.env._("tCO₂e"),
|
|
'data': list(data.values()),
|
|
'backgroundColor': 'rgba(135, 90, 123, 0.2)' if not is_sample else 'rgba(135, 90, 123, 0.05)',
|
|
'fill': 'start',
|
|
'borderWidth': 2,
|
|
'borderColor': 'rgba(135, 90, 123, 1)' if not is_sample else 'rgba(135, 90, 123, 0.1)',
|
|
},
|
|
],
|
|
},
|
|
'options': {
|
|
'scales': {
|
|
'x': {
|
|
'ticks': {
|
|
'display': False,
|
|
},
|
|
'grid': {
|
|
'display': False,
|
|
},
|
|
},
|
|
'y': {
|
|
'display': False,
|
|
'ticks': {
|
|
'display': False,
|
|
},
|
|
'grid': {
|
|
'display': False,
|
|
},
|
|
'beginAtZero': all(value == 0.0 for value in data.values()),
|
|
},
|
|
},
|
|
'plugins': {
|
|
'legend': {
|
|
'display': False,
|
|
},
|
|
'tooltip': {
|
|
'enabled': not is_sample,
|
|
'intersect': False,
|
|
'position': 'nearest',
|
|
'caretSize': 0,
|
|
},
|
|
},
|
|
'aspectRatio': 4,
|
|
},
|
|
}
|
|
|
|
def _get_carbon_footprint_section_data(self, data, is_sample=False):
|
|
section_data = {
|
|
'last_months_percentage': self.env._("n/a"),
|
|
'last_year_percentage': self.env._("n/a"),
|
|
}
|
|
if not is_sample:
|
|
months = list(data.keys())
|
|
current_month_value = data[months[-1]]
|
|
last_months_value = data[months[-6]]
|
|
last_year_value = data[months[-12]]
|
|
|
|
if last_months_value:
|
|
section_data['last_months_percentage'] = round(((current_month_value - last_months_value) / last_months_value) * 100, 2)
|
|
if last_year_value:
|
|
section_data['last_year_percentage'] = round(((current_month_value - last_year_value) / last_year_value) * 100, 2)
|
|
return section_data
|
|
|
|
def _get_dashboard_data(self):
|
|
end_date = date.today()
|
|
# Create data for bar chart grouped by scope
|
|
start_date = end_date.replace(day=1, month=1)
|
|
data_per_scope = self._build_data_per_scope(start_date, end_date)
|
|
title = end_date.year
|
|
graph_config_per_scope = self._build_graph_config_per_scope(data_per_scope['data'], title, data_per_scope['is_sample'])
|
|
carbon_analytics_section_data = self._get_carbon_analytics_section_data()
|
|
|
|
# Create data for line chart grouped by month-year
|
|
start_date = end_date - relativedelta(years=1)
|
|
data_per_month = self._build_data_per_month(start_date, end_date)
|
|
graph_config_per_month = self._build_graph_config_per_month(data_per_month['data'], data_per_month['is_sample'])
|
|
carbon_footprint_section_data = self._get_carbon_footprint_section_data(data_per_month['data'], data_per_month['is_sample'])
|
|
|
|
return {
|
|
'carbon_analytics_box': {
|
|
'graph_config': graph_config_per_scope,
|
|
'has_unassigned_emissions': carbon_analytics_section_data['has_unassigned_emissions'],
|
|
},
|
|
'carbon_footprint_box': {
|
|
'graph_config': graph_config_per_month,
|
|
'last_months_percentage': carbon_footprint_section_data['last_months_percentage'],
|
|
'last_year_percentage': carbon_footprint_section_data['last_year_percentage'],
|
|
},
|
|
}
|
|
|
|
@route('/esg/dashboard', type='jsonrpc', auth='user')
|
|
def get_esg_dashboard(self, company_ids):
|
|
if not request.env['esg.carbon.emission.report'].has_access('read'):
|
|
raise Forbidden()
|
|
request.update_context(allowed_company_ids=company_ids)
|
|
return self._get_dashboard_data()
|