Просмотр исходного кода

integration moteur datacheck ok

omassot 7 лет назад
Родитель
Сommit
97aba191da
8 измененных файлов с 132 добавлено и 76 удалено
  1. 0 19
      core/constants.py
  2. 2 1
      core/logging.yaml
  3. 57 0
      core/model.py
  4. 3 3
      core/validation_errors.py
  5. 20 34
      core/validator.py
  6. 6 9
      schemas/mn1_rec.py
  7. 6 6
      schemas/mn2_rec.py
  8. 38 4
      ui/dlg_main.py

+ 0 - 19
core/constants.py

@@ -12,22 +12,3 @@ LOGDIR = Path("%appdata%").expandvars() / "mncheck"
 LOGDIR.mkdir_p()
 LOGDIR.mkdir_p()
 
 
 LOGCONF = MAIN / "core" / "logging.yaml"
 LOGCONF = MAIN / "core" / "logging.yaml"
-
-
-
-GEOM_UNKNOWN = 0
-GEOM_POINT = 1
-GEOM_LINE = 2
-GEOM_POLYGON = 3
-GEOM_MULTIPOINT = 4
-GEOM_MULTILINE = 5
-GEOM_MULTIPOLYGON = 6
-
-GEOM_NAMES = {0: "(AUCUN)", 
-               1: "POINT", 
-               2: "LIGNE", 
-               3: "POLYGONE", 
-               4: "MULTI-POINT", 
-               5:"MULTI-LIGNE", 
-               6:"MULTI-POLYGONE",
-               }

+ 2 - 1
core/logging.yaml

@@ -15,6 +15,7 @@ handlers:
         class: core.QHandler.QHandler
         class: core.QHandler.QHandler
         level: DEBUG
         level: DEBUG
         formatter: message_only
         formatter: message_only
+        
     file:
     file:
         class: logging.handlers.RotatingFileHandler
         class: logging.handlers.RotatingFileHandler
         level: DEBUG
         level: DEBUG
@@ -32,5 +33,5 @@ loggers:
 
 
 root:
 root:
     level: DEBUG
     level: DEBUG
-    handlers: [file]
+    handlers: []
     propagate: yes
     propagate: yes

+ 57 - 0
core/model.py

@@ -0,0 +1,57 @@
+'''
+
+@author: olivier.massot, 2018
+'''
+
+import logging
+from qgis.core import QgsWkbTypes
+
+from PyQt5.QtCore import QVariant
+
+
+logger = logging.getLogger("mncheck")
+
+class QgsModel():
+    
+    GEOM_UNKNOWN = 0
+    GEOM_POINT = 1
+    GEOM_LINE = 2
+    GEOM_POLYGON = 3
+    GEOM_MULTIPOINT = 4
+    GEOM_MULTILINE = 5
+    GEOM_MULTIPOLYGON = 6
+    
+    GEOM_NAMES = {0: "(AUCUN)", 1: "POINT", 2: "LIGNE", 3: "POLYGONE", 
+                  4: "MULTI-POINT", 5:"MULTI-LIGNE", 6:"MULTI-POLYGONE"}
+    
+    layername = ""
+    pk = ""
+    geom_type = 0
+    bounding_box = (0,0,1,1)
+    schema = {}
+
+    def __init__(self, qgs_feature):
+        
+        self._feature = qgs_feature
+        
+        attributes = dict(zip([f.name() for f in qgs_feature.fields()], qgs_feature.attributes()))
+        
+        for attr, value in attributes.items():
+            if isinstance(value, QVariant):
+                value = value.value() if not value.isNull() else ""
+            setattr(self, attr, value)
+        
+        self.geom = qgs_feature.geometry()
+        
+    def is_geometry_valid(self):
+        return self._feature.geometry().isGeosValid()
+        
+    def get_geom_type(self):
+        return QgsWkbTypes.singleType(self._feature.geometry().wkbType())
+        
+    def get_bounding_box(self):
+        bb = self._feature.geometry().boundingBox()
+        return (bb.xMinimum(), bb.yMinimum(), bb.xMaximum(), bb.yMaximum())
+    
+        
+        

+ 3 - 3
core/validation_errors.py

@@ -14,13 +14,13 @@ class BaseValidationError():
     name = "Erreur"
     name = "Erreur"
     level = ERROR
     level = ERROR
     help = ""
     help = ""
-    def __init__(self, message, filename="", field=""):
+    def __init__(self, message, layername="", field=""):
         self.message = message
         self.message = message
-        self.filename = filename
+        self.layername = layername
         self.field = field
         self.field = field
         
         
     def __repr__(self):
     def __repr__(self):
-        return " - ".join(filter(None, [self.name, self.filename, self.field, self.message]))
+        return " - ".join(filter(None, [self.name, self.layername, self.field, self.message]))
 
 
 # Erreurs dans le chargement des couches
 # Erreurs dans le chargement des couches
 class InputError(BaseValidationError):
 class InputError(BaseValidationError):

+ 20 - 34
core/validator.py

@@ -3,14 +3,18 @@
 
 
     @author: olivier.massot, sept. 2018
     @author: olivier.massot, sept. 2018
 '''
 '''
-from qgis.core import QgsProject #@UnresolvedImport
+import logging
+from qgis.core import QgsProject
 
 
-from core import constants
 from core.cerberus_extend import CerberusErrorHandler, \
 from core.cerberus_extend import CerberusErrorHandler, \
     _translate_messages, ExtendedValidator
     _translate_messages, ExtendedValidator
+from core.model import QgsModel
 from core.validation_errors import CRITICAL, DataError, GeomTypeError, BoundingBoxError, \
 from core.validation_errors import CRITICAL, DataError, GeomTypeError, BoundingBoxError, \
     MissingLayer, WrongSrid
     MissingLayer, WrongSrid
 
 
+
+logger = logging.getLogger("mncheck")
+
 class ValidatorInterruption(BaseException): 
 class ValidatorInterruption(BaseException): 
     pass
     pass
 
 
@@ -19,28 +23,6 @@ class Checkpoint():
         self.name = name
         self.name = name
         self.valid = valid
         self.valid = valid
 
 
-
-###########    MODELES    ################
-
-class BaseModel():
-    filename = ""
-    pk = ""
-    schema = {}
-    def __init__(self, **kwargs):
-        self.__dict__.update(kwargs)
-        
-class QgsModel():
-    layername = ""
-    pk = ""
-    geom_type = 0
-    bounding_box = (0,0,1,1)
-    schema = {}
-    
-    def __init__(self, qgs_feature):
-        self.__dict__.update(qgs_feature.__dict__)
-
-###########    VALIDATION    ################
-
 class BaseValidator():
 class BaseValidator():
     schema_name = ""
     schema_name = ""
     models = {}
     models = {}
@@ -88,7 +70,7 @@ class BaseValidator():
         # Controle la structure des données (champs, formats et types)
         # Controle la structure des données (champs, formats et types)
         self._structure_validation()
         self._structure_validation()
         self.checkpoint("Contrôle de la structure des données")
         self.checkpoint("Contrôle de la structure des données")
-        
+         
         # Validation technique
         # Validation technique
         try:
         try:
             self._technical_validation()
             self._technical_validation()
@@ -128,30 +110,34 @@ class BaseValidator():
             
             
             for item in self.dataset[model]:
             for item in self.dataset[model]:
 
 
+                # geom valid
+                if not item.is_geometry_valid():
+                    self.log_error(GeomTypeError("La géométrie de l'objet est invalide".format(), layername=model.layername, field="geom"))
+
                 # geom type
                 # geom type
-                if item.geom_type != model.geom_type:
-                    self.log_error(GeomTypeError("Type de géométrie invalide: {} (attendu: {})".format(item.geom_name, constants.GEOM_NAMES[model.geom_type]), filename=model.filename, field="geom"))
+                if item.get_geom_type() != model.geom_type:
+                    self.log_error(GeomTypeError("Type de géométrie invalide: {} (attendu: {})".format(item.geom_name, QgsModel.GEOM_NAMES[model.geom_type]), layername=model.layername, field="geom"))
 
 
                 # bounding box
                 # bounding box
-                x1, y1, x2, y2 = item.bounding_box
+                x1, y1, x2, y2 = item.get_bounding_box()
                 if any(x < xmin or x > xmax for x in (x1, x2)) or \
                 if any(x < xmin or x > xmax for x in (x1, x2)) or \
                    any(y < ymin or y > ymax for y in (y1, y2)):
                    any(y < ymin or y > ymax for y in (y1, y2)):
-                    self.log_error(BoundingBoxError("Situé hors de l'emprise autorisée", filename=model.filename, field="geom"))
+                    self.log_error(BoundingBoxError("Situé hors de l'emprise autorisée", layername=model.layername, field="geom"))
 
 
                 v.validate(item.__dict__)
                 v.validate(item.__dict__)
-               
+                
                 for field, verrors in v.errors.items():
                 for field, verrors in v.errors.items():
                     for err in verrors:
                     for err in verrors:
-                        self.log_error(DataError(_translate_messages(err), filename=model.filename, field=field))
+                        self.log_error(DataError(_translate_messages(err), layername=model.layername, field=field))
     
     
     @classmethod
     @classmethod
     def _technical_validation(cls):
     def _technical_validation(cls):
         raise NotImplementedError()
         raise NotImplementedError()
     
     
-    def build_report(self, schema, filename):
+    def build_report(self, schema, layername):
         report = {}
         report = {}
         report["schema"] = schema
         report["schema"] = schema
-        report["filename"] = filename
+        report["reportname"] = ""
         report["checkpoints"] = [{"name": chk.name, "valid": chk.valid} for chk in self.checkpoints]
         report["checkpoints"] = [{"name": chk.name, "valid": chk.valid} for chk in self.checkpoints]
         
         
         report["errors"] = {}
         report["errors"] = {}
@@ -160,7 +146,7 @@ class BaseValidator():
             if not err.name in report["errors"]:
             if not err.name in report["errors"]:
                 report["errors"][err.name] = {"help": err.help, "order_": err.order_, "list": []}
                 report["errors"][err.name] = {"help": err.help, "order_": err.order_, "list": []}
             
             
-            err_report = {"filename": err.filename or "-",
+            err_report = {"layername": err.layername or "-",
                           "field": err.field or "-",
                           "field": err.field or "-",
                           "message": err.message}
                           "message": err.message}
             if err_report not in report["errors"][err.name]["list"]:
             if err_report not in report["errors"][err.name]["list"]:

+ 6 - 9
schemas/mn1_rec.py

@@ -15,7 +15,7 @@ from schemas._common import XMIN, YMIN, XMAX, YMAX, TOLERANCE, CRS
 
 
 class Artere(QgsModel):
 class Artere(QgsModel):
     layername = "artere_geo"
     layername = "artere_geo"
-    geom_type = constants.GEOM_LINE
+    geom_type = QgsModel.GEOM_LINE
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'AR_ID_INSE': {'type': 'string', 'empty': False, 'regex': r'50\d{3}'}, 
     schema = {'AR_ID_INSE': {'type': 'string', 'empty': False, 'regex': r'50\d{3}'}, 
@@ -46,7 +46,7 @@ class Artere(QgsModel):
 
 
 class Cable(QgsModel):
 class Cable(QgsModel):
     layername = "cable_geo"
     layername = "cable_geo"
-    geom_type = constants.GEOM_LINE
+    geom_type = QgsModel.GEOM_LINE
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'CA_NUMERO': {'type': 'string', 'maxlength': 17}, 
     schema = {'CA_NUMERO': {'type': 'string', 'maxlength': 17}, 
@@ -72,7 +72,7 @@ class Cable(QgsModel):
     
     
 class Equipement(QgsModel):
 class Equipement(QgsModel):
     layername = "equipement_passif"
     layername = "equipement_passif"
-    geom_type = constants.GEOM_POINT
+    geom_type = QgsModel.GEOM_POINT
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'EQ_NOM': {'type': 'string', 'maxlength': 10, 'contains_any_of': ['PBO', 'BPE', 'BAI']}, 
     schema = {'EQ_NOM': {'type': 'string', 'maxlength': 10, 'contains_any_of': ['PBO', 'BPE', 'BAI']}, 
@@ -94,7 +94,7 @@ class Equipement(QgsModel):
 
 
 class Noeud(QgsModel):
 class Noeud(QgsModel):
     layername = "noeud_geo"
     layername = "noeud_geo"
-    geom_type = constants.GEOM_POINT
+    geom_type = QgsModel.GEOM_POINT
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'NO_NOM': {'type': 'string', 'maxlength': 30}, 
     schema = {'NO_NOM': {'type': 'string', 'maxlength': 30}, 
@@ -127,7 +127,7 @@ class Noeud(QgsModel):
     
     
 class Tranchee(QgsModel):
 class Tranchee(QgsModel):
     layername = "tranchee_geo"
     layername = "tranchee_geo"
-    geom_type = constants.GEOM_LINE
+    geom_type = QgsModel.GEOM_LINE
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'TR_ID_INSE': {'type': 'string', 'empty': False, 'regex': r'50\d{3}'}, 
     schema = {'TR_ID_INSE': {'type': 'string', 'empty': False, 'regex': r'50\d{3}'}, 
@@ -156,7 +156,7 @@ class Tranchee(QgsModel):
     
     
 class Zapbo(QgsModel):
 class Zapbo(QgsModel):
     layername = "zapbo_geo"
     layername = "zapbo_geo"
-    geom_type = constants.GEOM_POLYGON
+    geom_type = QgsModel.GEOM_POLYGON
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'ID_ZAPBO': {'type': 'string', 'maxlength': 30, 'contains_any_of': ['PBO', 'BPE']}, 
     schema = {'ID_ZAPBO': {'type': 'string', 'maxlength': 30, 'contains_any_of': ['PBO', 'BPE']}, 
@@ -168,9 +168,6 @@ class Zapbo(QgsModel):
     
     
     
     
     
     
-    
-    
-    
 ####### Validateur
 ####### Validateur
     
     
     
     

+ 6 - 6
schemas/mn2_rec.py

@@ -21,7 +21,7 @@ STATUTS = ['EN ETUDE', 'EN REALISATION', 'EN SERVICE', 'HORS SERVICE']
 
 
 class Artere(QgsModel):
 class Artere(QgsModel):
     layername = "artere_geo"
     layername = "artere_geo"
-    geom_type = constants.GEOM_LINE
+    geom_type = QgsModel.GEOM_LINE
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'AR_CODE': {'type': 'string', 'maxlength': 26},
     schema = {'AR_CODE': {'type': 'string', 'maxlength': 26},
@@ -60,7 +60,7 @@ class Artere(QgsModel):
 
 
 class Cable(QgsModel):
 class Cable(QgsModel):
     layername = "cable_geo"
     layername = "cable_geo"
-    geom_type = constants.GEOM_LINE
+    geom_type = QgsModel.GEOM_LINE
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'CA_CODE': {'type': 'string', 'maxlength': 18}, 
     schema = {'CA_CODE': {'type': 'string', 'maxlength': 18}, 
@@ -93,7 +93,7 @@ class Cable(QgsModel):
     
     
 class Equipement(QgsModel):
 class Equipement(QgsModel):
     layername = "equipement_passif"
     layername = "equipement_passif"
-    geom_type = constants.GEOM_POINT
+    geom_type = QgsModel.GEOM_POINT
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'EQ_CODE': {'type': 'string', 'maxlength': 18}, 
     schema = {'EQ_CODE': {'type': 'string', 'maxlength': 18}, 
@@ -125,7 +125,7 @@ class Equipement(QgsModel):
 
 
 class Noeud(QgsModel):
 class Noeud(QgsModel):
     layername = "noeud_geo"
     layername = "noeud_geo"
-    geom_type = constants.GEOM_POINT
+    geom_type = QgsModel.GEOM_POINT
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'NO_CODE': {'type': 'string', 'maxlength': 18}, 
     schema = {'NO_CODE': {'type': 'string', 'maxlength': 18}, 
@@ -161,7 +161,7 @@ class Noeud(QgsModel):
     
     
 class Tranchee(QgsModel):
 class Tranchee(QgsModel):
     layername = "tranchee_geo"
     layername = "tranchee_geo"
-    geom_type = constants.GEOM_LINE
+    geom_type = QgsModel.GEOM_LINE
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'TR_CODE': {'type': 'string', 'maxlength': 23}, 
     schema = {'TR_CODE': {'type': 'string', 'maxlength': 23}, 
@@ -193,7 +193,7 @@ class Tranchee(QgsModel):
     
     
 class Zapbo(QgsModel):
 class Zapbo(QgsModel):
     layername = "zapbo_geo"
     layername = "zapbo_geo"
-    geom_type = constants.GEOM_POLYGON
+    geom_type = QgsModel.GEOM_POLYGON
     crs = CRS
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
     schema = {'ID_ZAPBO': {'type': 'string', 'maxlength': 30, 'contains_any_of': ['PBO', 'BPE']}, 
     schema = {'ID_ZAPBO': {'type': 'string', 'maxlength': 30, 'contains_any_of': ['PBO', 'BPE']}, 

+ 38 - 4
ui/dlg_main.py

@@ -2,17 +2,16 @@
 """
 """
 import importlib
 import importlib
 import logging
 import logging
-
-from qgis.core import QgsProject #@UnresolvedImport
+from qgis.core import QgsProject  # @UnresolvedImport
 
 
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5 import uic
 from PyQt5 import uic
 from PyQt5.Qt import QIcon, QPixmap, Qt, QTableWidgetItem, QHeaderView
 from PyQt5.Qt import QIcon, QPixmap, Qt, QTableWidgetItem, QHeaderView
+from PyQt5.QtWidgets import QApplication
 
 
 from core.constants import MAIN, RSCDIR
 from core.constants import MAIN, RSCDIR
 
 
 
 
-
 logger = logging.getLogger("mncheck")
 logger = logging.getLogger("mncheck")
 
 
 Ui_Main, _ = uic.loadUiType(MAIN / 'ui'/ 'dlg_main.ui')
 Ui_Main, _ = uic.loadUiType(MAIN / 'ui'/ 'dlg_main.ui')
@@ -105,6 +104,9 @@ class DlgMain(QtWidgets.QDialog):
 
 
     def run(self):
     def run(self):
         
         
+        QApplication.setOverrideCursor(Qt.WaitCursor)
+        self.clear_layers_table()
+        
         validator = self.selected_validator()
         validator = self.selected_validator()
         
         
         if not validator:
         if not validator:
@@ -113,7 +115,39 @@ class DlgMain(QtWidgets.QDialog):
         
         
         report = validator.submit(self.iface)
         report = validator.submit(self.iface)
         
         
-        logger.info(report)
+        logger.debug(report)
+        
+        for chk in report["checkpoints"]:
+            cur_row = self.ui.tbl_layers.rowCount()
+            self.ui.tbl_layers.insertRow(cur_row)
+            
+            lbl_icon, lbl_checkpoint = QTableWidgetItem(), QTableWidgetItem()
+            
+            lbl_checkpoint.setText(chk["name"])
+            lbl_icon.setIcon(QIcon(QPixmap(RSCDIR / ("ok_16.png" if chk["valid"] else "error_16.png"))))
+            
+            self.ui.tbl_layers.setItem(cur_row, 0, lbl_icon)
+            self.ui.tbl_layers.setItem(cur_row, 1, lbl_checkpoint)
+            if not chk["valid"]:
+                self.ui.tbl_layers.setItem(cur_row, 2, QTableWidgetItem("Des erreurs ont été trouvées"))
+
+        
+        for type_err in report["errors"]:
+            for err in report["errors"][type_err]["list"]:
+                cur_row = self.ui.tbl_layers.rowCount()
+                self.ui.tbl_layers.insertRow(cur_row)
+                
+                lbl_icon, lbl_field, lbl_err = QTableWidgetItem(), QTableWidgetItem(), QTableWidgetItem()
+                
+                lbl_field.setText(f"{err['layername']}.{err['field']}")
+                lbl_err.setText(f"{type_err}: {err['message']}")
+#                 lbl_icon.setIcon(QIcon(QPixmap(RSCDIR / "error_16.png")))
+                
+#                 self.ui.tbl_layers.setItem(cur_row, 0, lbl_icon)
+                self.ui.tbl_layers.setItem(cur_row, 1, lbl_field)
+                self.ui.tbl_layers.setItem(cur_row, 2, lbl_err)
+    
+        QApplication.restoreOverrideCursor()
     
     
     def show_help(self):
     def show_help(self):
         pass
         pass