omassot 7 years ago
parent
commit
332f6a59c6
4 changed files with 33 additions and 297 deletions
  1. 0 40
      core/QgsLogHandler.py
  2. 33 0
      core/logconf.py
  3. 0 93
      core/validation_errors.py
  4. 0 164
      core/validator.py

+ 0 - 40
core/QgsLogHandler.py

@@ -1,40 +0,0 @@
-'''
-
-    Logging handler for QGis
-
-@author: olivier.massot, 2018
-'''
-import logging
-
-from qgis.core import QgsMessageLog
-
-_to_qgis_level = {logging.DEBUG: 0, 
-                  logging.INFO: 0, 
-                  logging.WARNING: 1,
-                  logging.ERROR: 2,
-                  logging.CRITICAL: 2, 
-                  logging.NOTSET: 4}
-
-
-class QgsLogHandler(logging.Handler):
-    
-    _qgs_iface = None
-
-    @classmethod
-    def connect_to_iface(cls, iface):
-        cls._qgs_iface = iface
-
-    def emit(self, record):
-        try:
-            msg = self.format(record)
-            level = record.levelno
-            
-            QgsMessageLog.logMessage(msg, "Mnheck", _to_qgis_level[level])
-            
-            if self._qgs_iface and record.levelno >= logging.WARNING:
-                self._qgs_iface.messageBar().pushMessage("MnCheck", msg, level=_to_qgis_level[level])
-            
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except:
-            self.handleError(record)

+ 33 - 0
core/logconf.py

@@ -11,6 +11,39 @@ import yaml
 
 from core.constants import LOGDIR, LOGCONF
 
+from qgis.core import QgsMessageLog
+
+_to_qgis_level = {logging.DEBUG: 0, 
+                  logging.INFO: 0, 
+                  logging.WARNING: 1,
+                  logging.ERROR: 2,
+                  logging.CRITICAL: 2, 
+                  logging.NOTSET: 4}
+
+
+class QgsLogHandler(logging.Handler):
+    
+    _qgs_iface = None
+
+    @classmethod
+    def connect_to_iface(cls, iface):
+        cls._qgs_iface = iface
+
+    def emit(self, record):
+        try:
+            msg = self.format(record)
+            level = record.levelno
+            
+            QgsMessageLog.logMessage(msg, "Mnheck", _to_qgis_level[level])
+            
+            if self._qgs_iface and record.levelno >= logging.WARNING:
+                self._qgs_iface.messageBar().pushMessage("MnCheck", msg, level=_to_qgis_level[level])
+            
+        except (KeyboardInterrupt, SystemExit):
+            raise
+        except:
+            self.handleError(record)
+
 
 SYS_EXCEPT_HOOK = sys.excepthook
 

+ 0 - 93
core/validation_errors.py

@@ -1,93 +0,0 @@
-'''
-
-@author: olivier.massot, 2018
-'''
-
-MINOR = 10
-WARNING = 20
-ERROR = 30
-CRITICAL = 40
-
-_errorlvl_to_str = {10: "MINEURE", 20: "AVERTISSEMENT", 30: "ERREUR", 40: "CRITIQUE"}
-
-
-class BaseValidationError():
-    order_ = 0
-    name = "Erreur"
-    level = ERROR
-    help = ""
-    def __init__(self, message, layername="", field=""):
-        self.message = message
-        self.layername = layername
-        self.field = field
-        
-    def __repr__(self):
-        return " - ".join(filter(None, [self.name, self.layername, self.field, self.message]))
-
-# Erreurs dans le chargement des couches
-class InputError(BaseValidationError):
-    order_ = 0
-    level = CRITICAL
-    name = "Erreur de chargement"
-
-class MissingLayer(InputError):
-    order_ = 1
-    name = "Couche Manquante"
-    
-class WrongSrid(InputError):
-    order_ = 3
-    name = "Mauvais SRID"
-
-### Erreurs dans la structure des données
-class StructureError(BaseValidationError):
-    order_ = 10
-    name = "Erreur de structure"
-    level = ERROR
-    
-class GeomTypeError(StructureError):
-    order_ = 12
-    name = "Type de géométrie invalide"
-    level = CRITICAL
-    
-class BoundingBoxError(StructureError):
-    order_ = 11
-    name = "Coordonnées hors de la zone autorisée"
-    
-class InvalidGeometry(StructureError):
-    order_ = 13
-    name = "Géométrie invalide"
-    
-class DataError(StructureError):
-    order_ = 14
-    name = "Erreur de format"
-
-# Erreurs dans le contenu, erreurs métiers
-class TechnicalValidationError(BaseValidationError):
-    order_ = 20
-    level = ERROR
-    name = "Erreur technique"
-
-class UniqueError(TechnicalValidationError):
-    order_ = 21
-    name = "Doublons dans le champs"
-
-class RelationError(TechnicalValidationError):
-    order_ = 22
-    level = CRITICAL
-    name = "Un objet lié n'existe pas"
-
-class DuplicatedGeom(TechnicalValidationError):
-    order_ = 23
-    name = "Doublon graphique"
-
-class MissingItem(TechnicalValidationError):
-    order_ = 24
-    name = "Elément manquant"
-
-class DimensionError(TechnicalValidationError):
-    order_ = 25
-    name = "Elément de dimension"
-
-class PositionError(TechnicalValidationError):
-    order_ = 26
-    name = "Erreur de positionnement"

+ 0 - 164
core/validator.py

@@ -1,164 +0,0 @@
-'''
-
-
-    @author: olivier.massot, sept. 2018
-'''
-import logging
-from qgis.core import QgsProject
-
-from core.cerberus_ import CerberusErrorHandler, \
-    _translate_messages, ExtendedValidator
-from core.model import QgsModel
-from core.validation_errors import CRITICAL, DataError, GeomTypeError, BoundingBoxError, \
-    MissingLayer, WrongSrid, InvalidGeometry, TechnicalValidationError
-
-
-logger = logging.getLogger("mncheck")
-
-class ValidatorInterruption(BaseException): 
-    pass
-
-class Checkpoint():
-    def __init__(self, name, valid=True):
-        self.name = name
-        self.valid = valid
-
-class BaseValidator():
-    schema_name = ""
-    models = {}
-    dataset = {}
-    
-    def __init__(self):
-        self.valid = True
-        self.checkpoints = []
-        self.errors = []
-        self._current_checkpoint_valid = True
-        self.dt = 0
-    
-    def checkpoint(self, title):
-        logger.info(f"Checkpoint: {title}")
-        self.checkpoints.append(Checkpoint(title, self._current_checkpoint_valid))
-        self._current_checkpoint_valid = True
-        if self.errors:
-            self.valid = False
-            if self.critical_happened():
-                logger.info("Contrôle de données interrompu en raison d'une erreur critique dans les données")
-                raise ValidatorInterruption()
-            
-    def critical_happened(self):
-        return any([err.level == CRITICAL for err in self.errors])
-             
-    def log_error(self, validation_error):
-        self._current_checkpoint_valid = False
-        self.errors.append(validation_error)
-
-    @classmethod
-    def submit(cls, report_name=""):
-        validator = cls()
-        try:
-            validator.validate()
-        except ValidatorInterruption:
-            pass
-        
-        report = validator.build_report(validator.schema_name, report_name or "(nom inconnu)")
-        return report
-    
-    def validate(self):
-        
-        # Chargement des données en mémoire
-        self._load_layers()
-        self.checkpoint("Chargement des données")
-        
-        # Controle la structure des données (champs, formats et types)
-        self._structure_validation()
-        self.checkpoint("Contrôle de la structure des données")
-         
-        # Validation technique
-        try:
-            self._technical_validation()
-            self.checkpoint("Validation Métier")
-        except ValidatorInterruption:
-            raise
-        except:
-            logger.exception("Une erreur s'est produite")
-            self.log_error(TechnicalValidationError("Contrôle des données interrompu"))
-            self.checkpoint("Validation Métier [interrompu]")
-    
-    def _load_layers(self):
-    
-        for model in self.models:
-            layername = model.layername
-            
-            layer = next((l for l in QgsProject.instance().mapLayers().values() if l.name().lower() == layername))
-            if not layer:
-                self.log_error(MissingLayer("Couche manquante: '{}'".format(layername)))
-                continue
-            
-            self.dataset[model] = []
-            
-            if layer.crs().authid() != model.crs:
-                self.log_error(WrongSrid("Mauvaise projection: {} (attendu: {})".format(layer.crs().authid(), model.crs)))
-            
-            for feature in layer.getFeatures():
-                
-                item = model(feature)
-                
-                self.dataset[model].append(item)
-
-    
-
-    def _structure_validation(self):
-        
-        for model in self.models:
-            v = ExtendedValidator(model.schema, purge_unknown=True, error_handler=CerberusErrorHandler, require_all=True)
-            xmin, ymin, xmax, ymax = model.bounding_box
-            
-            for item in self.dataset[model]:
-
-                # geom valid
-                if not item.is_geometry_valid():
-                    self.log_error(InvalidGeometry("La géométrie de l'objet est invalide: {}".format(item), layername=model.layername, field="geom"))
-
-                # geom type
-                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
-                x1, y1, x2, y2 = item.get_bounding_box()
-                if any(x < xmin or x > xmax for x in (x1, x2)) or \
-                   any(y < ymin or y > ymax for y in (y1, y2)):
-                    self.log_error(BoundingBoxError("Situé hors de l'emprise autorisée", layername=model.layername, field="geom"))
-
-                v.validate(item.__dict__)
-                
-                for field, verrors in v.errors.items():
-                    for err in verrors:
-                        self.log_error(DataError(_translate_messages(err), layername=model.layername, field=field))
-    
-    @classmethod
-    def _technical_validation(cls):
-        raise NotImplementedError()
-    
-    def build_report(self, schema, layername):
-        report = {}
-        report["schema"] = schema
-        report["reportname"] = ""
-        report["checkpoints"] = [{"name": chk.name, "valid": chk.valid} for chk in self.checkpoints]
-        
-        report["errors"] = {}
-        
-        for err in self.errors:
-            if not err.name in report["errors"]:
-                report["errors"][err.name] = {"help": err.help, "order_": err.order_, "level": err.level, "list": []}
-            
-            err_report = {"layername": err.layername or "-",
-                          "field": err.field or "-",
-                          "message": err.message}
-            if err_report not in report["errors"][err.name]["list"]:
-                report["errors"][err.name]["list"].append(err_report)
-            
-        return report
-    
-
-
-