''' 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 # @UnusedImport 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) 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") # ITV_DIR = Path(__file__).parent / "resources" / "test_qgis_sync_wincan" # logger.handlers = [h for h in logger.handlers if (type(h) == logging.StreamHandler)] # logger.warning("<<<<<<<<<<<<<< Mode TEST >>>>>>>>>>>>>>>>>") ##----------------------------------------------- def main(): # Connexion à Wincan wincan_db = WincanDb(autocommit=False) # Regex pour parser les noms de repertoires 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") return for chantier_dir in a_importer: # Ici, les noms de chantier sont de la forme 000000, 000000-0, ou 000000-S0 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) if not wincan_db.exists(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)): 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) del sf, sh_points 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 |= {r.nom for r in wincan_db.read_all(Sql.format("""SELECT S_T.S_EndNode as nom 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 |= {r.nom for r in wincan_db.read_all(Sql.format("""SELECT S_T.S_StartNode as nom 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("Le regards suivant n'existe pas dans Wincan ({})".format(regard_name)) for regard_name in wincan_regards_name - shp_regards_name: logger.error("Le regards suivant est absent du fichier shapefile ({})".format(regard_name)) if shp_regards_name != wincan_regards_name: logger.info("Mise à jour annulée") continue # # Update the coordinates of the regards in WincanDb for point in points: 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)) 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 = chantier_dir.parent / "I_{}".format(chantier_dir.name) logger.debug("> Renomme {} en {}".format(chantier_dir.name, new_path.name)) try: chantier_dir.rename(new_path) except: logger.error("Impossible de renommer le dossier") if __name__ == "__main__": main() logger.info("-- Fin --")