From b893717f8735015b1059dd9dd177f353d829aada Mon Sep 17 00:00:00 2001 From: Jeff Osundwa Date: Thu, 12 Sep 2024 11:53:55 +0300 Subject: [PATCH 1/2] added black linter --- .../workflows/black-python-code-linter.yml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/black-python-code-linter.yml diff --git a/.github/workflows/black-python-code-linter.yml b/.github/workflows/black-python-code-linter.yml new file mode 100644 index 00000000..51b9d3d3 --- /dev/null +++ b/.github/workflows/black-python-code-linter.yml @@ -0,0 +1,20 @@ +name: Black Python Code Linter + +on: + push: + branches: + - main + + paths: + - "**.py" + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: psf/black@stable + with: + options: "--check --verbose" + src: "./src/qgis_gender_indicator_tool" From ac65de59e0e524586493715239f2e9faa5ed75d4 Mon Sep 17 00:00:00 2001 From: Jeff Osundwa Date: Thu, 12 Sep 2024 12:00:05 +0300 Subject: [PATCH 2/2] black formated files --- admin.py | 18 +++---- geest.py | 24 +++++++--- queue_manager.py | 10 ++-- resources/resources.py | 13 +++-- src/qgis_gender_indicator_tool/__init__.py | 3 +- .../gui/gender_indicator_tool_dialog.py | 10 +++- src/qgis_gender_indicator_tool/main.py | 3 -- task.py | 35 +++++++++----- test/__init__.py | 2 +- test/qgis_interface.py | 32 +++++++------ test/test_init.py | 47 ++++++++++--------- test/test_qgis_environment.py | 34 +++++++------- test/utilities_for_testing.py | 12 ++--- test_suite.py | 24 +++++----- tree_view_model.py | 41 +++++++++------- validate_json.py | 9 ++-- 16 files changed, 183 insertions(+), 134 deletions(-) diff --git a/admin.py b/admin.py index edbd25d8..2e8d1a9d 100644 --- a/admin.py +++ b/admin.py @@ -151,7 +151,7 @@ def generate_zip( :type context: Path """ build_dir = build(context) - metadata = _get_metadata()['general'] + metadata = _get_metadata()["general"] plugin_version = metadata["version"] if version is None else version output_directory.mkdir(parents=True, exist_ok=True) zip_path = output_directory / f"{SRC_NAME}.{plugin_version}.zip" @@ -217,7 +217,7 @@ def copy_icon( :rtype: Path """ - metadata = _get_metadata()['general'] + metadata = _get_metadata()["general"] icon_path = LOCAL_ROOT_DIR / "resources" / metadata["icon"] if icon_path.is_file(): target_path = output_directory / icon_path.name @@ -277,16 +277,18 @@ def compile_resources( _log(f"compile_resources target_path: {target_path}", context=context) subprocess.run(shlex.split(f"pyrcc5 -o {target_path} {resources_path}")) + @app.command() def add_requirements_file( - context: typer.Context, - output_directory: typing.Optional[Path] = LOCAL_ROOT_DIR / "build/temp", + context: typer.Context, + output_directory: typing.Optional[Path] = LOCAL_ROOT_DIR / "build/temp", ): resources_path = LOCAL_ROOT_DIR / "requirements-dev.txt" target_path = output_directory / "requirements-dev.txt" shutil.copy(str(resources_path.resolve()), str(target_path)) + @app.command() def generate_metadata( context: typer.Context, @@ -309,7 +311,7 @@ def generate_metadata( # do not modify case of parameters, as per # https://docs.python.org/3/library/configparser.html#customizing-parser-behaviour config.optionxform = lambda option: option - config["general"] = metadata['general'] + config["general"] = metadata["general"] with target_path.open(mode="w") as fh: config.write(fh) @@ -326,7 +328,7 @@ def generate_plugin_repo_xml( """ repo_base_dir = LOCAL_ROOT_DIR / "docs" / "repository" repo_base_dir.mkdir(parents=True, exist_ok=True) - metadata = _get_metadata()['general'] + metadata = _get_metadata()["general"] fragment_template = """ @@ -402,9 +404,7 @@ def _get_metadata() -> typing.Dict: } ) - metadata = { - 'general': general_metadata - } + metadata = {"general": general_metadata} return metadata diff --git a/geest.py b/geest.py index e3174967..c242f49e 100644 --- a/geest.py +++ b/geest.py @@ -9,13 +9,24 @@ See the LICENSE file in the project root for more information. """ -from PyQt5.QtWidgets import QDockWidget, QTreeView, QDialog, QVBoxLayout, QScrollArea, QPushButton, QWidget, QLabel, QProgressBar +from PyQt5.QtWidgets import ( + QDockWidget, + QTreeView, + QDialog, + QVBoxLayout, + QScrollArea, + QPushButton, + QWidget, + QLabel, + QProgressBar, +) from PyQt5.QtCore import Qt from PyQt5 import uic from qgis.core import QgsApplication from .tree_view_model import TreeViewModel from .queue_manager import QueueManager + class GEEST: """ The main plugin class for GEEST. @@ -84,10 +95,10 @@ def initGui(self): schema_files = { "group": "path/to/group_schema.json", "factor": "path/to/factor_schema.json", - "sub-factor": "path/to/sub_factor_schema.json" + "sub-factor": "path/to/sub_factor_schema.json", } - self.model = TreeViewModel('path/to/your/json_file.json', schema_files) + self.model = TreeViewModel("path/to/your/json_file.json", schema_files) self.tree_view.setModel(self.model) self.model.layoutChanged.connect(self.update_action_button_state) @@ -100,10 +111,10 @@ def show_config_dialog(self, node_name): node_name (str): The name of the node for which the dialog is shown. """ self.config_dialog = QDialog(self.iface.mainWindow()) - uic.loadUi('path/to/config_dialog.ui', self.config_dialog) - + uic.loadUi("path/to/config_dialog.ui", self.config_dialog) + # Set the banner text - banner_label = self.config_dialog.findChild(QLabel, 'bannerLabel') + banner_label = self.config_dialog.findChild(QLabel, "bannerLabel") if banner_label: banner_label.setText(f"{node_name} Configuration") @@ -174,4 +185,3 @@ def unload(self): Unloads the plugin from QGIS. """ self.iface.removeDockWidget(self.dock_widget) - diff --git a/queue_manager.py b/queue_manager.py index a5178fb3..d7b84b73 100644 --- a/queue_manager.py +++ b/queue_manager.py @@ -12,6 +12,7 @@ from qgis.core import QgsApplication from .task import GEESTTask + class QueueManager: """ Manages the queue of tasks for processing GEEST nodes. @@ -34,23 +35,22 @@ def generate_tasks(self): def process_tasks(self): self.generate_tasks() for task in self.tasks: - self.model.update_node_status(task.node, 'running') + self.model.update_node_status(task.node, "running") QgsApplication.taskManager().addTask(task) def cancel_tasks(self): for task in self.tasks: if not task.isCanceled(): task.cancel() - self.model.update_node_status(task.node, 'idle') + self.model.update_node_status(task.node, "idle") def on_task_finished(self, result): self.completed_tasks += 1 - status = 'success' if result else 'error' + status = "success" if result else "error" self.model.update_node_status(self.sender().node, status) self.progress_callback(self.completed_tasks, len(self.tasks)) def on_task_error(self): self.completed_tasks += 1 - self.model.update_node_status(self.sender().node, 'error') + self.model.update_node_status(self.sender().node, "error") self.progress_callback(self.completed_tasks, len(self.tasks)) - diff --git a/resources/resources.py b/resources/resources.py index b80173d9..0aa188e0 100644 --- a/resources/resources.py +++ b/resources/resources.py @@ -114,7 +114,7 @@ \x00\x00\x01\x89\x77\xc6\x6c\xc0\ " -qt_version = [int(v) for v in QtCore.qVersion().split('.')] +qt_version = [int(v) for v in QtCore.qVersion().split(".")] if qt_version < [5, 8, 0]: rcc_version = 1 qt_resource_struct = qt_resource_struct_v1 @@ -122,10 +122,17 @@ rcc_version = 2 qt_resource_struct = qt_resource_struct_v2 + def qInitResources(): - QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + QtCore.qRegisterResourceData( + rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data + ) + def qCleanupResources(): - QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + QtCore.qUnregisterResourceData( + rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data + ) + qInitResources() diff --git a/src/qgis_gender_indicator_tool/__init__.py b/src/qgis_gender_indicator_tool/__init__.py index b2cc8239..0770aa40 100644 --- a/src/qgis_gender_indicator_tool/__init__.py +++ b/src/qgis_gender_indicator_tool/__init__.py @@ -23,6 +23,7 @@ This script initializes the plugin, making it known to QGIS. """ + # noinspection PyPep8Naming def classFactory(iface): # pylint: disable=invalid-name """Load GenderIndicatorTool class from file GenderIndicatorTool. @@ -33,4 +34,4 @@ def classFactory(iface): # pylint: disable=invalid-name # from .main import GenderIndicatorTool - return GenderIndicatorTool(iface) \ No newline at end of file + return GenderIndicatorTool(iface) diff --git a/src/qgis_gender_indicator_tool/gui/gender_indicator_tool_dialog.py b/src/qgis_gender_indicator_tool/gui/gender_indicator_tool_dialog.py index f87c1614..78ac4c26 100644 --- a/src/qgis_gender_indicator_tool/gui/gender_indicator_tool_dialog.py +++ b/src/qgis_gender_indicator_tool/gui/gender_indicator_tool_dialog.py @@ -24,13 +24,16 @@ import os from qgis.core import QgsMessageLog, Qgis + try: import geopandas as gpd import rasterio from rasterio.features import rasterize from rasterio.transform import from_origin except Exception: - QgsMessageLog.logMessage("Problem importing packages in a dialog...", 'GEEST', level=Qgis.Info) + QgsMessageLog.logMessage( + "Problem importing packages in a dialog...", "GEEST", level=Qgis.Info + ) from qgis.PyQt import uic from qgis.PyQt import QtWidgets @@ -39,9 +42,12 @@ # This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer FORM_CLASS, _ = uic.loadUiType( - os.path.join(os.path.dirname(__file__), "../ui/gender_indicator_tool_dialog_base.ui") + os.path.join( + os.path.dirname(__file__), "../ui/gender_indicator_tool_dialog_base.ui" + ) ) + class GenderIndicatorToolDialog(QtWidgets.QDialog, FORM_CLASS): def __init__(self, parent=None): """Constructor.""" diff --git a/src/qgis_gender_indicator_tool/main.py b/src/qgis_gender_indicator_tool/main.py index 78f8cfd6..a9098fe3 100644 --- a/src/qgis_gender_indicator_tool/main.py +++ b/src/qgis_gender_indicator_tool/main.py @@ -232,6 +232,3 @@ def run(self): # show the dialog self.dlg.show() - - - \ No newline at end of file diff --git a/task.py b/task.py index 1e67d680..9032d890 100644 --- a/task.py +++ b/task.py @@ -13,10 +13,12 @@ from PyQt5.QtCore import pyqtSignal import os + class GEESTTask(QgsTask): """ Custom task for running GEEST plugin operations as a background job. """ + finished = pyqtSignal(bool) error = pyqtSignal() @@ -29,20 +31,30 @@ def run(self): Executes the task. This is the main work method that performs the background operation. """ try: - output_path = self.node['output_path'] - if self.node.get('processed', False) and os.path.exists(output_path): - QgsMessageLog.logMessage(f"{self.node['name']} already processed", "GEEST", QgsMessageLog.INFO) + output_path = self.node["output_path"] + if self.node.get("processed", False) and os.path.exists(output_path): + QgsMessageLog.logMessage( + f"{self.node['name']} already processed", + "GEEST", + QgsMessageLog.INFO, + ) self.finished.emit(True) return True - + # Simulate processing self.process_node() - self.node['processed'] = True - QgsMessageLog.logMessage(f"Processed {self.node['name']}", "GEEST", QgsMessageLog.INFO) + self.node["processed"] = True + QgsMessageLog.logMessage( + f"Processed {self.node['name']}", "GEEST", QgsMessageLog.INFO + ) self.finished.emit(True) return True except Exception as e: - QgsMessageLog.logMessage(f"Task failed for {self.node['name']}: {str(e)}", "GEEST", QgsMessageLog.CRITICAL) + QgsMessageLog.logMessage( + f"Task failed for {self.node['name']}: {str(e)}", + "GEEST", + QgsMessageLog.CRITICAL, + ) self.error.emit() return False @@ -50,15 +62,16 @@ def process_node(self): """ Simulates the processing of the node. """ - output_path = self.node['output_path'] + output_path = self.node["output_path"] os.makedirs(os.path.dirname(output_path), exist_ok=True) - with open(output_path, 'w') as f: + with open(output_path, "w") as f: f.write(f"Processed output for {self.node['name']}") def cancel(self): """ Handles task cancellation. """ - QgsMessageLog.logMessage(f"{self.node['name']} task was cancelled", "GEEST", QgsMessageLog.INFO) + QgsMessageLog.logMessage( + f"{self.node['name']} task was cancelled", "GEEST", QgsMessageLog.INFO + ) super().cancel() - diff --git a/test/__init__.py b/test/__init__.py index b979ed16..951c3e5e 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1 +1 @@ -__author__ = 'timlinux' +__author__ = "timlinux" diff --git a/test/qgis_interface.py b/test/qgis_interface.py index d94e99f8..4b667442 100644 --- a/test/qgis_interface.py +++ b/test/qgis_interface.py @@ -13,29 +13,31 @@ """ -__author__ = 'tim@kartoza.com' -__revision__ = '$Format:%H$' -__date__ = '10/01/2011' +__author__ = "tim@kartoza.com" +__revision__ = "$Format:%H$" +__date__ = "10/01/2011" __copyright__ = ( - 'Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk and ' - 'Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org' - 'Copyright (c) 2014 Tim Sutton, tim@kartoza.com' + "Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk and " + "Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org" + "Copyright (c) 2014 Tim Sutton, tim@kartoza.com" ) import logging from qgis.PyQt.QtCore import QObject, pyqtSlot, pyqtSignal from qgis.core import QgsMapLayerRegistry from qgis.gui import QgsMapCanvasLayer -LOGGER = logging.getLogger('qgis_templates_symbology') +LOGGER = logging.getLogger("qgis_templates_symbology") -#noinspection PyMethodMayBeStatic,PyPep8Naming + +# noinspection PyMethodMayBeStatic,PyPep8Naming class QgisInterface(QObject): """Class to expose QGIS objects and functions to plugins. This class is here for enabling us to run unit tests only, so most methods are simply stubs. """ + currentLayerChanged = pyqtSignal(QgsMapCanvasLayer) def __init__(self, canvas): @@ -46,7 +48,7 @@ def __init__(self, canvas): self.canvas = canvas # Set up slots so we can mimic the behaviour of QGIS when layers # are added. - LOGGER.debug('Initialising canvas...') + LOGGER.debug("Initialising canvas...") # noinspection PyArgumentList QgsMapLayerRegistry.instance().layersAdded.connect(self.addLayers) # noinspection PyArgumentList @@ -57,7 +59,7 @@ def __init__(self, canvas): # For processing module self.destCrs = None - @pyqtSlot('QStringList') + @pyqtSlot("QStringList") def addLayers(self, layers): """Handle layers being added to the registry so they show up in canvas. @@ -66,9 +68,9 @@ def addLayers(self, layers): .. note:: The QgsInterface models does not include this method, it is added here as a helper to facilitate testing. """ - #LOGGER.debug('addLayers called on qgis_interface') - #LOGGER.debug('Number of layers being added: %s' % len(layers)) - #LOGGER.debug('Layer Count Before: %s' % len(self.canvas.layers())) + # LOGGER.debug('addLayers called on qgis_interface') + # LOGGER.debug('Number of layers being added: %s' % len(layers)) + # LOGGER.debug('Layer Count Before: %s' % len(self.canvas.layers())) current_layers = self.canvas.layers() final_layers = [] for layer in current_layers: @@ -77,9 +79,9 @@ def addLayers(self, layers): final_layers.append(QgsMapCanvasLayer(layer)) self.canvas.setLayerSet(final_layers) - #LOGGER.debug('Layer Count After: %s' % len(self.canvas.layers())) + # LOGGER.debug('Layer Count After: %s' % len(self.canvas.layers())) - @pyqtSlot('QgsMapLayer') + @pyqtSlot("QgsMapLayer") def addLayer(self, layer): """Handle a layer being added to the registry so it shows up in canvas. diff --git a/test/test_init.py b/test/test_init.py index 14dce515..25110287 100644 --- a/test/test_init.py +++ b/test/test_init.py @@ -2,20 +2,21 @@ """Tests QGIS plugin init.""" from future import standard_library + standard_library.install_aliases() -__author__ = 'Tim Sutton ' -__revision__ = '$Format:%H$' -__date__ = '17/10/2010' +__author__ = "Tim Sutton " +__revision__ = "$Format:%H$" +__date__ = "17/10/2010" __license__ = "GPL" -__copyright__ = 'Copyright 2012, Australia Indonesia Facility for ' -__copyright__ += 'Disaster Reduction' +__copyright__ = "Copyright 2012, Australia Indonesia Facility for " +__copyright__ += "Disaster Reduction" import os import unittest import logging import configparser -LOGGER = logging.getLogger('QGIS') +LOGGER = logging.getLogger("QGIS") class TestInit(unittest.TestCase): @@ -37,30 +38,34 @@ def test_read_init(self): # plugins/validator.py required_metadata = [ - 'name', - 'description', - 'version', - 'qgisMinimumVersion', - 'email', - 'author'] - - file_path = os.path.abspath(os.path.join( - os.path.dirname(__file__), os.pardir, - 'metadata.txt')) + "name", + "description", + "version", + "qgisMinimumVersion", + "email", + "author", + ] + + file_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir, "metadata.txt") + ) LOGGER.info(file_path) metadata = [] parser = configparser.ConfigParser() parser.optionxform = str parser.read(file_path) message = 'Cannot find a section named "general" in %s' % file_path - assert parser.has_section('general'), message - metadata.extend(parser.items('general')) + assert parser.has_section("general"), message + metadata.extend(parser.items("general")) for expectation in required_metadata: - message = ('Cannot find metadata "%s" in metadata source (%s).' % ( - expectation, file_path)) + message = 'Cannot find metadata "%s" in metadata source (%s).' % ( + expectation, + file_path, + ) self.assertIn(expectation, dict(metadata), message) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/test/test_qgis_environment.py b/test/test_qgis_environment.py index a7af1ee2..adae372c 100644 --- a/test/test_qgis_environment.py +++ b/test/test_qgis_environment.py @@ -9,19 +9,16 @@ """ -__author__ = 'tim@kartoza.com' -__date__ = '20/01/2011' -__copyright__ = ('Copyright 2012, Australia Indonesia Facility for ' - 'Disaster Reduction') +__author__ = "tim@kartoza.com" +__date__ = "20/01/2011" +__copyright__ = "Copyright 2012, Australia Indonesia Facility for " "Disaster Reduction" import os import unittest -from qgis.core import ( - QgsProviderRegistry, - QgsCoordinateReferenceSystem, - QgsRasterLayer) +from qgis.core import QgsProviderRegistry, QgsCoordinateReferenceSystem, QgsRasterLayer from utilities_for_testing import get_qgis_app + QGIS_APP = get_qgis_app() @@ -32,30 +29,31 @@ def test_qgis_environment(self): """QGIS environment has the expected providers""" r = QgsProviderRegistry.instance() - self.assertIn('gdal', r.providerList()) - self.assertIn('ogr', r.providerList()) - self.assertIn('postgres', r.providerList()) + self.assertIn("gdal", r.providerList()) + self.assertIn("ogr", r.providerList()) + self.assertIn("postgres", r.providerList()) def test_projection(self): - """Test that QGIS properly parses a wkt string. - """ + """Test that QGIS properly parses a wkt string.""" crs = QgsCoordinateReferenceSystem() wkt = ( 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",' 'SPHEROID["WGS_1984",6378137.0,298.257223563]],' 'PRIMEM["Greenwich",0.0],UNIT["Degree",' - '0.0174532925199433]]') + "0.0174532925199433]]" + ) crs.createFromWkt(wkt) auth_id = crs.authid() - expected_auth_id = 'EPSG:4326' + expected_auth_id = "EPSG:4326" self.assertEqual(auth_id, expected_auth_id) # now test for a loaded layer - path = os.path.join(os.path.dirname(__file__), 'tenbytenraster.tif') - title = 'TestRaster' + path = os.path.join(os.path.dirname(__file__), "tenbytenraster.tif") + title = "TestRaster" layer = QgsRasterLayer(path, title) auth_id = layer.crs().authid() self.assertEqual(auth_id, expected_auth_id) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/test/utilities_for_testing.py b/test/utilities_for_testing.py index 844b89e3..2cc47872 100644 --- a/test/utilities_for_testing.py +++ b/test/utilities_for_testing.py @@ -6,7 +6,7 @@ import logging -LOGGER = logging.getLogger('QGIS') +LOGGER = logging.getLogger("QGIS") QGIS_APP = None # Static variable used to hold hand to running QGIS app CANVAS = None PARENT = None @@ -14,7 +14,7 @@ def get_qgis_app(): - """ Start one QGIS application to test against. + """Start one QGIS application to test against. :returns: Handle to QGIS app, canvas, iface and parent. If there are any errors the tuple members will be returned as None. @@ -35,7 +35,7 @@ def get_qgis_app(): if QGIS_APP is None: gui_flag = True # All test will run qgis in gui mode - #noinspection PyPep8Naming + # noinspection PyPep8Naming QGIS_APP = QgsApplication(sys.argv, gui_flag) # Make sure QGIS_PREFIX_PATH is set in your env if needed! QGIS_APP.initQgis() @@ -44,19 +44,19 @@ def get_qgis_app(): global PARENT # pylint: disable=W0603 if PARENT is None: - #noinspection PyPep8Naming + # noinspection PyPep8Naming PARENT = QtGui.QWidget() global CANVAS # pylint: disable=W0603 if CANVAS is None: - #noinspection PyPep8Naming + # noinspection PyPep8Naming CANVAS = QgsMapCanvas(PARENT) CANVAS.resize(QtCore.QSize(400, 400)) global IFACE # pylint: disable=W0603 if IFACE is None: # QgisInterface is a stub implementation of the QGIS plugin interface - #noinspection PyPep8Naming + # noinspection PyPep8Naming IFACE = QgisInterface(CANVAS) return QGIS_APP, CANVAS, IFACE, PARENT diff --git a/test_suite.py b/test_suite.py index a2b7876b..6479be4c 100644 --- a/test_suite.py +++ b/test_suite.py @@ -11,7 +11,7 @@ try: import coverage except ImportError: - pipmain(['install', 'coverage']) + pipmain(["install", "coverage"]) import coverage import tempfile from osgeo import gdal @@ -27,17 +27,17 @@ def _run_tests(test_suite, package_name, with_coverage=False): version = str(Qgis.QGIS_VERSION_INT) version = int(version) - print('########') - print('%s tests has been discovered in %s' % (count, package_name)) - print('QGIS : %s' % version) - print('Python GDAL : %s' % gdal.VersionInfo('VERSION_NUM')) - print('QT : %s' % Qt.QT_VERSION_STR) - print('Run slow tests : %s' % (not os.environ.get('ON_TRAVIS', False))) - print('########') + print("########") + print("%s tests has been discovered in %s" % (count, package_name)) + print("QGIS : %s" % version) + print("Python GDAL : %s" % gdal.VersionInfo("VERSION_NUM")) + print("QT : %s" % Qt.QT_VERSION_STR) + print("Run slow tests : %s" % (not os.environ.get("ON_TRAVIS", False))) + print("########") if with_coverage: cov = coverage.Coverage( - source=['./'], - omit=['*/test/*', './definitions/*'], + source=["./"], + omit=["*/test/*", "./definitions/*"], ) cov.start() @@ -51,11 +51,11 @@ def _run_tests(test_suite, package_name, with_coverage=False): # Produce HTML reports in the `htmlcov` folder and open index.html # cov.html_report() report.close() - with open(report.name, 'r') as fin: + with open(report.name, "r") as fin: print(fin.read()) -def test_package(package='test'): +def test_package(package="test"): """Test package. This function is called by Github actions or travis without arguments. :param package: The package to test. diff --git a/tree_view_model.py b/tree_view_model.py index a3ded7b4..b987e019 100644 --- a/tree_view_model.py +++ b/tree_view_model.py @@ -17,22 +17,30 @@ See the LICENSE file in the project root for more information. """ -from PyQt5.QtCore import QAbstractItemModel, Qt, QModelIndex, QVariant, QFileSystemWatcher +from PyQt5.QtCore import ( + QAbstractItemModel, + Qt, + QModelIndex, + QVariant, + QFileSystemWatcher, +) from PyQt5.QtGui import QIcon import json from jsonschema import validate, ValidationError, SchemaError + class TreeNode: """ Represents a node in the tree. """ + def __init__(self, name, node_type, status="orange"): self.name = name self.node_type = node_type self.status = status self.children = [] self.parent = None - self.task_status = 'idle' # 'idle', 'running', 'success', 'error' + self.task_status = "idle" # 'idle', 'running', 'success', 'error' def append_child(self, child): child.parent = self @@ -52,10 +60,12 @@ def row(self): def column_count(self): return 1 + class TreeViewModel(QAbstractItemModel): """ Custom model for a tree view, loaded from a JSON file. """ + def __init__(self, json_file, schema_files, parent=None): super(TreeViewModel, self).__init__(parent) self.root_node = TreeNode("Root", "root") @@ -73,19 +83,19 @@ def validate_json(self, data): def _validate_node(self, node, node_type): try: schema_path = self.schema_files[node_type] - with open(schema_path, 'r') as schema_file: + with open(schema_path, "r") as schema_file: schema = json.load(schema_file) validate(instance=node, schema=schema) except (ValidationError, SchemaError) as e: raise ValidationError(f"Invalid {node_type} node: {e.message}") - if 'children' in node: + if "children" in node: child_type = "factor" if node_type == "group" else "sub-factor" - for child in node['children']: + for child in node["children"]: self._validate_node(child, child_type) def load_json(self, json_file): - with open(json_file, 'r') as f: + with open(json_file, "r") as f: data = json.load(f) self.validate_json(data) self.setup_model_data(data, self.root_node) @@ -93,10 +103,10 @@ def load_json(self, json_file): def setup_model_data(self, data, parent_node): parent_node.children.clear() for item in data: - node = TreeNode(item['name'], item['type'], item.get('status', 'orange')) + node = TreeNode(item["name"], item["type"], item.get("status", "orange")) parent_node.append_child(node) - if 'children' in item: - self.setup_model_data(item['children'], node) + if "children" in item: + self.setup_model_data(item["children"], node) self.layoutChanged.emit() def reload_json(self): @@ -104,7 +114,7 @@ def reload_json(self): def is_valid(self): try: - with open(self.json_file, 'r') as f: + with open(self.json_file, "r") as f: data = json.load(f) self.validate_json(data) return True @@ -125,10 +135,10 @@ def data(self, index, role): return node.name elif role == Qt.DecorationRole: icons = { - 'running': QIcon("path/to/running_icon.png"), - 'success': QIcon("path/to/success_icon.png"), - 'error': QIcon("path/to/error_icon.png"), - 'idle': QIcon(f"resources/icons/{node.status}.png") + "running": QIcon("path/to/running_icon.png"), + "success": QIcon("path/to/success_icon.png"), + "error": QIcon("path/to/error_icon.png"), + "idle": QIcon(f"resources/icons/{node.status}.png"), } return icons[node.task_status] return QVariant() @@ -158,6 +168,3 @@ def parent(self, index): child_node = index.internalPointer() parent_node = child_node.parent - - if parent_node == self.root_node: - diff --git a/validate_json.py b/validate_json.py index 92b9abf5..7b5367f1 100644 --- a/validate_json.py +++ b/validate_json.py @@ -17,6 +17,7 @@ from jsonschema import validate, ValidationError, SchemaError import sys + def load_json_schema(schema_file): """ Loads a JSON schema from a file. @@ -27,9 +28,10 @@ def load_json_schema(schema_file): Returns: dict: JSON schema. """ - with open(schema_file, 'r') as f: + with open(schema_file, "r") as f: return json.load(f) + def validate_json(json_data, schema): """ Validates a JSON document against a schema. @@ -43,6 +45,7 @@ def validate_json(json_data, schema): """ validate(instance=json_data, schema=schema) + def main(json_file, schema_file): """ Main function to validate JSON file against a schema. @@ -53,7 +56,7 @@ def main(json_file, schema_file): """ try: schema = load_json_schema(schema_file) - with open(json_file, 'r') as f: + with open(json_file, "r") as f: json_data = json.load(f) validate_json(json_data, schema) print(f"{json_file} is valid.") @@ -62,9 +65,9 @@ def main(json_file, schema_file): except Exception as e: print(f"An error occurred: {e}") + if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: python validate_json.py ") else: main(sys.argv[1], sys.argv[2]) -