瀏覽代碼

CHG Corrections mineures

olivier.massot 7 年之前
父節點
當前提交
a8c82ba0b1
共有 3 個文件被更改,包括 184 次插入191 次删除
  1. 63 0
      core/db.py
  2. 30 75
      core/model.py
  3. 91 116
      ctrl2analytique.py

+ 63 - 0
core/db.py

@@ -123,3 +123,66 @@ class SqlServerDb(CustomDb):
 #     pwd = ""
 #     def __init__(self, dbpath, **kwargs):
 #         CustomDb.__init__(self, dbq=dbpath, **kwargs)
+
+
+### SQL Helpers ###
+class SQLHelper():
+    """ Génère du code sql """
+    @classmethod
+    def _sql_format(cls, val):
+        """ pre-formatte une variable pour injection sql dans une base MS Access """
+        raise NotImplementedError()
+    @classmethod
+    def select(cls, where=""):
+        raise NotImplementedError()
+    @classmethod
+    def update(cls, tblname, data, where):
+        raise NotImplementedError()
+    @classmethod
+    def insert(cls, tblname, data):
+        raise NotImplementedError()
+    @classmethod
+    def delete(cls, tblname, where):
+        raise NotImplementedError()
+
+class AccessSqlHelper(SQLHelper):
+    """ SQL Helper pour MS Access """
+    @classmethod
+    def _sql_format(cls, val):
+        if val is None:
+            return "Null"
+        elif type(val) is int or type(val) is bool:
+            return "{}".format(val)
+        elif type(val) is str:
+            return "'{}'".format(val)
+        elif type(val) is datetime:
+            return "#{:%Y-%m-%d %H:%M:%S}#".format(val)
+        return "{}".format(val)
+
+    @classmethod
+    def select(cls, where=""):
+        sql = "SELECT * FROM {}".format(cls._tblname)
+        if where:
+            sql = "{} WHERE {}".format(sql, where)
+        return sql
+
+    @classmethod
+    def update(cls, tblname, data, where):
+        sql = "UPDATE {} SET {} WHERE {}".format(tblname,
+                                                  ",".join(["{} = {}".format(key, cls._sql_format(data[key])) for key in data]),
+                                                  " AND ".join(["{} = {}".format(key, cls._sql_format(where[key])) for key in where]))
+        return sql
+
+    @classmethod
+    def insert(cls, tblname, data):
+        sql = "INSERT INTO {} ({}) VALUES ({})".format(tblname,
+                                                       ",".join(data.keys()),
+                                                       ",".join([cls._sql_format(data[key]) for key in data]))
+        return sql
+
+    @classmethod
+    def delete(cls, tblname, where):
+        sql = "DELETE * FROM {} WHERE {}".format(tblname,
+                                                 " AND ".join(["{} = {}".format(key, cls._sql_format(where[key])) for key in where]))
+        return sql
+

+ 30 - 75
core/model.py

@@ -3,105 +3,60 @@
 
     @author: olivier.massot, févr. 2018
 '''
-from datetime import datetime
 import logging
 
-
 logger = logging.getLogger("model")
 
-def _sql_format(val):
-    """ pre-formatte une variable pour injection sql dans une base MS Access """
-    if val is None:
-        return "Null"
-    elif type(val) is int or type(val) is bool:
-        return "{}".format(val)
-    elif type(val) is str:
-        return "'{}'".format(val)
-    elif type(val) is datetime:
-        return "#{:%Y-%m-%d %H:%M:%S}#".format(val)
-    return "{}".format(val)
 
 class Model():
-    """ Modèle de données d'un objet
+    _mapping = {}
 
-    """
-    _cnn = None
-    _tblname = ""
-    _fields = []
-    _identityfields = []
+    @property
+    def _fields(self):
+        return list(self.__dict__.keys())
 
-    def __init__(self):
-        """ Génère un objet vide,
-        les propriétés de l'objet sont générées automatiquement à partir de la variable de classe _FIELDS"""
-        for fld in self._identityfields + self._fields:
-            setattr(self, fld, None)
+    @property
+    def data(self):
+        return self.__dict__
 
     def __repr__(self):
-        return "<{} => {}>".format(self.__class__.__name__, ",".join(["{}={}".format(field, getattr(self, field)) for field in self._identityfields + self._fields]))
+        return "<{} => {}>".format(self.__class__.__name__, ",".join(["{}={}".format(field, value) for field, value in self.__dict__.items()]))
 
     @classmethod
     def from_dict(cls, data):
         """ Retourne un objet à partir d'un dictionnaire de données """
-        facture = cls()
+        model = cls()
         for key, value in data.items():
-            setattr(facture, key, value)
-        return facture
-
-    ### Fonctions CSV ###
-    def to_csv(self):
-        """ Renvoie une chaine de caractère correspondant aux données de l'objet au format CSV
-        Séparateur = tabulation (car c'est un caractère interdit dans Access) """
-        return "\t".join([str(getattr(self, field)).replace("\t", " ") for field in self._identityfields + self._fields] + ["\n"])
+            setattr(model, key, value)
+        return model
 
     @classmethod
-    def from_csv(cls, line):
-        """ Retourne un objet Facture à partir d'une ligne de texte au format CSV
-        Séparateur = tabulation (car c'est un caractère interdit dans Access) """
-        return cls.from_dict(dict(zip(cls._identityfields + cls._fields, line.split("\t"))))
-
-    def dump(self, path):
+    def _map_type(cls, field, value):
+        try:
+            return cls._mapping[field](value)
+        except KeyError:
+            return value
+
+    # Fonctions CSV
+    def dump_to_csv(self, path):
         """ Ajoute les données du modèle au format CSV dans le fichier spécifié en paramètre.
         Créé le fichier s'il n'existe pas, avec une ligne d'en-tête """
         if not path.exists():
             logger.debug("Génère le fichier %s", path)
-            firstline = "\t".join(self._identityfields + self._fields + ["\n"])
+            firstline = "\t".join(self._fields + ["\n"])
             with open(path, 'w+') as f:
                 f.write(firstline)
 
         with open(path, 'a') as f:
-            f.write(self.to_csv())
-
-    ### Fonctions Access ###
-    @classmethod
-    def select(cls, where=""):
-        sql = "SELECT * FROM {}".format(cls._tblname)
-        if where:
-            sql = "{} WHERE {}".format(sql, where)
-        logger.debug(sql)
-        for row in cls._cnn.read(sql):
-            yield cls.from_dict({f: getattr(row, f) for f in cls._identityfields + cls._fields})
+            f.write("\t".join([str(getattr(self, field)).replace("\t", " ") for field in self._fields] + ["\n"]))
 
     @classmethod
-    def first(cls, where=""):
-        return next(cls.select(where))
-
-    def update(self):
-        sql = "UPDATE {} SET {} WHERE {}".format(self._tblname,
-                                                  ",".join(["{} = {}".format(f, _sql_format(getattr(self, f))) for f in self._fields]),
-                                                  " AND ".join(["{} = {}".format(k, _sql_format(getattr(self, k))) for k in self._identityfields]))
-        logger.debug(sql)
-        self._cnn.execute(sql)
-
-    def insert(self):
-        sql = "INSERT INTO {} ({}) VALUES ({})".format(self._tblname,
-                                                              ",".join(self._fields),
-                                                              ",".join([_sql_format(getattr(self, f)) for f in self._fields]))
-        logger.debug(sql)
-        print(sql)
-        self._cnn.execute(sql)
-
-    def delete(self):
-        sql = "DELETE * FROM {} WHERE {}".format(self._tblname,
-                                                 " AND ".join(["{} = {}".format(k, _sql_format(getattr(self, k))) for k in self._identityfields]))
-        logger.debug(sql)
-        self._cnn.execute(sql)
+    def load_csv(cls, path):
+        """ parcourt les lignes du fichier csv et renvoie chaque ligne sous forme d'un objet Model
+        ATTENTION: chaque propriété dont le type n'est pas précisé dans _mapping aura le type 'string'
+        """
+        with open(path) as f:
+            fields = next(f).split("\t")
+            for line in f:
+                data = {key: cls._map_type(key, value) for key, value in zip(fields, line.split("\t"))}
+                yield(cls.from_dict(data))

+ 91 - 116
ctrl2analytique.py

@@ -15,7 +15,8 @@ import sys
 from path import Path  # @UnusedImport
 
 from core import logconf
-from core.model import CsvModel, Model
+from core.db import AccessSqlHelper
+from core.model import Model
 from core.pde import ControlesDb, AnalytiqueDb, mk_workdir, CommunDb
 
 
@@ -26,36 +27,14 @@ logconf.start("ctrl2analytique", logging.DEBUG)
 # > Lancer le script /resources/test_ctrl2analytique.py pour reinitialiser les données de la base de test
 ##-----------------------------------------------
 
-# logger.warning("<<<<<<<<<<<<<<   Mode TEST   >>>>>>>>>>>>>>>>>")
-# ControlesDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\cg67Parc_data.mdb")
-# AnalytiqueDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Db_analytique.mdb")
-# CommunDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Commun_Data.mdb")
+logger.warning("<<<<<<<<<<<<<<   Mode TEST   >>>>>>>>>>>>>>>>>")
+ControlesDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\cg67Parc_data.mdb")
+AnalytiqueDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Db_analytique.mdb")
+CommunDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Commun_Data.mdb")
 
 ##-----------------------------------------------
 
 
-class Affaire(CsvModel):
-    """ Modèle de données d'une affaire Analytique """
-    _FIELDS = ["strLiaisonControle", "strMOeId", "strCommneId", "strLieux",
-               "strEntrepriseId", "strMOId", "dtmCommande", "Ref", "blnMarche",
-               "dblMarche", "intTypeContrat", "strCT", "strTypeId", "intCoefFG", "strSituation",
-               "lngChantierId", "bytCommandeId"]
-
-class Interv(CsvModel):
-    """ Modèle de données d'une intervention de contrôle réseaux """
-    _FIELDS = ["strEquipeId", "strEnginId", "strRapportId", "strTypeInterventionId",
-               "strCatégorieInterventionId", "dblquantite", "strunite", "dtmIntervention",
-               "dtmDureeIntervention", "strLiaisonControle", "strArticleId", "intPeriode",
-               "remarques", "strgrandeur1", "strgrandeur2", "strgrandeur3",
-               "strcaracteristique1", "strcaracteristique2", "strcaracteristique3",
-               "strunite1", "strunite2", "strunite3", "dtmImportation", "strTest", "LienAff",
-               "lngChantierId", "bytCommandeId", "bytIntervId"
-               ]
-
-class Tarification(Model):
-    """ Modèle de donnée d'une ligne de tarification """
-    pass
-
 # #########    INITIALISATION    ##########
 logger.info("Initialisation...")
 
@@ -83,6 +62,21 @@ for file in (affaires_file, intervs_file):
         logger.debug("Supprime le fichier %s", file)
         file.remove()
 
+
+class Affaire(Model):
+    """ Modèle de données d'une affaire Analytique """
+    pass
+
+class Interv(Model):
+    """ Modèle de données d'une intervention de contrôle réseaux """
+    pass
+
+class Tarification(Model):
+    """ Modèle de donnée d'une ligne de tarification """
+    pass
+
+sqlHelper = AccessSqlHelper
+
 def get_type_id(lngChantierId, bytCommandeId):
     """ Recupère le type de chantier.
     'ZP': Chantier de contrôle d'étanchéité
@@ -152,7 +146,7 @@ for data in controles_db.read(sql):
     affaire.bytCommandeId = data.bytCommandeId
 
     # Créé la ligne dans le fichier affaires.csv
-    affaire.dump(affaires_file)
+    affaire.dump_to_csv(affaires_file)
 
     compteur += 1
 
@@ -243,7 +237,7 @@ for data in controles_db.read(sql):
     interv.bytIntervId = data.bytIntervId
 
     # Créé la ligne dans le fichier intervs.csv
-    interv.dump(intervs_file)
+    interv.dump_to_csv(intervs_file)
 
     compteur += 1
 
@@ -313,7 +307,7 @@ for data in controles_db.read(sql):
     interv.bytIntervId = data.bytIntervId
 
     # Créé la ligne dans le fichier intervs.csv
-    interv.dump(intervs_file)
+    interv.dump_to_csv(intervs_file)
 
     compteur += 1
 
@@ -377,7 +371,7 @@ for data in controles_db.read(sql):
     interv.bytIntervId = data.bytIntervId
 
     # Créé la ligne dans le fichier intervs.csv
-    interv.dump(intervs_file)
+    interv.dump_to_csv(intervs_file)
 
     compteur += 1
 
@@ -398,73 +392,65 @@ errors = -1
 while errors:
     errors = []
 
-    with open(affaires_file) as f:
-        next(f)  # saute la première ligne
-
-        for line in f:
-            affaire = Affaire.from_csv(line)
-
-            prefix = "Affaire {}: ".format(affaire.strLiaisonControle)
-            if not affaire.strMOId:
-                errors.append(prefix + "MO manquant")
-            else:
-                if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOId)):
-                    errors.append(prefix + "Le MO {} n'existe pas dans tblTiers".format(affaire.strMOId))
-            if not affaire.strMOeId:
-                errors.append(prefix + "MOe manquant")
-            else:
-                if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOeId)):
-                    errors.append(prefix + "Le MOe {} n'existe pas dans tblTiers".format(affaire.strMOeId))
-            if not affaire.strEntrepriseId:
-                errors.append(prefix + "Entreprise manquante")
-            else:
-                if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strEntrepriseId)):
-                    errors.append(prefix + "L'entreprise {} n'existe pas dans tblTiers".format(affaire.strEntrepriseId))
-            if not affaire.strCommneId:
-                errors.append(prefix + "Commune manquante")
-            else:
-                if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strCommneId)):
-                    errors.append(prefix + "La commune {} n'existe pas dans tblTiers".format(affaire.strCommneId))
-            if not affaire.strTypeId:
-                errors.append(prefix + "Type d'affaire manquant")
-            if not affaire.dtmCommande:
-                errors.append(prefix + "Date de commande manquante")
-            if affaire.blnMarche == True and not affaire.intDevisId:
-                errors.append(prefix + "Numéro de devis manquant")
-
-            if analytique_db.exists("SELECT dblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(affaire.strLiaisonControle)):
-                errors.append(prefix + "Une affaire portant ce code existe déjà: {}".format(affaire.strLiaisonControle))
-
-    with open(intervs_file) as f:
-        next(f)  # saute la première ligne
-
-        for line in f:
-            interv = Interv.from_csv(line)
-
-            prefix = "Intervention {}: ".format(interv.strLiaisonControle)
-            if not interv.strEquipeId:
-                errors.append(prefix + "Equipe manquante")
-            if not interv.strEnginId:
-                errors.append(prefix + "Engin manquant")
-            if not interv.strRapportId:
-                errors.append(prefix + "Rapport manquant")
-            if not interv.strCatégorieInterventionId:
-                errors.append(prefix + "Catégorie de l'intervention manquante")
-            if not interv.strTypeInterventionId:
-                errors.append(prefix + "Type d'intervention manquant")
-            if not interv.dblquantite:
-                errors.append(prefix + "Quantité nulle")
-            if not interv.strunite:
-                errors.append(prefix + "Unité non renseignée")
-            if not interv.dtmIntervention:
-                errors.append(prefix + "Erreur : date d'intervention")
-            if not interv.dtmDureeIntervention:
-                errors.append(prefix + "Durée d'intervention nulle")
-            if not interv.strunite:
-                errors.append(prefix + "Unité non renseignée")
-
-            if not engin_existe(interv.strEnginId):
-                errors.append(prefix + "l'engin {} n'existe pas".format(interv.strEnginId))
+    for affaire in Affaire.load_csv(affaires_file):
+
+        prefix = "Affaire {}: ".format(affaire.strLiaisonControle)
+        if not affaire.strMOId:
+            errors.append(prefix + "MO manquant")
+        else:
+            if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOId)):
+                errors.append(prefix + "Le MO {} n'existe pas dans tblTiers".format(affaire.strMOId))
+        if not affaire.strMOeId:
+            errors.append(prefix + "MOe manquant")
+        else:
+            if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strMOeId)):
+                errors.append(prefix + "Le MOe {} n'existe pas dans tblTiers".format(affaire.strMOeId))
+        if not affaire.strEntrepriseId:
+            errors.append(prefix + "Entreprise manquante")
+        else:
+            if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strEntrepriseId)):
+                errors.append(prefix + "L'entreprise {} n'existe pas dans tblTiers".format(affaire.strEntrepriseId))
+        if not affaire.strCommneId:
+            errors.append(prefix + "Commune manquante")
+        else:
+            if not commun_db.exists("SELECT [lngTiersId] FROM tblTiers WHERE [lngTiersId]={}".format(affaire.strCommneId)):
+                errors.append(prefix + "La commune {} n'existe pas dans tblTiers".format(affaire.strCommneId))
+        if not affaire.strTypeId:
+            errors.append(prefix + "Type d'affaire manquant")
+        if not affaire.dtmCommande:
+            errors.append(prefix + "Date de commande manquante")
+        if affaire.blnMarche == True and not affaire.intDevisId:
+            errors.append(prefix + "Numéro de devis manquant")
+
+        if analytique_db.exists("SELECT dblAffaireId FROM tbl_Affaires WHERE [strLiaisonControle]='{}'".format(affaire.strLiaisonControle)):
+            errors.append(prefix + "Une affaire portant ce code existe déjà: {}".format(affaire.strLiaisonControle))
+
+    for interv in Interv.load_csv(intervs_file):
+
+        prefix = "Intervention {}: ".format(interv.strLiaisonControle)
+        if not interv.strEquipeId:
+            errors.append(prefix + "Equipe manquante")
+        if not interv.strEnginId:
+            errors.append(prefix + "Engin manquant")
+        if not interv.strRapportId:
+            errors.append(prefix + "Rapport manquant")
+        if not interv.strCatégorieInterventionId:
+            errors.append(prefix + "Catégorie de l'intervention manquante")
+        if not interv.strTypeInterventionId:
+            errors.append(prefix + "Type d'intervention manquant")
+        if not interv.dblquantite:
+            errors.append(prefix + "Quantité nulle")
+        if not interv.strunite:
+            errors.append(prefix + "Unité non renseignée")
+        if not interv.dtmIntervention:
+            errors.append(prefix + "Erreur : date d'intervention")
+        if not interv.dtmDureeIntervention:
+            errors.append(prefix + "Durée d'intervention nulle")
+        if not interv.strunite:
+            errors.append(prefix + "Unité non renseignée")
+
+        if not engin_existe(interv.strEnginId):
+            errors.append(prefix + "l'engin {} n'existe pas".format(interv.strEnginId))
 
     # *** 6- Interruption pour corection manuelle des données (si nécessaire)
     if errors:
@@ -484,34 +470,21 @@ while errors:
             sys.exit(1)
 
 
-
-
 # ########## MISE A JOUR DE LA BASE DE DONNEES ANALYTIQUE ##########
 
 # On charge en mémoire les affaires et les interventions
 logger.info("# Mise à jour de la base Analytique")
 logger.info("> NB: Les modifications ne seront appliquées à la base que si toutes les opérations se déroulent normalement.")
 
-affaires = []
-with open(affaires_file) as f:
-    next(f)  # saute la première ligne
-    for line in f:
-        affaire = Affaire.from_csv(line)
-        affaires.append(affaire)
-
-intervs = []
-with open(intervs_file) as f:
-    next(f)  # saute la première ligne
-    for line in f:
-        interv = Interv.from_csv(line)
-        intervs.append(interv)
-
+affaires = list(Affaire.load_csv(affaires_file))
+intervs = list(Interv.load_csv(intervs_file))
 
 # On insère les affaires, interventions dans Analytique, et on génère la ou les lignes de tarification associées
 
 for affaire in affaires:
 
     # insertion dans tbl_Affaires
+
     sql = """ INSERT INTO tbl_Affaires ( strMOId, strMOeId, strEntrepriseId, strCommneId, strLieux, strTypeId, dtmCommande, Ref,
                                         blnMarche, dblMarche, intTypeContrat, strCT, strLiaisonControle, blnTarification,
                                         blnAnalyse, strSituation, intCoefFG )
@@ -521,6 +494,7 @@ for affaire in affaires:
        """.format(affaire=affaire)
 
     analytique_db.execute(sql)
+
     logger.info("> Ajout de l'affaire: {}".format(affaire.strLiaisonControle))
 
 
@@ -535,11 +509,11 @@ for interv in intervs:
     interv.dblAffaireId = affaire.DblAffaireId
 
     sql = """INSERT INTO tbl_Intervention ( DblAffaireId, strEquipeId, strEnginId, strRapportId, strCatégorieInterventionId, strTypeInterventionId,
-              dblquantite, strunite, dtmIntervention, dtmDureeIntervention, strcaracteristique1, strgrandeur1, strunite1,
+              dblquantite, strunite, dtmIntervention, dtmDureeIntervention, dtmDureeInstallation, strcaracteristique1, strgrandeur1, strunite1,
               strcaracteristique2, strgrandeur2, strunite2, strcaracteristique3, strgrandeur3, strunite3, strLiaisonControle, strarticleId,
               intPeriode, blnTarification, blnAnalyse, blnFacturer, remarques, blnPeriode, dtmImportation, strTest )
       VALUES ({interv.dblAffaireId}, '{interv.strEquipeId}', '{interv.strEnginId}', '{interv.strRapportId}', '{interv.strCatégorieInterventionId}',
-          '{interv.strTypeInterventionId}', {interv.dblquantite}, '{interv.strunite}', #{interv.dtmIntervention}#, #{interv.dtmDureeIntervention}#,
+          '{interv.strTypeInterventionId}', {interv.dblquantite}, '{interv.strunite}', #{interv.dtmIntervention}#, #{interv.dtmDureeIntervention}#, #1899-12-30 00:00:00#,
           '{interv.strcaracteristique1}', '{interv.strgrandeur1}', '{interv.strunite1}', '{interv.strcaracteristique2}',
           '{interv.strgrandeur2}', '{interv.strunite2}', '{interv.strcaracteristique3}', '{interv.strgrandeur3}', '{interv.strunite3}', '{interv.strLiaisonControle}',
           '{interv.strArticleId}', {interv.intPeriode}, True, False, False, '{interv.remarques}',
@@ -663,6 +637,7 @@ for interv in analytique_db.read_all(sql):
     sql = """UPDATE tbl_Intervention SET tbl_Intervention.dtmDureeInstallation = #{}#
              WHERE (((tbl_Intervention.dblInterventionId)={}));""".format(date0 + tps_install, interv.dblInterventionId)
     analytique_db.execute(sql)
+
     logger.debug("* Mise à jour du temps d'installation de l'intervention {}".format(interv.dblInterventionId))
 analytique_db.commit()