Browse Source

script ed synchro wincan complet (pas testé)

olivier.massot 7 years ago
parent
commit
3c3d836f6d
4 changed files with 176 additions and 5 deletions
  1. 0 1
      core/db.py
  2. 4 0
      logging.yaml
  3. 12 4
      qgis_sync_compactage.py
  4. 160 0
      qgis_sync_wincan.py

+ 0 - 1
core/db.py

@@ -36,7 +36,6 @@ class CustomDb(pypyodbc.Connection):
 
     def read(self, sql, *args):
         """ yield rows as NamedTupleRow """
-#         print(sql)
         cursor = self.execute(sql)
         row = cursor.fetchone()
         fieldnames = [(column[0] if column[0].isidentifier() else "field_{}".format(i)) for i, column in enumerate(cursor.description)]

+ 4 - 0
logging.yaml

@@ -51,6 +51,10 @@ loggers:
         level: DEBUG
         handlers: [console, file, mail]
         propagate: no
+    qgis_sync_wincan:
+        level: DEBUG
+        handlers: [console, file, mail]
+        propagate: no
     ctrl2analytique:
         level: DEBUG
         handlers: [console, file, mail]

+ 12 - 4
qgis_sync_compactage.py

@@ -25,11 +25,13 @@ from core.sqlformatter import SqlFormatter
 
 logger = logging.getLogger("qgis_sync_compactage")
 logconf.start("qgis_sync_compactage", logging.DEBUG)
+debug = False
 
 # # POUR TESTER, décommenter les lignes suivantes
 ##-----------------------------------------------
 
 ControlesDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\cg67Parc_data.mdb")
+debug = True
 logger.handlers = [h for h in logger.handlers if (type(h) == logging.StreamHandler)]
 logger.warning("<<<<<<<<<<<<<<   Mode TEST   >>>>>>>>>>>>>>>>>")
 
@@ -44,12 +46,13 @@ csig_db = CSigDb(autocommit=False)
 controles_db = ControlesDb(autocommit=False)
 
 # Regex pour parser les noms de repertoires
-rx = re.compile(r"^(I_)?(\d{5,6})(-S?\d{1,2})?[\s_]?(.*)$")
+rx = re.compile(r"^(I_)?(\d{5,6})(-S?\d{1,2})?[\s_]?(.*)$")  # tous
+rxi = re.compile(r"^(\d{5,6})(-S?\d{1,2})?[\s_]?(.*)$")  # non importés
 
-a_importer = [subdir for subdir in COMPACTAGE_DIR.dirs() if rx.search(subdir.name) and not subdir.name[:2] == "I_"]
+a_importer = [subdir for subdir in COMPACTAGE_DIR.dirs() if rxi.search(subdir.name)]
 
 if a_importer:
-    # !!! Selection des chantiers à importer
+    # TODO: Selection des chantiers à importer
     pass
 
 if not a_importer:
@@ -183,7 +186,12 @@ for chantier in chantiers:
     new_path = r"{}\I_{}".format(chantier.dir_path.parent, chantier.dir_path.name)
     logger.debug("Rename {} to {}".format(chantier.dir_path, new_path))
 
-    chantier.dir_path.rename(new_path)
+    if not debug:
+        try:
+            chantier.dir_path.rename(new_path)
+        except:
+            logger.error("Impossible de renommer le dossier")
+
 
     logger.info("Chantier importé")
 

+ 160 - 0
qgis_sync_wincan.py

@@ -0,0 +1,160 @@
+'''
+
+    Met à jour les coordonnées des regards dans la base Wincan
+
+    Les coordonnées sont issues des dossiers du répertoire 'itv_dir'
+    Une fois traités, ces dossiers sont renommés 'XXXXXX' > 'I_XXXXXX'
+
+    Plusieurs contrôles sont exécutés au cours de l'opération.
+    Les erreurs suivantes sont bloquantes:
+    - Le chantier correspondant n'existe pas dans Wincan
+    - Le champ SI_Spare1 du tronçon analysé est NULL
+    (ce qui signifie que la ligne n'a pas été traitée dans l'application Contrôles)
+    - Regards présents dans le shapefile et pas dans Wincan
+    - Regards présents dans Wincan et pas dans le shapefile
+    - Regards en doublons dans le shapefile
+
+    @author: olivier.massot, mai 2018
+'''
+import logging
+import re
+import sys
+
+from path import Path
+import shapefile
+
+from core import logconf
+from core.model import Sql
+from core.pde import WincanDb, ITV_DIR, QGisPoint
+
+
+logger = logging.getLogger("qgis_sync_wincan")
+logconf.start("qgis_sync_wincan", logging.DEBUG)
+debug = False
+
+IMPORT_DEPUIS = 24  # Ne cherche des données à importer que sur les X derniers mois (mettre à 0 pour ignorer)
+
+
+# # POUR TESTER, décommenter les lignes suivantes
+##-----------------------------------------------
+
+WincanDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Wincan\parc_2007\DB\PARC_2007.mdb")
+debug = True
+logger.handlers = [h for h in logger.handlers if (type(h) == logging.StreamHandler)]
+logger.warning("<<<<<<<<<<<<<<   Mode TEST   >>>>>>>>>>>>>>>>>")
+
+##-----------------------------------------------
+
+# Connexion à Wincan
+wincan_db = WincanDb(autocommit=False)
+
+# Regex pour parser les noms de repertoires
+rx = re.compile(r"^(I_)?(\d{5,6})(-S?\d{1,2})?[\s_]?(.*)$")  # tous
+rxi = re.compile(r"^(\d{5,6})(-S?\d{1,2})?[\s_]?(.*)$")  # non importés
+
+
+logger.info("Parcours des répertoires")
+a_importer = [subdir for subdir in ITV_DIR.dirs() if rxi.search(subdir.name)]
+
+if not a_importer:
+    logger.info("Aucun nouveau dossier à importer")
+    sys.exit()
+
+
+for chantier_dir in a_importer:
+    # Ici, les noms de chantier sont de la forme 000000, 000000-0, ou 000000-S0
+    dir_name = chantier_dir.name
+    logger.info("Traitement du répertoire: {}".format(chantier_dir.name))
+
+    # check the existence of the chantier in WincanDb, and check if the chantier has been treated in Controles (SI_Spare1 = 1)
+    query = wincan_db.exec_(Sql.format("""SELECT SI_T.SI_AutoNumber
+                                     FROM SI_T
+                                     WHERE (((SI_T.SI_Spare1) is not null
+                                     AND (SI_T.SI_JobNumber) Like {:text}))""", chantier_dir.name))
+    if not query.first():
+        logger.error("Le chantier n'existe pas dans wincan, ou SI_Spare1 est null")
+        continue
+
+    shp_path = chantier_dir / "{}_p_Regards.shp".format(chantier_dir.name)
+
+    logger.debug("> Lecture du fichier shapefile")
+    sf = shapefile.Reader(shp_path)
+
+    # should we check? :
+    if sf.shapeType != 1:
+        logger.error("Le fichier shapefile n'est pas de type POINT")
+        continue
+
+    sh_points = sf.shapeRecords()
+    if not sh_points:
+        logger.error("Le fichier shapefile ne contient aucune donnees")
+        continue
+
+    # Génère des objets 'Points' à partir des données du shapefile
+    points = []
+    for sh_point in sh_points:
+        point = QGisPoint()
+        point.number = sh_point.record[0]
+        point.name = sh_point.record[1]
+        point.x, point.y = sh_point.shape.points[0]
+        points.append(point)
+
+
+    logger.info("> Contrôle des données")
+    shp_regards_name = set([point.number for point in points])
+
+    # Vérifie l'absence de duplicats
+    if len(shp_regards_name) != len(points):
+        logger.error("Shapefile - Doublons dans les noms de regards")
+        continue
+
+    # Vérifie l'existence des regards dans WincanDb
+    wincan_regards_name = set([])  # Le set garantit l'unicité des items
+
+    wincan_regards_name |= wincan_db.readall(Sql.format("""SELECT S_T.S_EndNode
+                                                            FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
+                                                            WHERE (S_T.S_EndNode Not Like 'T%' And S_T.S_EndNode Not Like 'BP%'
+                                                            AND SI_T.SI_JobNumber Like {:text})""", chantier_dir.name))
+
+    wincan_regards_name |= wincan_db.readall(Sql.format("""SELECT S_T.S_StartNode
+                                                            FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
+                                                            WHERE (S_T.S_StartNode Not Like 'T%' And S_T.S_StartNode Not Like 'BP%'
+                                                            AND SI_T.SI_JobNumber Like {:text})""", chantier_dir.name))
+
+    for regard_name in shp_regards_name - wincan_regards_name:
+        logger.error("Regards manquant dans Wincan ({})".format(regard_name))
+    for regard_name in wincan_regards_name - shp_regards_name:
+        logger.error("Regards manquant dans le shapefile ({})".format(regard_name))
+    if shp_regards_name != wincan_regards_name:
+        continue
+
+    # # Update the coordinates of the regards in WincanDb
+
+    for point in points:
+        q = wincan_db.execute(Sql.format("""UPDATE S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
+                                            SET S_T.S_StartNodeCoord_X = {x}, S_T.S_StartNodeCoord_Y = {y}
+                                            WHERE SI_T.SI_InspName Like {chantier:text} AND S_T.S_StartNode={num:text};
+                                            """, chantier=chantier_dir.name + "%",
+                                                 num=point.number,
+                                                 x=point.x,
+                                                 y=point.y))
+        q = wincan_db.execute(Sql.format("""UPDATE S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
+                                            SET S_T.S_EndNodeCoord_X = {x}, S_T.S_EndNodeCoord_Y = {y}
+                                            WHERE SI_T.SI_InspName Like {chantier:text} AND S_T.S_EndNode={num:text};
+                                            """, chantier=chantier_dir.name + "%",
+                                                 num=point.number,
+                                                 x=point.x,
+                                                 y=point.y))
+        wincan_db.commit()
+
+    logger.info("> Mise à jour de la base Wincan")
+
+    # rename the directory to mark it as imported ('I_')
+    new_path = r"{}\I_{}".format(chantier_dir.parent, chantier_dir.name)
+    logger.debug("> Renomme {} en {}".format(chantier_dir.name, new_path.name))
+
+    if not debug:
+        try:
+            chantier_dir.rename(new_path)
+        except:
+            logger.error("Impossible de renommer le dossier")