Browse Source

Ajouts: accepte les zip, controle les coordonnées

olivier.massot 7 years ago
parent
commit
9fed13afab
4 changed files with 60 additions and 38 deletions
  1. 1 1
      logging.yaml
  2. 51 34
      main.py
  3. 1 1
      requirements.txt
  4. 7 2
      resources/netgeo_v2-2_doe.yaml

+ 1 - 1
logging.yaml

@@ -35,7 +35,7 @@ handlers:
         capacity: 100000000
         capacity: 100000000
 
 
 loggers:
 loggers:
-    netgeo_checker:
+    datachecker:
         level: DEBUG
         level: DEBUG
         handlers: [console]
         handlers: [console]
         propagate: no
         propagate: no

+ 51 - 34
main.py

@@ -3,12 +3,11 @@ Python 3.7+
 
 
 @author: olivier.massot, sept 2018
 @author: olivier.massot, sept 2018
 '''
 '''
-from dataclasses import dataclass
 from datetime import datetime
 from datetime import datetime
 import logging
 import logging
-import sys
+import zipfile
 
 
-from path import Path
+from path import Path, TempDir
 import shapefile
 import shapefile
 import yaml
 import yaml
 
 
@@ -16,38 +15,32 @@ from core import logconf
 from core.constants import MAIN
 from core.constants import MAIN
 
 
 
 
-logger = logging.getLogger("netgeo_checker")
-logconf.start("netgeo_checker", logging.INFO)
-
-ETUDE, IN_DIR = False, MAIN / "work" / "SCOPELEC_CAP_097AP0_REC_180829_OK"
-# ETUDE, IN_DIR = True, MAIN / "rsc" / "data_in" / "SCOPELEC_101BP0_APD_180725"
-
-
-subject = MAIN / "work" / "SCOPELEC_CAP_097AP0_REC_180829_OK"
-checker = MAIN / "resources" / "netgeo_v2-2_doe.yaml"
-
-# prendre une archive en entrée
-# contrôler la presence des 6 fichiers shp
-# Pour chaque couche, vérifier le type de géometrie, la presence de données, la projection, l'inclusion du bounding rect dans le departement
-# Charger les modèles
-# Vérifier enregistrement par enregistrement la validité des données
-
+logger = logging.getLogger("datachecker")
+logconf.start("datachecker", logging.INFO)
 
 
+# TODO Vérifier la projection (besoin de GDAL/OGR)
 
 
 def check(subject, checker):    
 def check(subject, checker):    
     """ prends un dossier ou une archive en entier et vérifie son contenu selon les règles données par le fichier de config """
     """ prends un dossier ou une archive en entier et vérifie son contenu selon les règles données par le fichier de config """
-    archive, checker = Path(subject), Path(checker)
+    subject, checker = Path(subject), Path(checker)
     
     
-    if archive.isfile():
-        # extraire vers un dossier temp
-        # dirname = tempdir
-        pass
-    elif archive.isdir():
-        dirname = subject
+    if subject.isfile():
+        with TempDir() as dirname:
+            zip_ref = zipfile.ZipFile(subject, 'r')
+            zip_ref.extractall(dirname)
+            zip_ref.close()
+            print()
+            if Path(dirname / subject.stem).isdir(): # cas où l'archive contient un dossier qui lui-même contient les fichiers
+                dirname /= subject.stem
+            check_folder(dirname, checker)
+        
+    elif subject.isdir():
+        check_folder(subject, checker)
     else:
     else:
         raise IOError(f"Impossible de trouver le fichier ou répertoire: {subject}")
         raise IOError(f"Impossible de trouver le fichier ou répertoire: {subject}")
-    
-    logging.info("*****   Traitement de '%s'   *****", subject.name)
+
+def check_folder(folder, checker):
+    logging.info("*****   Traitement de '%s'   *****", folder.name)
     
     
     logging.info("> Controlleur: '%s'", checker.name)
     logging.info("> Controlleur: '%s'", checker.name)
     
     
@@ -55,20 +48,34 @@ def check(subject, checker):
         config = yaml.load(cf)
         config = yaml.load(cf)
     
     
     for filename, model in config["files"].items():
     for filename, model in config["files"].items():
-        path_ = dirname / filename
+        path_ = folder / filename
         logging.info("* Traitement de %s", path_.name)
         logging.info("* Traitement de %s", path_.name)
         
         
+        if not path_.isfile():
+            logger.error("Fichier introuvable")
+            continue
+        
         try:
         try:
             sf = shapefile.Reader(path_)
             sf = shapefile.Reader(path_)
         except shapefile.ShapefileException:
         except shapefile.ShapefileException:
             logger.error("Fichier SHAPE illisible")
             logger.error("Fichier SHAPE illisible")
             continue
             continue
         
         
-        shape_names = {1:"Point", 3:"Polyligne", 5:"Polygone"}
-        if sf.shapeType != model["shape_type"]:
-            logger.error("Le fichier shapefile n'est pas de type %s", shape_names[model["shape_type"]])
-            del sf
-            continue
+        if "srid" in config:
+            pass
+
+        xmin, xmax, ymin, ymax = (int(config.get("xmin", 0)), 
+                                  int(config.get("xmax", float("inf"))), 
+                                  int(config.get("ymin", 0)), 
+                                  int(config.get("ymax", float("inf")))
+                                  )
+            
+        if "shape_type" in model:
+            shape_names = {1:"Point", 3:"Polyligne", 5:"Polygone"}
+            if sf.shapeType != model["shape_type"]:
+                logger.error("Le fichier shapefile n'est pas de type %s", shape_names[model["shape_type"]])
+                del sf
+                continue
 
 
         records = sf.shapeRecords()
         records = sf.shapeRecords()
         if not records:
         if not records:
@@ -95,6 +102,11 @@ def check(subject, checker):
             logging.info("\n> Enregistrement n°%s\n", i)
             logging.info("\n> Enregistrement n°%s\n", i)
             record_data = {field: record.record[i] for i, field in enumerate(fields)}
             record_data = {field: record.record[i] for i, field in enumerate(fields)}
             
             
+            x1, y1, x2, y2 = sf.shapes()[i].bbox
+            if not xmin <= x1 <= xmax or not xmin <= x2 <= xmax or \
+               not ymin <= y1 <= ymax or not ymin <= y2 <= ymax:
+                logger.error("L'élément est situé hors de la zone autorisée")
+            
             for fieldname, fieldmodel in model["fields"].items():
             for fieldname, fieldmodel in model["fields"].items():
                 
                 
                 try:
                 try:
@@ -133,5 +145,10 @@ def check(subject, checker):
 
 
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
+    
+    subject = MAIN / "work" / "SCOPELEC_CAP_097AP0_REC_180829_OK.zip"
+    checker = MAIN / "resources" / "netgeo_v2-2_doe.yaml"
+    
     check(subject, checker)
     check(subject, checker)
+    
     logger.info("-- Fin --")
     logger.info("-- Fin --")

+ 1 - 1
requirements.txt

@@ -1,5 +1,5 @@
 pypyodbc
 pypyodbc
-path.py
+path.py>=11.1.0
 lxml
 lxml
 python-dateutil
 python-dateutil
 pyyaml
 pyyaml

+ 7 - 2
resources/netgeo_v2-2_doe.yaml

@@ -1,3 +1,9 @@
+xmin: 1341999.9443451899569482
+xmax: 1429750.0912875002250075
+ymin: 8147750.0839389534667134
+ymax: 8294000.0620922148227692
+srid: 3949
+
 files:
 files:
   "artere_geo.shp":
   "artere_geo.shp":
     shape_type: 3
     shape_type: 3
@@ -17,7 +23,6 @@ files:
   "tranchee_geo.shp":
   "tranchee_geo.shp":
     shape_type: 3
     shape_type: 3
     can_be_empty: False
     can_be_empty: False
-    srid: 3949
     fields:
     fields:
       TR_CODE:
       TR_CODE:
         type: str
         type: str
@@ -78,4 +83,4 @@ files:
         type: str
         type: str
         in_list: ["EN ETUDE", "EN REALISATION", "EN SERVICE", "HORS SERVICE"]
         in_list: ["EN ETUDE", "EN REALISATION", "EN SERVICE", "HORS SERVICE"]
   "zapbo_geo.shp":
   "zapbo_geo.shp":
-    shape_type: 5
+    shape_type: 5