Sélectionner une révision Git
acc_operation.py 19,00 Kio
# Copyright 2023 Le Filament (<http://www.le-filament.com>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from datetime import datetime, time
from dateutil.relativedelta import relativedelta
from odoo import _, models
from odoo.exceptions import ValidationError
from odoo.osv import expression
from odoo.tools import date_utils
DEFAULT_MONTH_RANGE = 3
class AccOperation(models.Model):
_inherit = "acc.operation"
# ------------------------------------------------------
# Actions
# ------------------------------------------------------
def action_view_curves(self):
"""
Action qui ouvre le portail sur l'opération
:return: Vue Qweb
"""
self.ensure_one()
if self.id:
return {
"type": "ir.actions.act_url",
"url": "/operation/%s" % (self.id),
"target": "new",
}
# ------------------------------------------------------
# Business methods
# ------------------------------------------------------
def get_last_month_power_tot(self):
"""
Fonction retournant la consommation totale, l'autoconsommation totale
et la production totale du dernier mois.
:return: la somme de la conso totale de tous les consommateurs,
la somme de l autoconso totale de tous les consommateurs,
la sommme de la prod totale de tous les producteurs
la date de début de mois
"""
self.ensure_one()
# Get last date slot recorded
last_record = self.get_last_cdc_record()
date_start, date_end = self.get_interval("month", last_record)
query = """
SELECT
date_trunc('month', cdc.date_slot) AS date_slot,
(SUM( (CASE
WHEN cdc.comp_data_type = 'cons' THEN cdc.power
ELSE 0 END) )/2) / 1000 as conso_tot,
(SUM( (CASE
WHEN cdc.comp_data_type = 'prod'
THEN cdc.power ELSE 0 END) )/2) / 1000 as prod_tot,
(SUM( (CASE WHEN
cdc.comp_data_type = 'autocons' THEN cdc.power
ELSE 0 END) )/2) / 1000 as autoconso_tot
FROM
acc_enedis_cdc cdc
INNER JOIN acc_operation ope
ON ope.id = cdc.acc_operation_id
WHERE
cdc.acc_operation_id = %s
AND cdc.date_slot >= %s
AND cdc.date_slot <= %s
GROUP BY date_trunc('month', cdc.date_slot);
"""
query_params = (
self.id,
date_start,
date_end,
)
self.env.cr.execute(query, query_params)
raw_data = self.env.cr.fetchone()
return raw_data[1], raw_data[2], raw_data[3], date_start
def get_last_cdc_record(self):
"""
@returns: last acc.enedis.cdc record in operations
"""
# Get last date slot recorded
last_record = (
self.env["acc.enedis.cdc"]
.sudo()
.search(
[
("acc_operation_id", "in", self.ids),
],
limit=1,
order="date_slot DESC",
)
)
if not last_record:
raise ValidationError(_("L'opération ne possède pas de données"))
return last_record
def get_interval(self, scale, last_record):
"""
Fonction retournant une date de début et une date de fin.
Ces dates sont calculées en fonction de l'échelle choisie et du dernier
élément enregistré
- day: la date de début et la date de fin = dernier jour de données
- week: date de fin = fin du jour du dernier jour de données
date de début = date de fin moins 7 jours
- month: date de fin = fin du mois du dernier jour de données
date de début = début du mois du dernier jour de données
- year: date de fin = fin du mois du dernier jour de données
date de début = début de l'année du dernier jour de données
:param str scale: type d'affichage des graphes
(day/week/month/year)
:param object last_record: Dernier enregistrement dans la base
@returns: une date de début et une date de fin
"""
# Convert end datetime to timezone
last_day_start = datetime.combine(last_record.date_slot, time.min)
last_day_end = datetime.combine(last_record.date_slot, time.max)
start_month, end_month = date_utils.get_month(last_record.date_slot)
if scale == "year":
date_end = end_month
date_start = start_month.replace(month=1)
elif scale == "month":
date_end = end_month
date_start = start_month
elif scale == "week":
date_end = last_day_end
date_start = last_day_end - relativedelta(days=7)
elif scale == "day":
date_end = last_day_end
date_start = last_day_start
else:
raise ValueError(
_(
"La période d'affichage est incorrecte : %s, "
"valeurs attendues : day/week/month/year"
)
% scale
)
return date_start, date_end
def get_step_from_date(self, date_start, date_end):
"""
Fonction retournant le pas des courbes en fonction de 2 dates.
:return:
display_hourly_curves (boolean) : whether or not hourly curves
should be displayed
step: hour/month/year
step_display_curve: hour/day/month/year
"""
display_hourly_curves = False
step = "hour"
step_display_curve = "hour"
# Calculate delta between 2 dates
delta = (date_end - date_start).days
if delta < 32:
step_display_curve = "day"
display_hourly_curves = True
elif delta > 366:
step = "year"
step_display_curve = "year"
else:
step = "month"
step_display_curve = "month"
return display_hourly_curves, step, step_display_curve
def get_cdc_by_query_cons(self, slot_type, date_start, date_end, prm_ids=None):
"""
Fonction permettant de récupérer les données pour la
construction des chart pour une ou des opérations données
pour les consommateurs
:param slot_type: type de slot pour la query ("hour", "month" ou
"year")
date_start: date début
date_end: date de fin
prm_ids : liste des PRMs de soutirage à récupérer
@returns: un dictionnaire de données
(labels et data pour les charts à afficher)
"""
label = []
data_autocons = []
data_allocons = []
data_cons = []
data_prod = []
query = """
SELECT
date_trunc(%s, cdc.date_slot) AS date_slot,
(SUM( (CASE
WHEN cdc.comp_data_type = 'cons'
THEN cdc.power ELSE 0 END) )/2) / 1000 as cons,
(SUM( (CASE
WHEN cdc.comp_data_type = 'autocons'
THEN cdc.power ELSE 0 END) )/2) / 1000 as autocons,
(SUM( (CASE
WHEN cdc.comp_data_type = 'prod'
THEN cdc.power ELSE 0 END) )/2) / 1000 as prod
FROM
acc_enedis_cdc cdc
INNER JOIN acc_operation ope
ON ope.id = cdc.acc_operation_id
WHERE
cdc.acc_operation_id IN %s
AND ( cdc.acc_counter_id IN %s OR cdc.comp_data_type = 'prod' )
AND cdc.date_slot >= %s
AND cdc.date_slot <= %s
GROUP BY date_trunc(%s, cdc.date_slot)
ORDER BY date_slot ASC;
"""
query_params = (
slot_type,
tuple(self.ids),
tuple(prm_ids.ids),
date_start,
date_end,
slot_type,
)
self.env.cr.execute(query, query_params)
raw_data = self.env.cr.fetchall()
for row in raw_data:
label.append(row[0])
data_cons.append({"x": row[0], "y": round(row[1], 2)})
data_autocons.append({"x": row[0], "y": round(row[2], 2)})
data_allocons.append({"x": row[0], "y": round(row[1] - row[2], 2)})
data_prod.append({"x": row[0], "y": round(row[3], 2)})
cdc_cons = {
"label": label,
"autocons": data_autocons,
"allocons": data_allocons,
"cons": data_cons,
"prod": data_prod,
}
return cdc_cons
def get_cdc_by_query_daily_histo_cons(self, date_start, date_end, prm_ids=None):
"""
Fonction permettant de récupérer les données pour la construction
des chart pour une ou des opérations données pour les consommateurs
:param date_start: date début
date_end: date de fin
prm_ids: liste des PRMs de soutirage à récupérer
:return: un dictionnaire de données
(labels et data pour les charts à afficher)
"""
label_histo = []
data_autocons_histo = []
data_allocons_histo = []
query = """
SELECT
date_trunc('day', cdc.date_slot) AS date_slot,
(SUM( (CASE
WHEN cdc.comp_data_type = 'autocons'
THEN cdc.power ELSE 0 END) )/2) / 1000 as autocons,
((SUM( (CASE
WHEN cdc.comp_data_type = 'cons'
THEN cdc.power ELSE 0 END)) -
SUM(CASE
WHEN cdc.comp_data_type = 'autocons'
THEN cdc.power ELSE 0 END) ) / 2) / 1000 as allocons
FROM acc_enedis_cdc cdc
INNER JOIN acc_operation ope
ON ope.id = cdc.acc_operation_id
WHERE cdc.acc_operation_id IN %s
AND cdc.acc_counter_id IN %s
AND cdc.date_slot >= %s
AND cdc.date_slot <= %s
GROUP BY date_trunc('day', cdc.date_slot)
ORDER BY date_slot ASC;
"""
query_params = (
tuple(self.ids),
tuple(prm_ids.ids),
date_start,
date_end,
)
self.env.cr.execute(query, query_params)
raw_data = self.env.cr.fetchall()
for row in raw_data:
data_autocons_histo.append(round(row[1], 2))
data_allocons_histo.append(round(row[2], 2))
label_histo.append(row[0])
cdc_cons = {
"autocons_histo": data_autocons_histo,
"allocons_histo": data_allocons_histo,
"label_histo": label_histo,
}
return cdc_cons
def get_cdc_by_query_prod(self, slot_type, date_start, date_end, prm_ids=None):
"""
Fonction permettant de récupérer les données pour la construction des
chart pour une ou des opérations données pour les consommateurs
:param slot_type: type de slot pour la query ("hour", "month"
ou "year")
date_start: date début
date_end: date de fin
prm_ids: liste des PRMs d'injection à récupérer
:return: un dictionnaire de données
(labels et data pour les charts à afficher)
"""
label = []
data_autocons = []
data_surplus = []
query = """
SELECT
date_trunc(%s, cdc.date_slot) AS date_slot,
((SUM((CASE
WHEN cdc.comp_data_type = 'prod' THEN cdc.power
ELSE 0 END))
- SUM(CASE
WHEN cdc.comp_data_type = 'surplus' THEN cdc.power
ELSE 0 END)) / 2) / 1000 as autocons,
(SUM( (CASE
WHEN cdc.comp_data_type = 'surplus' THEN cdc.power
ELSE 0 END) )/2) / 1000 as surplus
FROM acc_enedis_cdc cdc
INNER JOIN acc_operation ope
ON ope.id = cdc.acc_operation_id
WHERE
cdc.acc_counter_id IN %s
AND cdc.acc_operation_id IN %s
AND cdc.date_slot >= %s
AND cdc.date_slot <= %s
GROUP BY date_trunc(%s, cdc.date_slot)
ORDER BY date_slot ASC;
"""
query_params = (
slot_type,
tuple(prm_ids.ids),
tuple(self.ids),
date_start,
date_end,
slot_type,
)
self.env.cr.execute(query, query_params)
raw_data = self.env.cr.fetchall()
for row in raw_data:
label.append(row[0])
data_autocons.append({"x": row[0], "y": round(row[1], 2)})
data_surplus.append({"x": row[0], "y": round(row[2], 2)})
cdc_prod = {
"label": label,
"autocons_prod": data_autocons,
"surplus": data_surplus,
}
return cdc_prod
def get_cdc_by_query_daily_histo_prod(self, date_start, date_end, prm_ids=None):
"""
Fonction permettant de récupérer les données pour la construction des
chart pour une ou des opérations données pour les consommateurs
:param date_start: date début
date_end: date de fin
prm_ids: liste des PRMs d'injection à récupérer
:return: un dictionnaire de données
(labels et data pour les charts à afficher)
"""
label_histo = []
data_autocons_prod_histo = []
data_surplus_histo = []
query = """
SELECT
date_trunc('day', cdc.date_slot) AS date_slot,
((SUM((CASE
WHEN cdc.comp_data_type = 'prod' THEN cdc.power
ELSE 0 END))
- SUM(CASE
WHEN cdc.comp_data_type = 'surplus' THEN cdc.power
ELSE 0 END)) / 2) / 1000 as autocons,
(SUM( (CASE
WHEN cdc.comp_data_type = 'surplus' THEN cdc.power
ELSE 0 END) )/2) / 1000 as surplus
FROM acc_enedis_cdc cdc
INNER JOIN acc_operation ope
ON ope.id = cdc.acc_operation_id
WHERE
cdc.acc_counter_id IN %s
AND cdc.acc_operation_id IN %s
AND cdc.date_slot >= %s
AND cdc.date_slot <= %s
GROUP BY date_trunc('day', cdc.date_slot)
ORDER BY date_slot ASC;
"""
query_params = (
tuple(prm_ids.ids),
tuple(self.ids),
date_start,
date_end,
)
self.env.cr.execute(query, query_params)
raw_data = self.env.cr.fetchall()
for row in raw_data:
label_histo.append(row[0])
data_autocons_prod_histo.append(round(row[1], 2))
data_surplus_histo.append(round(row[2], 2))
cdc_jour = {
"label_histo": label_histo,
"autocons_prod_histo": data_autocons_prod_histo,
"surplus_histo": data_surplus_histo,
}
return cdc_jour
def get_date_min_max(self):
self.ensure_one()
last_record = self.get_last_cdc_record()
date_max = last_record.date_slot.strftime("%d/%m/%Y")
date_min = self.date_start_contract.strftime("%d/%m/%Y")
return {"date_min": date_min, "date_max": date_max}
# ------------------------------------------------------
# Functions to manage route
# ------------------------------------------------------
def graph_view_global(
self,
date_start=None,
date_end=None,
partner_id=None,
prm_id=None,
data_type=None,
):
"""
Fonction appelée pour l'affichage des courbes globales
sur le portail
:param date_start: date début
date_end: date de fin
partner_id: données uniquement de ce contact
prm_id: données uniquement de ce PRM (mutuellement exclusif de
partner_id)
data_type: type de courbes à afficher:
- "pmo" : vue globale (cons + prod sans filtrage)
- "cons" : vue mon suivi conso (avec filtrage possible)
- "prod" : vue mon suivi production (avec filtrage possible)
@returns: dictionnaire pour la construction des graphes
"""
self.ensure_one()
result_graph = {}
date_start = datetime.strptime(date_start, "%d/%m/%Y")
date_end = datetime.combine(datetime.strptime(date_end, "%d/%m/%Y"), time.max)
display_hourly_curves, step_curve, step_display_curve = self.get_step_from_date(
date_start=date_start, date_end=date_end
)
# TODO: ajouter filtre par période de PRM en fonction date de début / fin
# (si prm_id ou partner_id)
# Si prm_id alors filtrer pour n'afficher que ce compteur
if prm_id:
acc_counter_ids = self.env["acc.counter"].browse(prm_id)
else:
# Filtre par défaut = même opération
domain = [["acc_operation_id", "=", self.id]]
# Si partner_id alors on ne prend que les PRMs appartenant à ce contact
if partner_id:
domain = expression.AND([domain, [("partner_id", "=", partner_id)]])
acc_counter_ids = (
self.env["acc.counter.period"].search(domain).mapped("acc_counter_id")
)
chart_data = {}
if data_type == "cons" or data_type == "pmo":
chart_data_cons = self.get_cdc_by_query_cons(
step_curve, date_start, date_end, acc_counter_ids
)
if display_hourly_curves:
chart_data_histo = self.get_cdc_by_query_daily_histo_cons(
date_start, date_end, acc_counter_ids
)
chart_data_cons.update(chart_data_histo)
chart_data.update(chart_data_cons)
if data_type == "prod" or data_type == "pmo":
chart_data_prod = self.get_cdc_by_query_prod(
step_curve, date_start, date_end, acc_counter_ids
)
if display_hourly_curves:
chart_data_histo = self.get_cdc_by_query_daily_histo_prod(
date_start, date_end, acc_counter_ids
)
chart_data_prod.update(chart_data_histo)
chart_data.update(chart_data_prod)
result_graph["chart_data"] = chart_data
result_graph.update(self.get_date_min_max())
result_graph.update(
{
"date_start": date_start,
"date_end": date_end,
"scale": step_display_curve,
"is_curve_line": display_hourly_curves,
}
)
return result_graph