qgis_sync_compactage.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. """
  2. Importe les données géographiques des essais de compactage depuis les fichiers Shapefile
  3. recus des exploitants vers la base Postgis de ControlesSig
  4. - Ajoute de nouveaux chantiers / points de compactage à la base POSTGIS
  5. - Les chantiers créés sont issus des dossiers du répertoire 'compactage_dir'
  6. - Une fois importés, ces dossiers sont renommés 'XXXXXX' > 'I_XXXXXX'
  7. - Les noms complets des chantiers sont récupérés dans la base Controles
  8. Pour lancer l'import de tous les chantiers (pas de prompt pour la selection), utilisez l'option --all
  9. Pour répondre automatiquement à la demande "Voulez-vous remplacer le chantier existant", utilisez les options --yes ou --no
  10. @author: olivier.massot, mai 2018
  11. """
  12. import logging
  13. import re
  14. import sys
  15. from path import Path
  16. import shapefile
  17. from core import logconf
  18. from core.pde import ControlesDb, CSigDb, COMPACTAGE_DIR, QGisChantier, \
  19. QGisPoint, SRID
  20. logger = logging.getLogger("qgis_sync_compactage")
  21. logconf.start("qgis_sync_compactage", logging.DEBUG)
  22. # # POUR TESTER, décommenter les lignes suivantes
  23. ##-----------------------------------------------
  24. # ControlesDb._path = Path(r"\\h2o\local\4-transversal\BDD\mdb_test\cg67Parc_data.mdb")
  25. # CSigDb.server = "TR-POSTGIS-02"
  26. # CSigDb.pwd = "Am5VOMkdFHU7WwrfVOs9"
  27. # COMPACTAGE_DIR = Path(__file__).parent / "resources" / "test_qgis_sync_compactage"
  28. # logger.handlers = [h for h in logger.handlers if (type(h) == logging.StreamHandler)]
  29. # logger.warning("<<<<<<<<<<<<<< Mode TEST >>>>>>>>>>>>>>>>>")
  30. ##-----------------------------------------------
  31. def main():
  32. # Connexion à ControlesSig (postgres)
  33. csig_db = CSigDb(autocommit=False)
  34. # Connexion à Controles
  35. controles_db = ControlesDb(autocommit=False)
  36. # Regex pour parser les noms de repertoires
  37. rxi = re.compile(r"^(\d{5,6})([-_]S?\d*)?[\s_]?(.*)$") # non importés
  38. a_importer = [subdir for subdir in COMPACTAGE_DIR.dirs() if rxi.search(subdir.name)]
  39. if a_importer and not "--all" in sys.argv:
  40. reponse = input("""Les chantiers suivants peuvent être importés.
  41. Tapez les numéros correpsondant aux chantiers à importer, séparés par une virgule (ou * pour tout importer)
  42. Ex: "1", "1,2,5", "*"
  43. {}""".format("\n".join(["{} - {}".format(i, item.name) for i, item in enumerate(a_importer)])))
  44. if reponse != "*":
  45. a_importer = [item for i, item in enumerate(a_importer) if str(i) in reponse.split(",")]
  46. if not a_importer:
  47. logger.info("Aucun nouveau dossier à importer")
  48. sys.exit()
  49. chantiers = []
  50. # ** Read the data in the shapefiles and store it in memory **
  51. for chantier_dir_path in a_importer:
  52. logger.info("Lecture de : %s", chantier_dir_path)
  53. # instanciate a new Chantier
  54. chantier = QGisChantier()
  55. chantier.dir_path = chantier_dir_path
  56. logger.debug("> parse path")
  57. parsed = rxi.search(chantier_dir_path.name)
  58. chantier.number = parsed.group(1)
  59. chantier.complement = parsed.group(2).replace("_", "-") if parsed.group(2) else ""
  60. logger.debug("> number: %s, compl: %s", chantier.number, chantier.complement)
  61. # query for name in ControlesDb
  62. logger.debug("> Query ControlesDb for chantier's name")
  63. row = controles_db.first("""SELECT tblChantiers.lngChantierId, tblCollectivites.strNom
  64. FROM tblChantiers INNER JOIN tblCollectivites
  65. ON tblChantiers.strCollectiviteId = tblCollectivites.strCollectiviteId
  66. WHERE lngChantierId = {lngChantierId};""".format(lngChantierId=chantier.number))
  67. chantier.name = "{}{} {}".format(chantier.number, chantier.complement, row.strNom)
  68. logger.debug("> {}".format(chantier.name))
  69. # importe le fichier shape dans une couche temp
  70. shp_path = chantier_dir_path / "{}_p_PointCompactage.shp".format(chantier_dir_path.name)
  71. logger.debug("Read the shapefile: %s", shp_path)
  72. sf = shapefile.Reader(shp_path)
  73. # should we check? :
  74. if sf.shapeType != 1:
  75. logger.error("Le fichier shapefile n'est pas de type POINT")
  76. sys.exit(1)
  77. sh_points = sf.shapeRecords()
  78. if not sh_points:
  79. logger.error("Le fichier shapefile ne contient aucune donnees")
  80. return
  81. chantier.points = []
  82. for sh_point in sh_points:
  83. # create the Point instance
  84. point = QGisPoint()
  85. point.number = sh_point.record[0]
  86. point.name = sh_point.record[1]
  87. point.x, point.y = sh_point.shape.points[0]
  88. logger.debug("> {}".format(point))
  89. chantier.points.append(point)
  90. del sf, sh_points
  91. # compute the chantier's rect coordinates
  92. logger.debug("Compute the chantier's rect coordinates")
  93. chantier.x0 = min([point.x for point in chantier.points]) - 5
  94. chantier.x1 = max([point.x for point in chantier.points]) + 5
  95. chantier.y0 = min([point.y for point in chantier.points]) - 5
  96. chantier.y1 = max([point.y for point in chantier.points]) + 5
  97. logger.debug("> ({}, {}, {}, {})".format(chantier.x0, chantier.x1, chantier.y0, chantier.y1))
  98. chantiers.append(chantier)
  99. # ** Insère les chantiers dans la base **
  100. for chantier in chantiers:
  101. logger.debug("** Chantier {}: {} points".format(chantier.name, len(chantier.points)))
  102. # Contrôle si un chantier de compactage portant ce nom n'existe pas déjà dans la base
  103. qry = csig_db.read(u"SELECT id FROM t_chantiers WHERE nom = '{}' AND id_type_chantier=2;".format(chantier.name))
  104. cancelled = False
  105. for row in qry:
  106. logger.warning("Un chantier de compactage portant ce nom existe déjà '{}' (id {})".format(chantier.name, row.id))
  107. if not "--no" in sys.argv and ("--yes" in sys.argv or input("Voulez-vous le remplacer? (o/n)") == "o"):
  108. # delete the old chantier
  109. csig_db.execute("""DELETE FROM t_points_compactage
  110. WHERE id_chantier = {};""".format(row.id))
  111. csig_db.execute("""DELETE FROM t_chantiers
  112. WHERE id = {};""".format(row.id))
  113. logger.info("> L'ancien chantier a été supprimé".format(chantier.name, row.id))
  114. else:
  115. cancelled = True
  116. break
  117. if cancelled:
  118. logger.warning("Import du chantier annulé")
  119. continue
  120. # Créé le chantier
  121. q = csig_db.first("""INSERT INTO t_chantiers(id_type_chantier, numero, nom, geom, archive)
  122. VALUES ({chantier_type}, {number}, '{name}', {geom}, {archive})
  123. RETURNING id;
  124. """.format(
  125. chantier_type=2,
  126. number=chantier.number,
  127. name=chantier.name,
  128. geom="ST_GeomFromText('POLYGON(({x0} {y0}, \
  129. {x0} {y1}, {x1} {y1}, {x1} {y0}, {x0} {y0}))', {srid})".format(x0=chantier.x0,
  130. x1=chantier.x1,
  131. y0=chantier.y0,
  132. y1=chantier.y1,
  133. srid=SRID),
  134. archive="FALSE"
  135. )
  136. )
  137. # get its postgis ID
  138. logger.debug("Getting newly created ID")
  139. chantier.pgid = q.id
  140. logger.debug("> {}".format(chantier.pgid))
  141. q = None
  142. # create the points
  143. for point in chantier.points:
  144. csig_db.execute("""INSERT INTO t_points_compactage(numero, nom, id_chantier, geom, archive)
  145. VALUES ({number}, '{name}', {chantier_id}, ST_GeomFromText('POINT({x} {y})', {srid}), False);
  146. """.format(
  147. number=point.number,
  148. name=point.name,
  149. chantier_id=chantier.pgid,
  150. x=point.x,
  151. y=point.y,
  152. srid=SRID
  153. )
  154. )
  155. csig_db.commit()
  156. # rename the directory to mark it as imported ('I_')
  157. new_path = r"{}\I_{}".format(chantier.dir_path.parent, chantier.dir_path.name)
  158. logger.debug("Rename {} to {}".format(chantier.dir_path, new_path))
  159. try:
  160. chantier.dir_path.rename(new_path)
  161. except:
  162. logger.error("Impossible de renommer le dossier")
  163. logger.info("Le chantier %s a été importé", chantier.name)
  164. csig_db.close()
  165. controles_db.close()
  166. if __name__ == "__main__":
  167. main()
  168. logger.info("-- Fin --")