Ver código fonte

add progress bar and progression signals

omassot 7 anos atrás
pai
commit
b938e93887
5 arquivos alterados com 102 adições e 49 exclusões
  1. 23 13
      core/checking.py
  2. 2 2
      core/mncheck.py
  3. 1 4
      schemas/mn1_rec.py
  4. 14 14
      schemas/mn2_rec.py
  5. 62 16
      ui/dlg_main.py

+ 23 - 13
core/checking.py

@@ -9,6 +9,8 @@ import re
 import sys
 import traceback
 
+from PyQt5.QtCore import QObject
+
 
 UNKNOWN = 0
 SUCCESS = 1
@@ -50,12 +52,18 @@ class TestResult():
 
     @property
     def title(self):
-        return self._test.__doc__.split("\n")[0].strip() 
+        try:
+            return self._test.__doc__.split("\n")[0].strip()
+        except AttributeError:
+            return self.name
 
     @property
     def description(self):
-        return re.sub(" +", " ", self._test.__doc__.strip(), flags=re.MULTILINE) #@UndefinedVariable
-    
+        try:
+            return re.sub(" +", " ", self._test.__doc__.strip(), flags=re.MULTILINE) #@UndefinedVariable
+        except AttributeError:
+            return ""
+
     @property
     def status(self):
         return self._status
@@ -79,18 +87,19 @@ class TestResult():
                           {"exc_info": "{}\n{}\n{}".format(typ.__name__, value, ''.join(traceback.format_tb(trace)))})
         self.errors.append(error)
 
-    def grouped_errors(self):
-        res = {}
-        for err in self.errors:
-            if not err.message in res:
-                res[err.message] = []
-            res[err.message].append(err)
-        return res
+class Comlink():
+    def _started_test(self, test):
+        pass
+    
+    def _ended_test(self, test):
+        pass
 
 class BaseChecker():
     
     def __init__(self):
         self._test_running = None
+        self.comlink = Comlink()
+        self.tests = sorted([m for _, m in inspect.getmembers(self, predicate=inspect.ismethod) if m.__name__[:5] == 'test_'], key=_linenumber)
         
     def setUp(self):
         pass
@@ -108,13 +117,12 @@ class BaseChecker():
         
         tests_results = []
         
-        tests = sorted([m for _, m in inspect.getmembers(self, predicate=inspect.ismethod) if m.__name__[:5] == 'test_'], key=_linenumber)
-        
-        for test in tests:
+        for test in self.tests:
             
             result = TestResult(test)
             
             self._test_running = result
+            self.comlink._started_test(result)
             
             self.setUp()
             
@@ -126,6 +134,8 @@ class BaseChecker():
             self.tearDown()
 
             tests_results.append(result)
+            
+            self.comlink._ended_test(result)
     
             if any(err.critical for err in result.errors):
                 break

+ 2 - 2
core/mncheck.py

@@ -8,9 +8,9 @@ import logging
 import pkgutil
 from qgis.core import QgsWkbTypes, QgsGeometry, QgsPoint
 
-from PyQt5.QtCore import QVariant
+from PyQt5.QtCore import QVariant, pyqtSignal, QObject
 
-from core.checking import BaseChecker
+from core.checking import BaseChecker, Comlink
 
 
 logger = logging.getLogger("mncheck")

+ 1 - 4
schemas/mn1_rec.py

@@ -6,7 +6,6 @@
 '''
 
 import logging
-
 from qgis.core import QgsProject, QgsGeometry
 
 from core.cerberus_ import is_float, is_multi_int, is_int, \
@@ -15,6 +14,7 @@ from core.cerberus_ import is_float, is_multi_int, is_int, \
 from core.checking import BaseChecker
 from core.mncheck import QgsModel
 
+
 logger = logging.getLogger("mncheck")
 
 SCHEMA_NAME = "Schéma MN v1 REC"
@@ -188,9 +188,6 @@ models = [Artere, Cable, Equipement, Noeud, Tranchee, Zapbo]
 
 class Mn1Checker(BaseChecker):
     
-    def __init__(self):
-        super().__init__()
-    
     def test_load_layers(self):
         """ Chargement des données
             Contrôle la présence des couches attendues

+ 14 - 14
schemas/mn2_rec.py

@@ -4,6 +4,7 @@
 
 @author: olivier.massot, 2018
 '''
+
 import logging
 from qgis.core import QgsProject, QgsGeometry
 
@@ -16,10 +17,8 @@ from core.mncheck import QgsModel
 
 logger = logging.getLogger("mncheck")
 
-
 SCHEMA_NAME = "Schéma MN v2 REC"
 
-
 STATUTS = ['EN ETUDE', 'EN REALISATION', 'EN SERVICE', 'HORS SERVICE']
 
 XMIN, XMAX, YMIN, YMAX = 1341999, 1429750, 8147750, 8294000
@@ -35,6 +34,7 @@ class Artere(QgsModel):
     geom_type = QgsModel.GEOM_LINE
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
+    pk = "AR_CODE"
     schema = {'AR_CODE': {'type': 'string', 'maxlength': 26},
               'AR_NOM': {'type': 'string', 'maxlength': 26}, 
               'AR_ID_INSE': {'type': 'string', 'empty': False, 'regex': r'50\d{3}'}, 
@@ -67,13 +67,14 @@ class Artere(QgsModel):
               'AR_STATUT': {'type': 'string', 'empty': False, 'allowed': STATUTS}}
 
     def __repr__(self):
-        return "Artere {}".format(self.AR_CODE)
+        return f"Artere {self.AR_CODE}"
 
 class Cable(QgsModel):
     layername = "cable_geo"
     geom_type = QgsModel.GEOM_LINE
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
+    pk = "CA_CODE"
     schema = {'CA_CODE': {'type': 'string', 'maxlength': 18}, 
               'CA_NOM': {'type': 'string', 'maxlength': 18}, 
               'CA_NUMERO': {'type': 'string', 'maxlength': 17},  
@@ -100,13 +101,14 @@ class Cable(QgsModel):
               'CA_STATUT': {'type': 'string', 'empty': False, 'allowed': STATUTS}}
 
     def __repr__(self):
-        return "Cable {}-{}".format(self.CA_EQ_A, self.CA_EQ_B)
+        return f"Cable {self.CA_CODE}"
     
 class Equipement(QgsModel):
     layername = "equipement_passif"
     geom_type = QgsModel.GEOM_POINT
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
+    pk = "EQ_CODE"
     schema = {'EQ_CODE': {'type': 'string', 'maxlength': 18}, 
               'EQ_NOM': {'type': 'string', 'maxlength': 10, 'contains_any_of': ['PBO', 'BPE', 'BAI']}, 
               'EQ_NOM_NOE': {'type': 'string', 'maxlength': 30}, 
@@ -132,13 +134,14 @@ class Equipement(QgsModel):
               'EQ_STATUT': {'type': 'string', 'empty': False, 'allowed': STATUTS}}
         
     def __repr__(self):
-        return "Equipement {}".format(self.EQ_NOM)
+        return f"Equipement {self.EQ_CODE}"
 
 class Noeud(QgsModel):
     layername = "noeud_geo"
     geom_type = QgsModel.GEOM_POINT
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
+    pk = "NO_CODE"
     schema = {'NO_CODE': {'type': 'string', 'maxlength': 18}, 
               'NO_NOM': {'type': 'string', 'maxlength': 30}, 
               'NO_ID_INSE': {'type': 'string', 'empty': False, 'regex': r'50\d{3}'}, 
@@ -168,13 +171,14 @@ class Noeud(QgsModel):
               'NO_STATUT': {'type': 'string', 'empty': False, 'allowed': STATUTS}}
 
     def __repr__(self):
-        return "Noeud {}".format(self.NO_NOM)
+        return f"Noeud {self.NO_CODE}"
     
 class Tranchee(QgsModel):
     layername = "tranchee_geo"
     geom_type = QgsModel.GEOM_LINE
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
+    pk = "TR_CODE"
     schema = {'TR_CODE': {'type': 'string', 'maxlength': 23}, 
               'TR_NOM': {'type': 'string', 'maxlength': 23}, 
               'TR_ID_INSE': {'type': 'string', 'empty': False, 'regex': r'50\d{3}'}, 
@@ -200,30 +204,26 @@ class Tranchee(QgsModel):
               'TR_STATUT': {'type': 'string', 'empty': False, 'allowed': STATUTS}}
 
     def __repr__(self):
-        return "Tranchee {}".format(self.TR_VOIE)
+        return f"Tranchee {self.TR_CODE}"
     
 class Zapbo(QgsModel):
     layername = "zapbo_geo"
     geom_type = QgsModel.GEOM_POLYGON
     crs = CRS
     bounding_box = (XMIN,YMIN,XMAX,YMAX)
+    pk = "ID_ZAPBO"
     schema = {'ID_ZAPBO': {'type': 'string', 'maxlength': 30, 'contains_any_of': ['PBO', 'BPE']}, 
               'COMMENTAIR': {'type': 'string', 'maxlength': 254, 'empty': True}, 
               'STATUT': {'type': 'string', 'empty': False, 'allowed': STATUTS}}
     
     def __repr__(self):
-        return "Zapbo {}".format(self.ID_ZAPBO)
+        return f"Zapbo {self.ID_ZAPBO}"
 
-
-   
 models = [Artere, Cable, Equipement, Noeud, Tranchee, Zapbo]
     
 ####### Validateur
 
-class Mn1Checker(BaseChecker):
-    
-    def __init__(self):
-        super().__init__()
+class Mn2Checker(BaseChecker):
     
     def test_load_layers(self):
         """ Chargement des données

+ 62 - 16
ui/dlg_main.py

@@ -1,25 +1,34 @@
 """
 """
 from core import mncheck, checking
-import importlib
 import logging
-from qgis.core import QgsProject  # @UnresolvedImport
+from qgis.core import QgsProject, QgsVectorLayer
 
 from PyQt5 import QtWidgets
 from PyQt5 import uic
 from PyQt5.Qt import Qt
+from PyQt5.QtCore import QObject, pyqtSignal
 from PyQt5.QtGui import QIcon, QPixmap
 from PyQt5.QtWidgets import QApplication, QTreeWidgetItem
 
+from core.checking import TestResult
 from core.constants import MAIN, RSCDIR
-from core.mncheck import QgsModel
 
 
 logger = logging.getLogger("mncheck")
 
 Ui_Main, _ = uic.loadUiType(MAIN / 'ui'/ 'dlg_main.ui')
 
-# SCHEMAS = ["mn1_rec", "mn2_rec", "mn3_pro", "mn3_exe", "mn3_rec"]
+class QComlink(QObject):
+    started_test = pyqtSignal(TestResult)
+    ended_test = pyqtSignal(TestResult)
+    
+    def _started_test(self, test):
+        self.started_test.emit(test)
+    
+    def _ended_test(self, test):
+        self.ended_test.emit(test)
+    
 
 class DlgMain(QtWidgets.QDialog):
     def __init__(self, iface, parent=None):
@@ -53,17 +62,42 @@ class DlgMain(QtWidgets.QDialog):
             
         self.ui.cbb_schemas.currentIndexChanged.connect(self.update_layers_list)
         
+        self.ui.progress_bar.setVisible(False)
+        
         self.ui.tree_report.setColumnWidth(0, 35)
         self.ui.tree_report.itemClicked.connect(self.tree_item_clicked)
+        self.ui.tree_report.expanded.connect(self.tree_item_expanded)
         
         self.update_layers_list()
     
+    def tree_item_expanded(self, _):
+        self.tree_auto_resize()
+    
+    def tree_auto_resize(self):
+        self.ui.tree_report.resizeColumnToContents(1)
+        self.ui.tree_report.resizeColumnToContents(2)
+        self.ui.tree_report.setColumnWidth(1, self.ui.tree_report.columnWidth(1) + 20)
+        self.ui.tree_report.setColumnWidth(2, self.ui.tree_report.columnWidth(2) + 20)
+    
     def current_schema(self):
         schema_name = self.available_schemas[int(self.ui.cbb_schemas.itemData(self.ui.cbb_schemas.currentIndex()))]
         logger.info("Selected schema: %s", schema_name)
     
         return mncheck.get_schema(schema_name)
     
+    def init_progress_bar(self, value=0, minimum=0, maximum=100):
+        self.ui.progress_bar.setMinimum(minimum)
+        self.ui.progress_bar.setValue(value)
+        self.ui.progress_bar.setMaximum(maximum)
+        
+    def test_started(self, test):
+        logger.debug("* %s - start test", test.name)
+    
+    def test_ended(self, test):
+        logger.debug("* %s - %s", test.name, test.status_str)
+        self.ui.progress_bar.setValue(self.ui.progress_bar.value() + 1)
+        self.ui.progress_bar.update()
+    
     def update_layers_list(self):
         
         schema = self.current_schema()
@@ -92,6 +126,8 @@ class DlgMain(QtWidgets.QDialog):
                 
             self.ui.tree_report.addTopLevelItem(item)
 
+        self.tree_auto_resize()
+
 
     def run(self):
         
@@ -109,17 +145,28 @@ class DlgMain(QtWidgets.QDialog):
                 logger.error("Aucun testeur trouvé dans le schéma")
                 return
         
-            for checker in checkers:
-                logger.info(f"Execution du checker {checker.__name__}")
-                self._run(checker)
+            for checker_cls in checkers:
+                logger.info(f"Execution du checker {checker_cls.__name__}")
+                self._run(checker_cls)
         except:
             raise
         finally:
+            self.tree_auto_resize()
+            self.ui.progress_bar.setVisible(False)
             QApplication.restoreOverrideCursor()
     
-    def _run(self, checker):
+    def _run(self, checker_cls):
+        
+        checker = checker_cls()
+        
+        checker.comlink = QComlink()
+        checker.comlink.started_test.connect(self.test_started)
+        checker.comlink.ended_test.connect(self.test_ended)
+        
+        self.ui.progress_bar.setVisible(True)
+        self.init_progress_bar(maximum=len(checker.tests))
         
-        results = checker().run()
+        results = checker.run()
         
         for result in results:
             topitem = QTreeWidgetItem()
@@ -156,7 +203,7 @@ class DlgMain(QtWidgets.QDialog):
                 elif "model" in err.info:
                     data = err.info["model"]
                     erritem.setText(1, msg)
-                    erritem.setText(2, f"{data.layername}")
+                    erritem.setText(2, f"{data.layername}".upper())
                     erritem.setData(1, Qt.UserRole, data)
                     
                 elif "item" in err.info:
@@ -165,9 +212,12 @@ class DlgMain(QtWidgets.QDialog):
                     erritem.setText(2, f"{data}")
                     erritem.setData(1, Qt.UserRole, data)
                 
+                font = erritem.font(2)
+                font.setItalic(True)
+                erritem.setFont(2, font)
+                
                 topitem.addChild(erritem)
         
-        self.ui.tree_report.resizeColumnToContents(1)
         
     def tree_item_clicked(self, item, _):
         
@@ -176,23 +226,19 @@ class DlgMain(QtWidgets.QDialog):
         if not data:
             return
         
-        logger.debug("error clicked (data: %s)", data)
-        
         if hasattr(data, "_feature"):
             self.zoom_to_feature(data.layer, data._feature)
             
         elif hasattr(data, "layer"):
             self.select_layer(data.layer)
             
-        else:
-            logger.error(f"Data unknown: {data}")
-        
     def select_layer(self, layer):
         self.iface.setActiveLayer(layer)
         
     def zoom_to_feature(self, layer, feature):
         self.iface.setActiveLayer(layer)
         self.iface.mapCanvas().zoomToFeatureExtent(feature.geometry().boundingBox())
+        layer.removeSelection()
         layer.select(feature.id())
         
     def show_help(self):