qgis_sync_wincan.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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. logger.info("> Contrôle des données")
  74. shp_regards_name = set([point.number for point in points])
  75. # Vérifie l'absence de duplicats
  76. if len(shp_regards_name) != len(points):
  77. logger.error("Shapefile - Doublons dans les noms de regards")
  78. continue
  79. # Vérifie l'existence des regards dans WincanDb
  80. wincan_regards_name = set([]) # Le set garantit l'unicité des items
  81. wincan_regards_name |= {r.nom for r in wincan_db.read_all(Sql.format("""SELECT S_T.S_EndNode as nom
  82. FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
  83. WHERE (S_T.S_EndNode Not Like 'T%' And S_T.S_EndNode Not Like 'BP%'
  84. AND SI_T.SI_JobNumber Like {:text})""", chantier_dir.name))}
  85. wincan_regards_name |= {r.nom for r in wincan_db.read_all(Sql.format("""SELECT S_T.S_StartNode as nom
  86. FROM S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
  87. WHERE (S_T.S_StartNode Not Like 'T%' And S_T.S_StartNode Not Like 'BP%'
  88. AND SI_T.SI_JobNumber Like {:text})""", chantier_dir.name))}
  89. for regard_name in shp_regards_name - wincan_regards_name:
  90. logger.error("Le regards suivant n'existe pas dans Wincan ({})".format(regard_name))
  91. for regard_name in wincan_regards_name - shp_regards_name:
  92. logger.error("Le regards suivant est absent du fichier shapefile ({})".format(regard_name))
  93. if shp_regards_name != wincan_regards_name:
  94. logger.info("Mise à jour annulée")
  95. continue
  96. # # Update the coordinates of the regards in WincanDb
  97. for point in points:
  98. q = wincan_db.execute(Sql.format("""UPDATE S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
  99. SET S_T.S_StartNodeCoord_X = {x}, S_T.S_StartNodeCoord_Y = {y}
  100. WHERE SI_T.SI_InspName Like {chantier:text} AND S_T.S_StartNode={num:text};
  101. """, chantier=chantier_dir.name + "%",
  102. num=point.number,
  103. x=point.x,
  104. y=point.y))
  105. q = wincan_db.execute(Sql.format("""UPDATE S_T INNER JOIN SI_T ON S_T.S_ID = SI_T.SI_Section_ID
  106. SET S_T.S_EndNodeCoord_X = {x}, S_T.S_EndNodeCoord_Y = {y}
  107. WHERE SI_T.SI_InspName Like {chantier:text} AND S_T.S_EndNode={num:text};
  108. """, chantier=chantier_dir.name + "%",
  109. num=point.number,
  110. x=point.x,
  111. y=point.y))
  112. wincan_db.commit()
  113. logger.info("> Mise à jour de la base Wincan")
  114. # rename the directory to mark it as imported ('I_')
  115. new_path = chantier_dir.parent / "I_{}".format(chantier_dir.name)
  116. logger.debug("> Renomme {} en {}".format(chantier_dir.name, new_path.name))
  117. try:
  118. chantier_dir.rename(new_path)
  119. except:
  120. logger.error("Impossible de renommer le dossier")