validation.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  1. '''
  2. @author: olivier.massot, sept. 2018
  3. '''
  4. import time
  5. import zipfile
  6. from path import Path, TempDir
  7. from core import gis_
  8. from core.cerberus_extend import CerberusErrorHandler, \
  9. _translate_messages, ExtendedValidator
  10. from schemas.common import SRID
  11. class ValidatorInterruption(BaseException):
  12. pass
  13. class Checkpoint():
  14. def __init__(self, name, valid=True):
  15. self.name = name
  16. self.valid = valid
  17. ########### MODELES ################
  18. class BaseModel():
  19. filename = ""
  20. pk = ""
  21. schema = {}
  22. def __init__(self, **kwargs):
  23. self.__dict__.update(kwargs)
  24. class BaseGeoModel(gis_.Feature):
  25. filename = ""
  26. pk = ""
  27. geom_type = 0
  28. bounding_box = (0,0,1,1)
  29. schema = {}
  30. def __init__(self, feature):
  31. self.__dict__.update(feature.__dict__)
  32. ########### ERREURS DE VALIDATION ################
  33. VALIDATION_ERROR_LEVELS = {10: "MINEURE", 20: "AVERTISSEMENT", 30: "ERREUR", 40: "CRITIQUE"}
  34. MINOR = 10
  35. WARNING = 20
  36. ERROR = 30
  37. CRITICAL = 40
  38. class BaseValidationError():
  39. order_ = 0
  40. name = "Erreur"
  41. level = ERROR
  42. help = ""
  43. def __init__(self, message, filename="", field=""):
  44. self.message = message
  45. self.filename = filename
  46. self.field = field
  47. def __repr__(self):
  48. return " - ".join(filter(None, [self.name, self.filename, self.field, self.message]))
  49. # Erreurs dans le chargement des fichiers
  50. class InputError(BaseValidationError):
  51. order_ = 0
  52. level = CRITICAL
  53. name = "Erreur de chargement"
  54. class MissingFile(InputError):
  55. order_ = 1
  56. name = "Fichier Manquant"
  57. class UnreadableFile(InputError):
  58. order_ = 2
  59. name = "Fichier Illisible"
  60. class WrongSrid(InputError):
  61. order_ = 3
  62. name = "Mauvais SRID"
  63. ### Erreurs dans la structure des données
  64. class StructureError(BaseValidationError):
  65. order_ = 10
  66. name = "Erreur de structure"
  67. level = ERROR
  68. class GeomTypeError(StructureError):
  69. order_ = 12
  70. name = "Type de géométrie invalide"
  71. level = CRITICAL
  72. class BoundingBoxError(StructureError):
  73. order_ = 11
  74. name = "Coordonnées hors de la zone autorisée"
  75. class InvalidGeometry(StructureError):
  76. order_ = 13
  77. name = "Géométrie invalide"
  78. class DataError(StructureError):
  79. order_ = 14
  80. name = "Erreur de format"
  81. # Erreurs dans le contenu, erreurs métiers
  82. class TechnicalValidationError(BaseValidationError):
  83. order_ = 20
  84. level = ERROR
  85. name = "Erreur technique"
  86. class UniqueError(TechnicalValidationError):
  87. order_ = 21
  88. name = "Doublons dans le champs"
  89. class RelationError(TechnicalValidationError):
  90. order_ = 22
  91. level = CRITICAL
  92. name = "Un objet lié n'existe pas"
  93. class DuplicatedGeom(TechnicalValidationError):
  94. order_ = 23
  95. name = "Doublon graphique"
  96. class MissingItem(TechnicalValidationError):
  97. order_ = 24
  98. name = "Elément manquant"
  99. class DimensionError(TechnicalValidationError):
  100. order_ = 25
  101. name = "Elément de dimension"
  102. class PositionError(TechnicalValidationError):
  103. order_ = 26
  104. name = "Erreur de positionnement"
  105. ########### VALIDATION ################
  106. class BaseValidator():
  107. schema_name = ""
  108. models = {}
  109. dataset = {}
  110. def __init__(self):
  111. self.valid = True
  112. self.checkpoints = []
  113. self.errors = []
  114. self._current_checkpoint_valid = True
  115. self.dt = 0
  116. def checkpoint(self, title):
  117. self.checkpoints.append(Checkpoint(title, self._current_checkpoint_valid))
  118. self._current_checkpoint_valid = True
  119. if self.errors:
  120. self.valid = False
  121. if self.critical_happened():
  122. raise ValidatorInterruption()
  123. def critical_happened(self):
  124. return any([err.level == CRITICAL for err in self.errors])
  125. def log_error(self, validation_error):
  126. self._current_checkpoint_valid = False
  127. self.errors.append(validation_error)
  128. @classmethod
  129. def submit(cls, subject):
  130. """ prends un dossier ou une archive en entrée et vérifie son contenu """
  131. subject = Path(subject)
  132. if subject.isfile():
  133. with TempDir() as dirname:
  134. zip_ref = zipfile.ZipFile(subject, 'r')
  135. zip_ref.extractall(dirname)
  136. zip_ref.close()
  137. if Path(dirname / subject.stem).isdir(): # cas où l'archive contient un dossier qui lui-même contient les fichiers
  138. dirname /= subject.stem
  139. return cls._submit_folder(dirname)
  140. elif subject.isdir():
  141. return cls._submit_folder(subject)
  142. else:
  143. raise FileNotFoundError(f"Impossible de trouver le fichier ou répertoire: {subject}")
  144. @classmethod
  145. def _submit_folder(cls, folder):
  146. validator = cls()
  147. t0 = time.time()
  148. try:
  149. validator.validate(folder)
  150. except ValidatorInterruption:
  151. pass
  152. validator.dt = time.time() - t0
  153. report = validator.build_report(validator.schema_name, folder.name)
  154. return report
  155. def validate(self, folder):
  156. # Chargement des données en mémoire
  157. self._load_files(folder)
  158. self.checkpoint("Chargement des données")
  159. # Controle la structure des données (champs, formats et types)
  160. self._structure_validation()
  161. self.checkpoint("Contrôle de la structure des données")
  162. # Validation technique
  163. try:
  164. self._technical_validation()
  165. self.checkpoint("Validation Métier")
  166. except ValidatorInterruption:
  167. raise
  168. except:
  169. self.checkpoint("Validation Métier [interrompu]")
  170. def _load_files(self, folder):
  171. """ Charge les données du fichier et les associe à un modèle.
  172. Attention: pas de contrôle de format ou de validité à ce niveau! """
  173. raise NotImplementedError()
  174. def _structure_validation(self):
  175. for model in self.models:
  176. v = ExtendedValidator(model.schema, purge_unknown=True, error_handler=CerberusErrorHandler, require_all=True)
  177. for item in self.dataset[model]:
  178. v.validate(item.__dict__)
  179. for field, verrors in v.errors.items():
  180. for err in verrors:
  181. self.log_error(DataError(_translate_messages(err), filename=model.filename, field=field))
  182. @classmethod
  183. def _technical_validation(cls):
  184. raise NotImplementedError()
  185. def build_report(self, schema, filename):
  186. report = {}
  187. report["schema"] = schema
  188. report["filename"] = filename
  189. report["exec_time"] = "{:.3g} s.".format(self.dt)
  190. report["checkpoints"] = [{"name": chk.name, "valid": chk.valid} for chk in self.checkpoints]
  191. report["errors"] = {}
  192. for err in self.errors:
  193. if not err.name in report["errors"]:
  194. report["errors"][err.name] = {"help": err.help, "order_": err.order_, "list": []}
  195. err_report = {"filename": err.filename or "-",
  196. "field": err.field or "-",
  197. "message": err.message}
  198. if err_report not in report["errors"][err.name]["list"]:
  199. report["errors"][err.name]["list"].append(err_report)
  200. return report
  201. class NetgeoValidator(BaseValidator):
  202. def _load_files(self, folder):
  203. for model in self.models:
  204. filename = model.filename
  205. path_ = Path(folder) / filename
  206. if not path_.isfile():
  207. self.log_error(MissingFile("Fichier manquant: '{}'".format(filename)))
  208. continue
  209. self.dataset[model] = []
  210. try:
  211. ds = gis_.Datasource(path_)
  212. layer = ds.layer
  213. if layer.srid != SRID:
  214. self.log_error(WrongSrid("Mauvaise projection: {} (attendu: {})".format(layer.srid, SRID)))
  215. for feature in layer:
  216. item = model(feature)
  217. self.dataset[model].append(item)
  218. except IOError:
  219. self.log_error(UnreadableFile("Fichier illisible: {}".format(path_.name)))
  220. def _structure_validation(self):
  221. for model in self.models:
  222. v = ExtendedValidator(model.schema, purge_unknown=True, error_handler=CerberusErrorHandler, require_all=True)
  223. xmin, ymin, xmax, ymax = model.bounding_box
  224. for item in self.dataset[model]:
  225. # geom type
  226. if item.geom_type != model.geom_type:
  227. self.log_error(GeomTypeError("Type de géométrie invalide: {} (attendu: {})".format(item.geom_name, gis_.GEOM_NAMES[model.geom_type]), filename=model.filename, field="geom"))
  228. # bounding box
  229. x1, y1, x2, y2 = item.bounding_box
  230. if any(x < xmin or x > xmax for x in (x1, x2)) or \
  231. any(y < ymin or y > ymax for y in (y1, y2)):
  232. self.log_error(BoundingBoxError("Situé hors de l'emprise autorisée", filename=model.filename, field="geom"))
  233. v.validate(item.__dict__)
  234. for field, verrors in v.errors.items():
  235. for err in verrors:
  236. self.log_error(DataError(_translate_messages(err), filename=model.filename, field=field))
  237. # def _technical_validation(self):
  238. #
  239. # # construction des index
  240. # arteres = self.dataset[Artere]
  241. # cables = self.dataset[Cable]
  242. # tranchees = self.dataset[Tranchee]
  243. #
  244. # noeuds = {}
  245. # for noeud in self.dataset[Noeud]:
  246. # if not noeud.NO_NOM in noeuds:
  247. # noeuds[noeud.NO_NOM] = noeud
  248. # else:
  249. # self.log_error(UniqueError("Doublons dans le champs: {}".format(noeud), filename=Noeud.filename, field="NO_NOM"))
  250. #
  251. # equipements = {}
  252. # for equipement in self.dataset[Equipement]:
  253. # if not equipement.EQ_NOM in equipements:
  254. # equipements[equipement.EQ_NOM] = equipement
  255. # else:
  256. # self.log_error(UniqueError("Doublons dans le champs: {}".format(equipement), filename=Equipement.filename, field="EQ_NOM"))
  257. #
  258. # zapbos = {}
  259. # for zapbo in self.dataset[Zapbo]:
  260. # if not zapbo.ID_ZAPBO in zapbos:
  261. # zapbos[zapbo.ID_ZAPBO] = zapbo
  262. # else:
  263. # self.log_error(UniqueError("Doublons dans le champs: {}".format(zapbo), filename=Zapbo.filename, field="ID_ZAPBO"))
  264. #
  265. # # contrôle de la validité des géométries
  266. # for artere in arteres:
  267. # if not artere.valid:
  268. # self.log_error(InvalidGeometry("Géométrie invalide: {}".format(artere), filename=Artere.filename, field="geom"))
  269. # for tranchee in tranchees:
  270. # if not tranchee.valid:
  271. # self.log_error(InvalidGeometry("Géométrie invalide: {}".format(tranchee), filename=Tranchee.filename, field="geom"))
  272. # for cable in cables:
  273. # if not "baguette" in cable.CA_COMMENT.lower() and not cable.valid:
  274. # self.log_error(InvalidGeometry("Géométrie invalide: {}".format(cable), filename=Cable.filename, field="geom"))
  275. # for noeud in noeuds.values():
  276. # if not noeud.valid:
  277. # self.log_error(InvalidGeometry("Géométrie invalide: {}".format(noeud), filename=Noeud.filename, field="geom"))
  278. # for equipement in equipements.values():
  279. # if not equipement.valid:
  280. # self.log_error(InvalidGeometry("Géométrie invalide: {}".format(equipement), filename=Equipement.filename, field="geom"))
  281. # for zapbo in zapbos.values():
  282. # if not zapbo.valid:
  283. # self.log_error(InvalidGeometry("Géométrie invalide: {}".format(zapbo), filename=Zapbo.filename, field="geom"))
  284. #
  285. # # rattachement les noeuds aux artères
  286. # for artere in arteres:
  287. # try:
  288. # artere.noeud_a = noeuds[artere.AR_NOEUD_A]
  289. # except KeyError:
  290. # artere.noeud_a = None
  291. # self.log_error(RelationError("Le noeud '{}' n'existe pas".format(artere.AR_NOEUD_A), filename=Artere.filename, field="AR_NOEUD_A"))
  292. #
  293. # try:
  294. # artere.noeud_b = noeuds[artere.AR_NOEUD_B]
  295. # except KeyError:
  296. # artere.noeud_b = None
  297. # self.log_error(RelationError("Le noeud '{}' n'existe pas".format(artere.AR_NOEUD_B), filename=Artere.filename, field="AR_NOEUD_A"))
  298. #
  299. # # rattachement des equipements aux cables
  300. # for cable in cables:
  301. # try:
  302. # cable.equipement_a = equipements[cable.CA_EQ_A]
  303. # except KeyError:
  304. # cable.equipement_a = None
  305. # self.log_error(RelationError("L'équipement '{}' n'existe pas".format(cable.CA_EQ_A), filename=Cable.filename, field="CA_EQ_A"))
  306. #
  307. # try:
  308. # cable.equipement_b = equipements[cable.CA_EQ_B]
  309. # except KeyError:
  310. # cable.equipement_b = None
  311. # self.log_error(RelationError("L'équipement '{}' n'existe pas".format(cable.CA_EQ_B), filename=Cable.filename, field="CA_EQ_B"))
  312. #
  313. # # rattachement des equipements aux noeuds
  314. # for equipement in equipements.values():
  315. # try:
  316. # equipement.noeud = noeuds[equipement.EQ_NOM_NOE]
  317. # except KeyError:
  318. # equipement.noeud = None
  319. # self.log_error(RelationError("Le noeud '{}' n'existe pas".format(equipement.EQ_NOM_NOE, equipement.EQ_NOM), filename=Equipement.filename, field="EQ_NOM_NOE"))
  320. #
  321. # # verifie que tous les equipements sont l'equipement B d'au moins un cable
  322. # equipements_b = [cable.CA_EQ_B for cable in cables]
  323. # for eq_id in equipements:
  324. # if equipements[eq_id].EQ_TYPE == "BAI":
  325. # continue
  326. # if not eq_id in equipements_b:
  327. # self.log_error(RelationError("L'equipement '{}' n'est l'équipement B d'aucun cable".format(eq_id), filename=Equipement.filename, field="EQ_NOM"))
  328. #
  329. # # controle des doublons graphiques
  330. # for i, tranchee in enumerate(tranchees):
  331. # for other in tranchees[i+1:]:
  332. # if tranchee.geom == other.geom:
  333. # self.log_error(DuplicatedGeom("Une entité graphique est dupliquée".format(tranchee), filename=Tranchee.filename, field="geom"))
  334. #
  335. # for i, artere in enumerate(arteres):
  336. # for other in arteres[i+1:]:
  337. # if artere.geom == other.geom:
  338. # self.log_error(DuplicatedGeom("Une entité graphique est dupliquée ('{}')".format(artere), filename=Artere.filename, field="geom"))
  339. #
  340. # for i, cable in enumerate(cables):
  341. # for other in cables[i+1:]:
  342. # if cable.geom == other.geom and cable.CA_EQ_A == other.CA_EQ_A and cable.CA_EQ_B == other.CA_EQ_B:
  343. # self.log_error(DuplicatedGeom("Une entité graphique est dupliquée ('{}')".format(cable), filename=Cable.filename, field="geom"))
  344. #
  345. # ls_noeuds = list(noeuds.values())
  346. # for i, noeud in enumerate(ls_noeuds):
  347. # for other in ls_noeuds[i+1:]:
  348. # if noeud.geom == other.geom:
  349. # self.log_error(DuplicatedGeom("Une entité graphique est dupliquée ('{}')".format(noeud), filename=Noeud.filename, field="geom"))
  350. # del ls_noeuds
  351. #
  352. # ls_zapbos = list(zapbos.values())
  353. # for i, zapbo in enumerate(ls_zapbos):
  354. # for other in ls_zapbos[i+1:]:
  355. # if zapbo.geom == other.geom:
  356. # self.log_error(DuplicatedGeom("Une entité graphique est dupliquée ('{}')".format(zapbo), filename=Zapbo.filename, field="geom"))
  357. # del ls_zapbos
  358. #
  359. # # Arteres: comparer la géométrie à celle des noeuds
  360. # for artere in arteres:
  361. # if not artere.noeud_a or not artere.noeud_b:
  362. # continue
  363. #
  364. # buffer_a, buffer_b = artere.points[0].Buffer(TOLERANCE), artere.points[-1].Buffer(TOLERANCE)
  365. #
  366. # if not (buffer_a.Contains(artere.noeud_a.points[0]) and buffer_b.Contains(artere.noeud_b.points[0])) \
  367. # and not (buffer_a.Contains(artere.noeud_b.points[0]) and buffer_b.Contains(artere.noeud_a.points[0])):
  368. #
  369. # self.log_error(MissingItem("Pas de noeud aux coordonnées attendues ('{}')".format(artere), filename=Artere.filename, field="geom"))
  370. #
  371. #
  372. # # Cables: comparer la géométrie à celle des equipements (on utilise en fait la geom du noeud correspondant à l'équipement)
  373. # for cable in cables:
  374. # if not cable.equipement_a or not cable.equipement_b or not cable.valid or not cable.equipement_a.noeud or not cable.equipement_b.noeud:
  375. # continue
  376. #
  377. # buffer_a, buffer_b = cable.points[0].Buffer(TOLERANCE), cable.points[-1].Buffer(TOLERANCE)
  378. #
  379. # if not (buffer_a.Contains(cable.equipement_a.noeud.points[0]) and buffer_b.Contains(cable.equipement_b.noeud.points[0])) \
  380. # and not (buffer_a.Contains(cable.equipement_b.noeud.points[0]) and buffer_b.Contains(cable.equipement_a.noeud.points[0])):
  381. #
  382. # self.log_error(MissingItem("Pas d'equipement aux coordonnées attendues ('{}')".format(cable), filename=Cable.filename, field="geom"))
  383. #
  384. # del buffer_a, buffer_b
  385. #
  386. # # Verifie que chaque tranchée a au moins une artère
  387. # arteres_emprise = Feature.buffered_union(arteres, TOLERANCE)
  388. #
  389. # for tranchee in tranchees:
  390. # if not arteres_emprise.Contains(tranchee.geom):
  391. # self.log_error(MissingItem("Tranchée sans artère ('{}')".format(tranchee), filename=Tranchee.filename, field="-"))
  392. #
  393. #
  394. # # Verifie que chaque cable a au moins une artère (sauf si commentaire contient 'baguette')
  395. # for cable in cables:
  396. # if "baguette" in cable.CA_COMMENT.lower() or not cable.valid:
  397. # continue
  398. # if not arteres_emprise.Contains(cable.geom):
  399. # self.log_error(MissingItem("Cable sans artère ('{}')".format(cable), filename=Cable.filename, field="-"))
  400. #
  401. # del arteres_emprise
  402. #
  403. # # Verifie que chaque artère a au moins un cable (sauf si commentaire contient un de ces mots 'racco client adductio attente bus 'sans cable'')
  404. # cables_emprise = Feature.buffered_union(cables, TOLERANCE)
  405. #
  406. # for artere in arteres:
  407. # if any(x in artere.AR_COMMENT.lower() for x in ['racco','client','adductio','attente','bus','sans cable']):
  408. # continue
  409. # if not cables_emprise.Contains(artere.geom):
  410. # self.log_error(MissingItem("Artère sans cable ('{}')".format(artere), filename=Artere.filename, field="-"))
  411. #
  412. # del cables_emprise
  413. #
  414. # # Contrôle des dimensions logiques
  415. # for artere in arteres:
  416. # try:
  417. # if not int(artere.AR_FOU_DIS) <= int(artere.AR_NB_FOUR):
  418. # self.log_error(DimensionError("Le nombre de fourreaux disponibles doit être inférieur au nombre total ('{}')".format(artere), filename=Artere.filename, field="AR_FOU_DIS"))
  419. # except (TypeError, ValueError):
  420. # pass
  421. #
  422. # for cable in cables:
  423. # try:
  424. # if not int(cable.CA_NB_FO_U) <= int(cable.CA_NB_FO):
  425. # self.log_error(DimensionError("Le nombre de fourreaux utilisés doit être inférieur au nombre total ('{}')".format(cable), filename=Cable.filename, field="CA_NB_FO_U"))
  426. # if not int(cable.CA_NB_FO_D) <= int(cable.CA_NB_FO):
  427. # self.log_error(DimensionError("Le nombre de fourreaux disponibles doit être inférieur au nombre total ('{}')".format(cable), filename=Cable.filename, field="CA_NB_FO_D"))
  428. # except (TypeError, ValueError):
  429. # pass
  430. #
  431. # ant_db = mn.ANTDb_0()
  432. # ant_db.execute("alter session set NLS_NUMERIC_CHARACTERS = '.,';") # definit le separateur decimal sur '.'
  433. #
  434. # # Toutes les zapbo contiennent au moins une prise
  435. # for zapbo in zapbos.values():
  436. #
  437. # if len(zapbo.points) >= 499:
  438. # # passe l'erreur provoquée par la limite au nombre d'arguments en SQL
  439. # zapbo.nb_prises = None
  440. # continue
  441. #
  442. # sql = """Select SUM(NB_PRISE) AS NB_PRISES FROM SIG_ANT.FTTH_MN_PRISE_LOT z
  443. # WHERE SDO_INSIDE(z.GEOMETRY,
  444. # SDO_GEOMETRY(2003, 3949, SDO_POINT_TYPE(null,null,null), SDO_ELEM_INFO_ARRAY(1,1003,1), SDO_ORDINATE_ARRAY({}))
  445. # )='TRUE';""".format(", ".join(["{},{}".format(p.GetX(), p.GetY()) for p in zapbo.points]))
  446. #
  447. # zapbo.nb_prises = int(ant_db.first(sql).NB_PRISES)
  448. # if not zapbo.nb_prises:
  449. # self.log_error(MissingItem("La Zapbo ne contient aucune prise: {}".format(zapbo), filename=Zapbo.filename, field="-"))
  450. #
  451. # # Toutes les prises de la ou les ZAPM impactées sont dans une zapbo
  452. # zapms = {}
  453. # # > on déduit la liste des zapms à partir de la position des zapbos
  454. # for zapbo in zapbos.values():
  455. # centre = zapbo.geom.Centroid()
  456. # zapm = ant_db.first("""SELECT z.ID_ZAPM
  457. # FROM SIG_ANT.FTTH_MN_ZAPM z
  458. # WHERE sdo_contains(z.GEOMETRY,
  459. # SDO_GEOMETRY(2001, 3949, SDO_POINT_TYPE({}, {}, NULL), NULL, NULL)) = 'TRUE'
  460. # """.format(centre.GetX(), centre.GetY()))
  461. # try:
  462. # zapms[zapm.ID_ZAPM].append(zapbo)
  463. # except KeyError:
  464. # zapms[zapm.ID_ZAPM] = [zapbo]
  465. #
  466. # for id_zapm in zapms:
  467. # zapm_couverture = Feature.union(zapms[id_zapm])
  468. # for prise in ant_db.read("""SELECT t.X AS x, t.Y AS y
  469. # FROM SIG_ANT.FTTH_MN_PRISE_LOT z,
  470. # TABLE(SDO_UTIL.GETVERTICES(z.GEOMETRY)) t
  471. # WHERE T_ETAT<>'OBSOLETE' AND ID_ZAPM_PARTIELLE='{}';""".format(id_zapm)):
  472. # point = ogr.Geometry(ogr.wkbPoint)
  473. # point.AddPoint(prise.x, prise.y)
  474. # if not zapm_couverture.Contains(point):
  475. # self.log_error(MissingItem("Certaines prises de la ZAPM ne sont pas comprises dans une ZAPBO: {}".format(id_zapm), filename=Zapbo.filename, field="-"))
  476. #
  477. # # Verifier que chaque equipement de type PBO est contenu dans une zapbo, et que le nom de la zapbo contient le nom de l'equipement
  478. #
  479. # for equipement in equipements.values():
  480. # if not equipement.EQ_TYPE == "PBO":
  481. # continue
  482. #
  483. # #zapbos englobant l'equipement
  484. # candidates = []
  485. # for zapbo in zapbos.values():
  486. # if zapbo.geom.Contains(equipement.geom):
  487. # candidates.append(zapbo)
  488. #
  489. # # le pbo doit être contenu dans une zapbo
  490. # if not candidates:
  491. # self.log_error(MissingItem("Le PBO n'est contenu dans aucune ZAPBO: {}".format(equipement), filename=Equipement.filename, field="geom"))
  492. # continue
  493. #
  494. # # On se base sur le nom pour trouver la zapbo correspondante
  495. # try:
  496. # equipement.zapbo = next((z for z in candidates if equipement.EQ_NOM in z.ID_ZAPBO))
  497. # except StopIteration:
  498. # self.log_error(MissingItem("Le nom du PBO ne coincide avec le nom d'aucune des ZAPBO qui le contient: {}".format(equipement), filename=Equipement.filename, field="EQ_NOM"))
  499. # break
  500. #
  501. # # Controle du dimensionnement des PBO
  502. # if equipement.zapbo.nb_prises is not None:
  503. # if equipement.EQ_TYPE_PH == 'PBO 6' and not equipement.zapbo.nb_prises < 6:
  504. # self.log_error(DimensionError("Le PBO 6 contient plus de 5 prises: {}".format(equipement), filename=Equipement.filename, field="-"))
  505. #
  506. # if equipement.EQ_TYPE_PH == 'PBO 12' and not equipement.zapbo.nb_prises >= 6 and equipement.zapbo.nb_prises <= 8:
  507. # self.log_error(DimensionError("Le PBO 12 contient mois de 6 prises ou plus de 8 prises: {}".format(equipement), filename=Equipement.filename, field="-"))
  508. #
  509. # if equipement.zapbo.STATUT == "REC" and not equipement.EQ_STATUT == "REC":
  510. # self.log_error(TechnicalValidationError("Le statut du PBO n'est pas cohérent avec le statut de sa ZAPBO: {}".format(equipement), filename=Equipement.filename, field="-"))
  511. #
  512. # if equipement.EQ_STATUT == "REC" and not equipement.zapbo.STATUT == "REC" and not equipement.zapbo.ID_ZAPBO[:4].lower() == "att_":
  513. # self.log_error(TechnicalValidationError("Le statut du PBO n'est pas cohérent avec le statut de sa ZAPBO: {}".format(equipement), filename=Equipement.filename, field="-"))
  514. #
  515. # # Contrôler dans la base si des éléments portant ces codes existent à des emplacements différents
  516. # for noeud in noeuds.values():
  517. # sql = """SELECT z.NO_NOM, SDO_GEOM.SDO_DISTANCE(z.GEOMETRY, SDO_GEOMETRY(2001, 3949, SDO_POINT_TYPE({}, {}, NULL), NULL, NULL),0.005) AS DIST
  518. # FROM SIG_ANT.FTTH_MN_GR_NOEUD_GEO z
  519. # WHERE z.NO_NOM='{}';""".format(noeud.geom.GetX(), noeud.geom.GetY(), noeud.NO_NOM)
  520. # existing = ant_db.first(sql)
  521. # if existing:
  522. # if existing.DIST > TOLERANCE and existing.DIST < 20:
  523. # self.log_error(PositionError("La position du noeud ne correspond pas à l'existant: {}".format(noeud), filename=Noeud.filename, field="geom"))
  524. # elif existing.DIST > 20:
  525. # self.log_error(DuplicatedGeom("Un noeud portant ce nom existe déjà ailleurs sur le territoire: {}".format(noeud), filename=Noeud.filename, field="NO_NOM"))
  526. #
  527. # for zapbo in zapbos.values():
  528. # sql = """SELECT z.ID_ZAPBO, SDO_GEOM.SDO_DISTANCE(SDO_GEOM.SDO_CENTROID(z.GEOMETRY,0.005), SDO_GEOMETRY(2001, 3949, SDO_POINT_TYPE({}, {}, NULL), NULL, NULL),0.005) AS DIST
  529. # FROM SIG_ANT.FTTH_MN_GR_ZAPBO_GEO z
  530. # WHERE z.ID_ZAPBO='{}';""".format(zapbo.geom.Centroid().GetX(), zapbo.geom.Centroid().GetY(), zapbo.ID_ZAPBO)
  531. # existing = ant_db.first(sql)
  532. # if existing:
  533. # if existing.DIST > TOLERANCE and existing.DIST < 20:
  534. # self.log_error(PositionError("La position de la ZAPBO ne correspond pas à l'existant: {}".format(zapbo), filename=Zapbo.filename, field="geom"))
  535. # elif existing.DIST > 20:
  536. # self.log_error(DuplicatedGeom("Une ZAPBO portant ce nom existe déjà ailleurs sur le territoire: {}".format(zapbo), filename=Zapbo.filename, field="ID_ZAPBO"))
  537. #
  538. # # Contrôle si un equipement portant ce nom existe, mais associé à un noeud différent
  539. # for equipement in equipements.values():
  540. # sql = """SELECT z.EQ_NOM, z.EQ_NOM_NOEUD
  541. # FROM SIG_ANT.FTTH_MN_GR_EQ_PASSIF z
  542. # WHERE z.EQ_NOM='{}';""".format(equipement.EQ_NOM)
  543. # existing = ant_db.first(sql)
  544. # if existing and existing.EQ_NOM_NOEUD != equipement.EQ_NOM_NOE:
  545. # self.log_error(DuplicatedGeom("Un équipement portant ce nom ({}) existe déjà et est associé à un noeud différent ({})".format(equipement.NO_NOM, existing.EQ_NOM_NOEUD), filename=Noeud.filename, field="geom"))
  546. #
  547. #