qgis_sync_wincan.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. '''
  2. Met à jour les coordonnées des regards dans la base Wincan
  3. Les coordonnées sont issues des dossiers du répertoire 'itv_dir'
  4. Une fois traités, ces dossiers sont renommés 'XXXXXX' > 'I_XXXXXX'
  5. Plusieurs contrôles sont exécutés au cours de l'opération.
  6. Les erreurs suivantes sont bloquantes:
  7. - Le chantier correspondant n'existe pas dans Wincan
  8. - Le champ SI_Spare1 du tronçon analysé est NULL
  9. (ce qui signifie que la ligne n'a pas été traitée dans l'application Contrôles)
  10. - Regards présents dans le shapefile et pas dans Wincan
  11. - Regards présents dans Wincan et pas dans le shapefile
  12. - Regards en doublons dans le shapefile
  13. @author: olivier.massot, mai 2018
  14. '''
  15. import logging
  16. import re
  17. import sys
  18. from path import Path
  19. import shapefile
  20. from core import logconf
  21. from core.model import Sql
  22. from core.pde import WincanDb, ITV_DIR, QGisPoint
  23. logger = logging.getLogger("qgis_sync_wincan")
  24. logconf.start("qgis_sync_wincan", logging.DEBUG)
  25. IMPORT_DEPUIS = 24 # Ne cherche des données à importer que sur les X derniers mois (mettre à 0 pour ignorer)
  26. # # POUR TESTER, décommenter les lignes suivantes
  27. ##-----------------------------------------------
  28. # WincanDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\Wincan\parc_2007\DB\PARC_2007.mdb")
  29. # ITV_DIR = Path(__file__).parent / "resources" / "test_qgis_sync_wincan"
  30. # logger.handlers = [h for h in logger.handlers if (type(h) == logging.StreamHandler)]
  31. # logger.warning("<<<<<<<<<<<<<< Mode TEST >>>>>>>>>>>>>>>>>")
  32. ##-----------------------------------------------
  33. # Connexion à Wincan
  34. wincan_db = WincanDb(autocommit=False)
  35. # Regex pour parser les noms de repertoires
  36. rx = re.compile(r"^(I_)?(\d{5,6})(-S?\d{1,2})?[\s_]?(.*)$") # tous
  37. rxi = re.compile(r"^(\d{5,6})(-S?\d{1,2})?[\s_]?(.*)$") # non importés
  38. logger.info("Parcours des répertoires")
  39. a_importer = [subdir for subdir in ITV_DIR.dirs() if rxi.search(subdir.name)]
  40. if not a_importer:
  41. logger.info("Aucun nouveau dossier à importer")
  42. sys.exit()
  43. for chantier_dir in a_importer:
  44. # Ici, les noms de chantier sont de la forme 000000, 000000-0, ou 000000-S0
  45. dir_name = chantier_dir.name
  46. logger.info("# Traitement du répertoire: {}".format(chantier_dir.name))
  47. # check the existence of the chantier in WincanDb, and check if the chantier has been treated in Controles (SI_Spare1 = 1)
  48. if not wincan_db.exists(Sql.format("""SELECT SI_T.SI_AutoNumber
  49. FROM SI_T
  50. WHERE (((SI_T.SI_Spare1) is not null
  51. AND (SI_T.SI_JobNumber) Like {:text}))""", chantier_dir.name)):
  52. logger.error("Le chantier n'existe pas dans wincan, ou SI_Spare1 est null")
  53. continue
  54. shp_path = chantier_dir / "{}_p_Regards.shp".format(chantier_dir.name)
  55. logger.debug("> Lecture du fichier shapefile")
  56. sf = shapefile.Reader(shp_path)
  57. # should we check? :
  58. if sf.shapeType != 1:
  59. logger.error("Le fichier shapefile n'est pas de type POINT")
  60. continue
  61. sh_points = sf.shapeRecords()
  62. if not sh_points:
  63. logger.error("Le fichier shapefile ne contient aucune donnees")
  64. continue
  65. # Génère des objets 'Points' à partir des données du shapefile
  66. points = []
  67. for sh_point in sh_points:
  68. point = QGisPoint()
  69. point.number = sh_point.record[0]
  70. point.name = sh_point.record[1]
  71. point.x, point.y = sh_point.shape.points[0]
  72. points.append(point)
  73. del sf, sh_points
  74. logger.info("> Contrôle des données")
  75. shp_regards_name = set([point.number for point in points])
  76. # Vérifie l'absence de duplicats
  77. if len(shp_regards_name) != len(points):
  78. logger.error("Shapefile - Doublons dans les noms de regards")
  79. continue
  80. # Vérifie l'existence des regards dans WincanDb
  81. wincan_regards_name = set([]) # Le set garantit l'unicité des items
  82. wincan_regards_name |= {r.nom for r in wincan_db.read_all(Sql.format("""SELECT S_T.S_EndNode as nom
  83. FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
  84. WHERE (S_T.S_EndNode Not Like 'T%' And S_T.S_EndNode Not Like 'BP%'
  85. AND SI_T.SI_JobNumber Like {:text})""", chantier_dir.name))}
  86. wincan_regards_name |= {r.nom for r in wincan_db.read_all(Sql.format("""SELECT S_T.S_StartNode as nom
  87. FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
  88. WHERE (S_T.S_StartNode Not Like 'T%' And S_T.S_StartNode Not Like 'BP%'
  89. AND SI_T.SI_JobNumber Like {:text})""", chantier_dir.name))}
  90. for regard_name in shp_regards_name - wincan_regards_name:
  91. logger.error("Le regards suivant n'existe pas dans Wincan ({})".format(regard_name))
  92. for regard_name in wincan_regards_name - shp_regards_name:
  93. logger.error("Le regards suivant est absent du fichier shapefile ({})".format(regard_name))
  94. if shp_regards_name != wincan_regards_name:
  95. logger.info("Mise à jour annulée")
  96. continue
  97. # # Update the coordinates of the regards in WincanDb
  98. for point in points:
  99. q = wincan_db.execute(Sql.format("""UPDATE S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
  100. SET S_T.S_StartNodeCoord_X = {x}, S_T.S_StartNodeCoord_Y = {y}
  101. WHERE SI_T.SI_InspName Like {chantier:text} AND S_T.S_StartNode={num:text};
  102. """, chantier=chantier_dir.name + "%",
  103. num=point.number,
  104. x=point.x,
  105. y=point.y))
  106. q = wincan_db.execute(Sql.format("""UPDATE S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
  107. SET S_T.S_EndNodeCoord_X = {x}, S_T.S_EndNodeCoord_Y = {y}
  108. WHERE SI_T.SI_InspName Like {chantier:text} AND S_T.S_EndNode={num:text};
  109. """, chantier=chantier_dir.name + "%",
  110. num=point.number,
  111. x=point.x,
  112. y=point.y))
  113. wincan_db.commit()
  114. logger.info("> Mise à jour de la base Wincan")
  115. # rename the directory to mark it as imported ('I_')
  116. new_path = chantier_dir.parent / "I_{}".format(chantier_dir.name)
  117. logger.debug("> Renomme {} en {}".format(chantier_dir.name, new_path.name))
  118. try:
  119. chantier_dir.rename(new_path)
  120. except:
  121. logger.error("Impossible de renommer le dossier")