Files
enterprise-addons/esg/controllers/esg_dashboard.py
T

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()