diff --git a/models/__init__.py b/models/__init__.py index c9f67d1029451187faac73b30248b565bd1340d9..b9c11d9d929c89108a38db6f9596ab8bfd372d52 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1 +1,2 @@ +from . import acc_enedis_cdc from . import acc_operation diff --git a/models/acc_enedis_cdc.py b/models/acc_enedis_cdc.py new file mode 100644 index 0000000000000000000000000000000000000000..aefbdc9d551a1a6118304c5bfb8d8be5540789b2 --- /dev/null +++ b/models/acc_enedis_cdc.py @@ -0,0 +1,312 @@ +# Copyright 2021- Le Filament (https://le-filament.com) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +import logging +from datetime import date, datetime + +from dateutil.relativedelta import relativedelta + +from odoo import fields, models + +_logger = logging.getLogger(__name__) + + +class AccEnedisCdc(models.Model): + _inherit = "acc.enedis.cdc" + + # ------------------------------------------------------ + # Fields declaration + # ------------------------------------------------------ + + # ------------------------------------------------------ + # SQL Constraints + # ------------------------------------------------------ + + # ------------------------------------------------------ + # Default methods + # ------------------------------------------------------ + + # ------------------------------------------------------ + # Computed fields / Search Fields + # ------------------------------------------------------ + + # ------------------------------------------------------ + # Onchange / Constraints + # ------------------------------------------------------ + + # ------------------------------------------------------ + # CRUD methods (ORM overrides) + # ------------------------------------------------------ + + # ------------------------------------------------------ + # Actions + # ------------------------------------------------------ + + # ------------------------------------------------------ + # API functions + # ------------------------------------------------------ + def _curves( + self, + operation, + start_date, + end_date, + usage_point_cons_ids=None, + usage_point_prod_ids=None, + ): + """ + Vérifie les périodes, crée un log et appelle les courbes de conso / prod + à partir de l'API Enedis (cet appel passe par un job queue) + + :param record operation : operation (model acc.operation) + :param date start_date : date de début à récupérer (incluse) + :param date end_date : date de fin à récupérer (exclue) + :param recordset usage_point_cons_ids : liste de PRMs de soutirage à récupérer + :param recordset usage_point_prod_ids : liste de PRMs d'injection à récupérer + Les PRMs sont optionnels, + si non renseignés, tous les PRMs liés à l'opération seront récupérés + """ + operation._check_access_api() + + # Si pas de PRM sélectionnés + if not usage_point_cons_ids and not usage_point_prod_ids: + usage_point_cons_ids = operation.acc_delivery_period_ids.mapped( + "acc_counter_id" + ) + usage_point_prod_ids = operation.acc_injection_period_ids.mapped( + "acc_counter_id" + ) + + message = "" + message += ( + "<h1>API Enedis OACC - Création des jobs de récupération des Courbes " + + str(fields.Datetime.now()) + + "</h1>" + "Appels API pour la période " + "" + str(start_date) + " " + str(end_date) + "<br/>" + ) + + if usage_point_cons_ids: + # Traitement données de cons + message += ( + "<br/><strong>Création des jobs de récupération des courbes " + "de consommation</strong><br/>" + ) + for usage_point_id in usage_point_cons_ids: + # Vérification que le PRM est actif sur la période demandée + period_ids = self.env["acc.counter.period"]._get_periods_from_interval( + [ + ("acc_operation_id", "=", operation.id), + ("acc_counter_id", "=", usage_point_id.id), + ("prm_type", "=", "delivery"), + ], + start_date, + end_date, + ) + # Si pas de période de PRM, on ne fait pas d'appel API + if not period_ids: + message += ( + "Opération: " + + operation.name + + " - PRM: " + + usage_point_id.name + + " - aucune période trouvée du " + + str(start_date) + + " au " + + str(end_date) + + "<br/>" + ) + # Si période de PRM on vérifie les dates puis on appelle l'API + else: + start_date_upd = max(start_date, period_ids[0].start_date) + if period_ids[-1].end_date: + end_date_upd = min(end_date, period_ids[-1].end_date) + else: + end_date_upd = end_date + desc = ( + "Opération: " + + operation.name + + " - PRM: " + + usage_point_id.name + + " - Date: " + + str(fields.Datetime.today()) + + " - Période: du " + + str(start_date_upd) + + " au " + + str(end_date_upd) + ) + message += desc + "<br/>" + self.with_delay(description=desc)._get_definitive_load_curves( + operation, start_date_upd, end_date_upd, usage_point_id, "cons" + ) + + if usage_point_prod_ids: + # Traitement données de prod + message += ( + "<br/><strong>Création des jobs de récupération des courbes " + "de production</strong><br/>" + ) + for usage_point_id in usage_point_prod_ids: + # Vérification que le PRM est actif sur la période demandée + period_ids = self.env["acc.counter.period"]._get_periods_from_interval( + [ + ("acc_operation_id", "=", operation.id), + ("acc_counter_id", "=", usage_point_id.id), + ("prm_type", "=", "injection"), + ], + start_date, + end_date, + ) + # Si pas de période de PRM, on ne fait pas d'appel API + if not period_ids: + message += ( + "Opération: " + + operation.name + + " - PRM: " + + usage_point_id.name + + " - aucune période trouvée du " + + str(start_date) + + " au " + + str(end_date) + + "<br/>" + ) + # Si période de PRM on vérifie les dates puis on appelle l'API + else: + start_date_upd = max(start_date, period_ids[0].start_date) + if period_ids[-1].end_date: + end_date_upd = min(end_date, period_ids[-1].end_date) + else: + end_date_upd = end_date + desc = ( + "Opération: " + + operation.name + + " - PRM: " + + usage_point_id.name + + " - Date: " + + str(fields.Datetime.today()) + + " - Période: du " + + str(start_date_upd) + + " au " + + str(end_date_upd) + ) + message += desc + "<br/>" + self.with_delay(description=desc)._get_definitive_load_curves( + operation, + start_date_upd, + end_date_upd, + usage_point_id, + "prod", + ) + + message += ( + "<br/><h1>Fin de création des jobs de récupération des courbes: " + + str(fields.Datetime.now()) + + "</h1>" + ) + if not operation.is_cdc_data_exists: + operation.is_cdc_data_exists = True + # Logs information + self.env["acc.logs"].create( + { + "name": "Appel API Enedis Courbes du " + + str(fields.Date.today()) + + " - Période " + + str(start_date) + + " " + + str(end_date), + "date_launched": fields.Datetime.now(), + "type_log": "api", + "message": message, + "acc_operation_id": operation.id, + } + ) + + def _get_definitive_load_curves( + self, operation, start_date, end_date, usage_point_id, type_prm + ): + """ + Appelle les courbes de conso / prod à partir de l'API Enedis + + :param record operation : operation (model acc.operation) + :param date start_date : date de début à récupérer (incluse) + :param date end_date : date de fin à récupérer (exclue) + :param record usage_point_id : PRM à récupérer + :param char type_prm : type de PRM à récupérer ("cons" ou "prod") + """ + message = "" + message += "PRM " + usage_point_id.name + "\n" + message += "Appel API ...\n" + # Transformation des dates naives en datetime UTC + start_datetime = self._convert_time(start_date) + end_datetime = self._convert_time(end_date) + curves_data = operation._get_definitive_load_curves( + operation.name, + start_datetime, + end_datetime, + usage_point_id.name, + operation.client_id, + operation.secret_id, + type_prm, + ) + message += "Appel API terminé. Traitement des données ...\n" + + curves = curves_data.get("curves") + + if curves: + name = usage_point_id.name + "_" + str(start_date) + "_" + str(end_date) + for curve in curves: + type_curve = curve["type"] + + for point in curve["interval_reading"]: + date_slot = datetime.strptime(point["timestamp"], "%Y-%m-%dT%H:%M:%SZ") + self.env["acc.enedis.cdc"].create( + { + "name": name, + "acc_operation_id": operation.id, + "acc_counter_id": usage_point_id.id or False, + "comp_data_type": type_curve, + "power": point["value"], + "date_slot": date_slot, + } + ) + # Update partner_id for retrieved cdc + domain = [ + ("acc_operation_id", "=", operation.id), + ("acc_counter_id", "=", usage_point_id.id or False), + ("prm_type", "=", "delivery" if type_prm == "cons" else "injection"), + ] + self.env["acc.counter.period"]._get_periods_from_interval( + domain, start_date, end_date + )._update_cdc_partner_id() + message += "Fin du traitement des données\n" + _logger.info(message) + return message + + def _get_curves_all( + self, operation, usage_point_cons_ids=None, usage_point_prod_ids=None + ): + """ + Récupère les données de l'opération depuis le début de la mise en place + de l'opération. A partir de la date de début de contrat, calcul du nombre + de mois entre la date de début de contrat et la date du jour. Puis + récupération des données de tous les mois + """ + nb_months = (date.today().year - operation.date_start_contract.year) * 12 + ( + date.today().month - operation.date_start_contract.month + ) + + start_date_it = operation.date_start_contract + end_date_it = operation.date_start_contract + relativedelta(months=1) + + for _i in range(nb_months): + self._curves( + operation, + start_date_it, + end_date_it, + usage_point_cons_ids, + usage_point_prod_ids, + ) + start_date_it = start_date_it + relativedelta(months=1) + end_date_it = start_date_it + relativedelta(months=1) + + # ------------------------------------------------------ + # Business methods + # ------------------------------------------------------ diff --git a/models/acc_operation.py b/models/acc_operation.py index b6d81abca8ea60c16976649988bfd3b40b94a0b9..65edb73ccf523aa3bd31be637fa557b9e8ff6c41 100644 --- a/models/acc_operation.py +++ b/models/acc_operation.py @@ -1,9 +1,7 @@ # Copyright 2021- Le Filament (https://le-filament.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) import logging -from datetime import date, datetime - -from dateutil.relativedelta import relativedelta +from datetime import date from odoo import _, fields, models from odoo.exceptions import UserError, ValidationError @@ -44,7 +42,6 @@ class AccOperation(models.Model): # ------------------------------------------------------ # Actions # ------------------------------------------------------ - def get_perimeter(self): self.ensure_one() message = self.perimeter() @@ -80,254 +77,6 @@ class AccOperation(models.Model): # ------------------------------------------------------ # API functions # ------------------------------------------------------ - def curves( - self, start_date, end_date, usage_point_cons_ids=None, usage_point_prod_ids=None - ): - """ - Vérifie les périodes, crée un log et appelle les courbes de conso / prod - à partir de l'API Enedis (cet appel passe par un job queue) - - :param date start_date : date de début à récupérer (incluse) - :param date end_date : date de fin à récupérer (exclue) - :param recordset usage_point_cons_ids : liste de PRMs de soutirage à récupérer - :param recordset usage_point_prod_ids : liste de PRMs d'injection à récupérer - Les PRMs sont optionnels, - si non renseignés, tous les PRMs liés à l'opération seront récupérés - """ - self._check_access_api() - # TODO: Update to get curves only for existing periods - - # Si pas de PRM sélectionnés - if not usage_point_cons_ids and not usage_point_prod_ids: - usage_point_cons_ids = self.acc_delivery_period_ids.mapped("acc_counter_id") - usage_point_prod_ids = self.acc_injection_period_ids.mapped( - "acc_counter_id" - ) - - message = "" - message += ( - "<h1>API Enedis OACC - Création des jobs de récupération des Courbes " - + str(fields.Datetime.now()) - + "</h1>" - "Appels API pour la période " - "" + str(start_date) + " " + str(end_date) + "<br/>" - ) - - if usage_point_cons_ids: - # Traitement données de cons - message += ( - "<br/><strong>Création des jobs de récupération des courbes " - "de consommation</strong><br/>" - ) - for usage_point_id in usage_point_cons_ids: - # Vérification que le PRM est actif sur la période demandée - period_ids = self.env["acc.counter.period"]._get_periods_from_interval( - [ - ("acc_operation_id", "=", self.id), - ("acc_counter_id", "=", usage_point_id.id), - ("prm_type", "=", "delivery"), - ], - start_date, - end_date, - ) - # Si pas de période de PRM, on ne fait pas d'appel API - if not period_ids: - message += ( - "Opération: " - + self.name - + " - PRM: " - + usage_point_id.name - + " - aucune période trouvée du " - + str(start_date) - + " au " - + str(end_date) - + "<br/>" - ) - # Si période de PRM on vérifie les dates puis on appelle l'API - else: - start_date_upd = max(start_date, period_ids[0].start_date) - if period_ids[-1].end_date: - end_date_upd = min(end_date, period_ids[-1].end_date) - else: - end_date_upd = end_date - desc = ( - "Opération: " - + self.name - + " - PRM: " - + usage_point_id.name - + " - Date: " - + str(fields.Datetime.today()) - + " - Période: du " - + str(start_date_upd) - + " au " - + str(end_date_upd) - ) - message += desc + "<br/>" - self.with_delay(description=desc).get_definitive_load_curves( - start_date_upd, end_date_upd, usage_point_id, "cons" - ) - - if usage_point_prod_ids: - # Traitement données de prod - message += ( - "<br/><strong>Création des jobs de récupération des courbes " - "de production</strong><br/>" - ) - for usage_point_id in usage_point_prod_ids: - # Vérification que le PRM est actif sur la période demandée - period_ids = self.env["acc.counter.period"]._get_periods_from_interval( - [ - ("acc_operation_id", "=", self.id), - ("acc_counter_id", "=", usage_point_id.id), - ("prm_type", "=", "injection"), - ], - start_date, - end_date, - ) - # Si pas de période de PRM, on ne fait pas d'appel API - if not period_ids: - message += ( - "Opération: " - + self.name - + " - PRM: " - + usage_point_id.name - + " - aucune période trouvée du " - + str(start_date) - + " au " - + str(end_date) - + "<br/>" - ) - # Si période de PRM on vérifie les dates puis on appelle l'API - else: - start_date_upd = max(start_date, period_ids[0].start_date) - if period_ids[-1].end_date: - end_date_upd = min(end_date, period_ids[-1].end_date) - else: - end_date_upd = end_date - desc = ( - "Opération: " - + self.name - + " - PRM: " - + usage_point_id.name - + " - Date: " - + str(fields.Datetime.today()) - + " - Période: du " - + str(start_date_upd) - + " au " - + str(end_date_upd) - ) - message += desc + "<br/>" - self.with_delay(description=desc).get_definitive_load_curves( - start_date_upd, - end_date_upd, - usage_point_id, - "prod", - ) - - message += ( - "<br/><h1>Fin de création des jobs de récupération des courbes: " - + str(fields.Datetime.now()) - + "</h1>" - ) - if not self.is_cdc_data_exists: - self.is_cdc_data_exists = True - # Logs information - self.env["acc.logs"].create( - { - "name": "Appel API Enedis Courbes du " - + str(fields.Date.today()) - + " - Période " - + str(start_date) - + " " - + str(end_date), - "date_launched": fields.Datetime.now(), - "type_log": "api", - "message": message, - "acc_operation_id": self.id, - } - ) - - def get_definitive_load_curves( - self, start_date, end_date, usage_point_id, type_prm - ): - """ - Appelle les courbes de conso / prod à partir de l'API Enedis - - :param date start_date : date de début à récupérer (incluse) - :param date end_date : date de fin à récupérer (exclue) - :param record usage_point_id : PRM à récupérer - :param char type_prm : type de PRM à récupérer ("cons" ou "prod") - """ - message = "" - message += "PRM " + usage_point_id.name + "\n" - message += "Appel API ...\n" - # Transformation des dates naives en datetime UTC - start_datetime = self._convert_time(start_date) - end_datetime = self._convert_time(end_date) - curves_data = self._get_definitive_load_curves( - self.name, - start_datetime, - end_datetime, - usage_point_id.name, - self.client_id, - self.secret_id, - type_prm, - ) - message += "Appel API terminé. Traitement des données ...\n" - - curves = curves_data.get("curves") - - if curves: - name = usage_point_id.name + "_" + str(start_date) + "_" + str(end_date) - for curve in curves: - type_curve = curve["type"] - - for point in curve["interval_reading"]: - date_slot = datetime.strptime(point["timestamp"], "%Y-%m-%dT%H:%M:%SZ") - self.env["acc.enedis.cdc"].create( - { - "name": name, - "acc_operation_id": self.id, - "acc_counter_id": usage_point_id.id or False, - "comp_data_type": type_curve, - "power": point["value"], - "date_slot": date_slot, - } - ) - # Update partner_id for retrieved cdc - domain = [ - ("acc_operation_id", "=", self.id), - ("acc_counter_id", "=", usage_point_id.id or False), - ("prm_type", "=", "delivery" if type_prm == "cons" else "injection"), - ] - self.env["acc.counter.period"]._get_periods_from_interval( - domain, start_date, end_date - )._update_cdc_partner_id() - message += "Fin du traitement des données\n" - _logger.info(message) - return message - - def get_curves_all(self, usage_point_cons_ids=None, usage_point_prod_ids=None): - """ - Récupère les données de l'opération depuis le début de la mise en place - de l'opération. A partir de la date de début de contrat, calcul du nombre - de mois entre la date de début de contrat et la date du jour. Puis - récupération des données de tous les mois - """ - nb_months = (date.today().year - self.date_start_contract.year) * 12 + ( - date.today().month - self.date_start_contract.month - ) - - start_date_it = self.date_start_contract - end_date_it = self.date_start_contract + relativedelta(months=1) - - for _i in range(nb_months): - self.curves( - start_date_it, end_date_it, usage_point_cons_ids, usage_point_prod_ids - ) - start_date_it = start_date_it + relativedelta(months=1) - end_date_it = start_date_it + relativedelta(months=1) - def perimeter(self): """ Récupère les données de l'opération concernant le périmètre: diff --git a/wizards/acc_operation_wizard.py b/wizards/acc_operation_wizard.py index e368b45db2f31df2f74013efbb7ec683279a372d..ab239b6f1d6f9858f02dc3a02d6bd722515ba50a 100644 --- a/wizards/acc_operation_wizard.py +++ b/wizards/acc_operation_wizard.py @@ -104,8 +104,8 @@ class AccOperationWizard(models.TransientModel): domain_extra_cdc = expression.OR([domain_cons_cdc, domain_prod_cdc]) domain_periods = expression.AND([domain_base, domain_extra]) - start_datetime = self.env["acc.operation"]._convert_time(self.start_date) - end_datetime = self.env["acc.operation"]._convert_time(self.end_date) + start_datetime = self.env["acc.enedis.cdc"]._convert_time(self.start_date) + end_datetime = self.env["acc.enedis.cdc"]._convert_time(self.end_date) domain_date = [ ("date_slot", ">=", start_datetime), ("date_slot", "<", end_datetime), @@ -150,8 +150,12 @@ class AccOperationWizard(models.TransientModel): "target": "new", } else: - self.operation_id.curves( - self.start_date, self.end_date, self.prm_cons_ids, self.prm_prod_ids + self.env["acc.enedis.cdc"]._curves( + self.operation_id, + self.start_date, + self.end_date, + self.prm_cons_ids, + self.prm_prod_ids, ) return {"type": "ir.actions.act_window_close"} @@ -189,7 +193,9 @@ class AccOperationWizard(models.TransientModel): "target": "new", } else: - self.operation_id.get_curves_all(self.prm_cons_ids, self.prm_prod_ids) + self.env["acc.enedis.cdc"]._get_curves_all( + self.operation_id, self.prm_cons_ids, self.prm_prod_ids + ) return {"type": "ir.actions.act_window_close"} # ------------------------------------------------------ diff --git a/wizards/acc_operation_wizard_confirm.py b/wizards/acc_operation_wizard_confirm.py index 03d49f2c44e385f8596f810484d48274a2a1d875..57177d2a8d8938d1c83be5bb512c2c38219b2aa0 100644 --- a/wizards/acc_operation_wizard_confirm.py +++ b/wizards/acc_operation_wizard_confirm.py @@ -61,12 +61,18 @@ class AccOperationWizardConfirm(models.TransientModel): # Appels API # Si date présente, appel pour la période if self.start_date: - self.operation_id.curves( - self.start_date, self.end_date, self.prm_cons_ids, self.prm_prod_ids + self.env["acc.enedis.cdc"]._curves( + self.operation_id, + self.start_date, + self.end_date, + self.prm_cons_ids, + self.prm_prod_ids, ) else: # Pas de date renseignée, récupération de toutes les données - self.operation_id.get_curves_all(self.prm_cons_ids, self.prm_prod_ids) + self.env["acc.enedis.cdc"]._get_curves_all( + self.operation_id, self.prm_cons_ids, self.prm_prod_ids + ) return {"type": "ir.actions.act_window_close"} # ------------------------------------------------------