From 5aff9ed94474572d55cf8b045014a2ff69e70232 Mon Sep 17 00:00:00 2001 From: Julien Moura Date: Tue, 23 Apr 2024 12:38:19 +0200 Subject: [PATCH 1/9] feature(cli): add command to export rules context in current environment --- qgis_deployment_toolbelt/cli.py | 15 ++- qgis_deployment_toolbelt/commands/__init__.py | 5 - .../commands/rules_context.py | 91 +++++++++++++++++++ tests/test_cli_rules_context_export.py | 50 ++++++++++ 4 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 qgis_deployment_toolbelt/commands/rules_context.py create mode 100644 tests/test_cli_rules_context_export.py diff --git a/qgis_deployment_toolbelt/cli.py b/qgis_deployment_toolbelt/cli.py index 09e5751a..2e54bf2b 100644 --- a/qgis_deployment_toolbelt/cli.py +++ b/qgis_deployment_toolbelt/cli.py @@ -25,7 +25,9 @@ __uri_homepage__, __version__, ) -from qgis_deployment_toolbelt.commands import parser_main_deployment, parser_upgrade +from qgis_deployment_toolbelt.commands.deployment import parser_main_deployment +from qgis_deployment_toolbelt.commands.rules_context import parser_rules_context_export +from qgis_deployment_toolbelt.commands.upgrade import parser_upgrade from qgis_deployment_toolbelt.utils.journalizer import configure_logger # ############################################################################# @@ -166,6 +168,17 @@ def main(in_args: list[str] = None): add_common_arguments(subcmd_deployment) parser_main_deployment(subcmd_deployment) + # Rules context + subcmd_rules_context = subparsers.add_parser( + "export-rules-context", + help="Export QDT rules context taking into account the local environment to " + "help rules writing.", + formatter_class=main_parser.formatter_class, + prog="rules-context-export", + ) + add_common_arguments(subcmd_rules_context) + parser_rules_context_export(subcmd_rules_context) + # Upgrader subcmd_upgrade = subparsers.add_parser( "upgrade", diff --git a/qgis_deployment_toolbelt/commands/__init__.py b/qgis_deployment_toolbelt/commands/__init__.py index 308ad01e..e69de29b 100644 --- a/qgis_deployment_toolbelt/commands/__init__.py +++ b/qgis_deployment_toolbelt/commands/__init__.py @@ -1,5 +0,0 @@ -#! python3 # noqa: E265 - -# submodules -from .deployment import parser_main_deployment # noqa: F401 -from .upgrade import parser_upgrade # noqa: F401 diff --git a/qgis_deployment_toolbelt/commands/rules_context.py b/qgis_deployment_toolbelt/commands/rules_context.py new file mode 100644 index 00000000..68e2c06e --- /dev/null +++ b/qgis_deployment_toolbelt/commands/rules_context.py @@ -0,0 +1,91 @@ +#! python3 # noqa: E265 + + +""" + Sub-command to export local rules context. + + Author: Julien M. (https://github.com/guts) +""" + + +# ############################################################################ +# ########## IMPORTS ############# +# ################################ + +# standard library +import argparse +import logging +from pathlib import Path + +# package +from qgis_deployment_toolbelt.constants import get_qdt_working_directory +from qgis_deployment_toolbelt.profiles.rules_context import QdtRulesContext +from qgis_deployment_toolbelt.utils.bouncer import exit_cli_error, exit_cli_success + +# ############################################################################ +# ########## GLOBALS ############# +# ################################ + +logger = logging.getLogger(__name__) + + +# ############################################################################ +# ########## CLI ################# +# ################################ + + +def parser_rules_context_export( + subparser: argparse.ArgumentParser, +) -> argparse.ArgumentParser: + """Set the argument parser for subcommand. + + Args: + subparser (argparse.ArgumentParser): parser to set up + + Returns: + argparse.ArgumentParser: parser ready to use + """ + + subparser.add_argument( + "-o", + "--output", + help="Path to the output file where to write rules context.", + default=get_qdt_working_directory().joinpath("export/qdt_rules_context.json"), + type=Path, + dest="output_path", + ) + + subparser.set_defaults(func=run) + + return subparser + + +# ############################################################################ +# ########## MAIN ################ +# ################################ + + +def run(args: argparse.Namespace): + """Run the sub command logic. + + Open result of a previous command. + + Args: + args (argparse.Namespace): arguments passed to the subcommand + """ + logger.debug(f"Running {args.command} with {args}") + + try: + context_json_path = Path(args.output_path) + context_json_path.parent.mkdir(parents=True, exist_ok=True) + rules_context = QdtRulesContext() + + # write into the file passing extra parameters to json.dumps + with context_json_path.open("w", encoding="UTF8") as wf: + wf.write(rules_context.to_json(indent=4, sort_keys=True)) + + # exit nicely + print(f"Rules context exported in {args.output_path}") + exit_cli_success(f"Rules context exported in {args.output_path}") + except Exception as err: + exit_cli_error(err) diff --git a/tests/test_cli_rules_context_export.py b/tests/test_cli_rules_context_export.py new file mode 100644 index 00000000..ad4caadf --- /dev/null +++ b/tests/test_cli_rules_context_export.py @@ -0,0 +1,50 @@ +#! python3 # noqa: E265 + +""" + Test CLI's rules context export command. + + Author: Julien Moura (Oslandia) +""" + +# ############################################################################# +# ########## Libraries ############# +# ################################## + + +# 3rd party +import pytest + +# project +from qgis_deployment_toolbelt import cli + +# ############################################################################# +# ######## Classes ################# +# ################################## + + +@pytest.mark.parametrize("option", ("-h", "--help")) +def test_cli_export_rules_context_help(capsys, option): + """Test CLI help.""" + with pytest.raises(SystemExit): + cli.main(["export-rules-context", option]) + + out, err = capsys.readouterr() + + assert err == "" + + +def test_cli_export_rules_context(capsys): + """Test CLI.""" + with pytest.raises(SystemExit): + cli.main(["export-rules-context"]) + + out, err = capsys.readouterr() + + assert err == "" + + +# ############################################################################# +# ######## Standalone ############## +# ################################## +if __name__ == "__main__": + pass From 46221b8e6e4f80705574b7070088bb0688ee44bf Mon Sep 17 00:00:00 2001 From: Julien Moura Date: Tue, 23 Apr 2024 12:41:13 +0200 Subject: [PATCH 2/9] refacto: rename to avoid module conflict --- .../commands/{rules_context.py => cmd_rules_context.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename qgis_deployment_toolbelt/commands/{rules_context.py => cmd_rules_context.py} (100%) diff --git a/qgis_deployment_toolbelt/commands/rules_context.py b/qgis_deployment_toolbelt/commands/cmd_rules_context.py similarity index 100% rename from qgis_deployment_toolbelt/commands/rules_context.py rename to qgis_deployment_toolbelt/commands/cmd_rules_context.py From ffe28b8b8fcf17061941d631540329390a45859d Mon Sep 17 00:00:00 2001 From: Julien Moura Date: Tue, 23 Apr 2024 12:42:05 +0200 Subject: [PATCH 3/9] sonar: clean up --- qgis_deployment_toolbelt/profiles/rules_context.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qgis_deployment_toolbelt/profiles/rules_context.py b/qgis_deployment_toolbelt/profiles/rules_context.py index 2c34bcb4..e34d4ec6 100644 --- a/qgis_deployment_toolbelt/profiles/rules_context.py +++ b/qgis_deployment_toolbelt/profiles/rules_context.py @@ -39,7 +39,6 @@ class QdtRulesContext: - pass @property def _context_date(self) -> dict: From 7b23b97466db8dc6a84dcefb03c828e30b68186e Mon Sep 17 00:00:00 2001 From: Julien Moura Date: Tue, 23 Apr 2024 12:42:30 +0200 Subject: [PATCH 4/9] refacto: rename to avoid module conflict --- qgis_deployment_toolbelt/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qgis_deployment_toolbelt/cli.py b/qgis_deployment_toolbelt/cli.py index 2e54bf2b..c22caae4 100644 --- a/qgis_deployment_toolbelt/cli.py +++ b/qgis_deployment_toolbelt/cli.py @@ -25,8 +25,10 @@ __uri_homepage__, __version__, ) +from qgis_deployment_toolbelt.commands.cmd_rules_context import ( + parser_rules_context_export, +) from qgis_deployment_toolbelt.commands.deployment import parser_main_deployment -from qgis_deployment_toolbelt.commands.rules_context import parser_rules_context_export from qgis_deployment_toolbelt.commands.upgrade import parser_upgrade from qgis_deployment_toolbelt.utils.journalizer import configure_logger From af85d0ca1675c1c57c020797669c6c9ab5ef1539 Mon Sep 17 00:00:00 2001 From: Julien Moura Date: Tue, 23 Apr 2024 12:48:18 +0200 Subject: [PATCH 5/9] quality(sonar): use _ for unpacked but unused vars --- tests/test_cli_main.py | 10 +++++----- tests/test_cli_rules_context_export.py | 4 ++-- tests/test_cli_upgrade.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_cli_main.py b/tests/test_cli_main.py index 3391ffe9..e164988a 100644 --- a/tests/test_cli_main.py +++ b/tests/test_cli_main.py @@ -102,7 +102,7 @@ def test_main_run(capsys, option): with pytest.raises(SystemExit): cli.main(option) - out, err = capsys.readouterr() + _, err = capsys.readouterr() assert err == "" # checks @@ -128,7 +128,7 @@ def test_main_run_as_admin(capsys): ] ) - out, err = capsys.readouterr() + _, err = capsys.readouterr() assert err == "" @@ -142,7 +142,7 @@ def test_main_run_unexising_jobs(capsys): ] ) - out, err = capsys.readouterr() + _, err = capsys.readouterr() assert err == "" @@ -151,7 +151,7 @@ def test_main_run_failed(capsys): with pytest.raises(FileExistsError): cli.main(["deploy", f"--scenario={str(sample_scenario_false.resolve())}"]) - out, err = capsys.readouterr() + _, err = capsys.readouterr() assert err == "" @@ -169,7 +169,7 @@ def test_main_run_removing_splash(capsys): ] ) - out, err = capsys.readouterr() + _, err = capsys.readouterr() assert err == "" diff --git a/tests/test_cli_rules_context_export.py b/tests/test_cli_rules_context_export.py index ad4caadf..d515f23c 100644 --- a/tests/test_cli_rules_context_export.py +++ b/tests/test_cli_rules_context_export.py @@ -28,7 +28,7 @@ def test_cli_export_rules_context_help(capsys, option): with pytest.raises(SystemExit): cli.main(["export-rules-context", option]) - out, err = capsys.readouterr() + _, err = capsys.readouterr() assert err == "" @@ -38,7 +38,7 @@ def test_cli_export_rules_context(capsys): with pytest.raises(SystemExit): cli.main(["export-rules-context"]) - out, err = capsys.readouterr() + _, err = capsys.readouterr() assert err == "" diff --git a/tests/test_cli_upgrade.py b/tests/test_cli_upgrade.py index 0d12f7e3..85a22298 100644 --- a/tests/test_cli_upgrade.py +++ b/tests/test_cli_upgrade.py @@ -38,7 +38,7 @@ def test_cli_upgrade_check_only(capsys): with pytest.raises(SystemExit): cli.main(["upgrade", "-c"]) - out, err = capsys.readouterr() + _, err = capsys.readouterr() assert err == "" @@ -48,7 +48,7 @@ def test_cli_upgrade_download(capsys): with pytest.raises(SystemExit): cli.main(["upgrade", "-n", "-w", "tests/"]) - out, err = capsys.readouterr() + _, err = capsys.readouterr() assert err == "" From 9b4475e49901d0d760f0253a8de634202bf963f1 Mon Sep 17 00:00:00 2001 From: Julien Moura Date: Tue, 23 Apr 2024 12:48:52 +0200 Subject: [PATCH 6/9] quality(sonar): remove unused code block --- tests/test_cli_main.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/test_cli_main.py b/tests/test_cli_main.py index e164988a..945b2151 100644 --- a/tests/test_cli_main.py +++ b/tests/test_cli_main.py @@ -171,10 +171,3 @@ def test_main_run_removing_splash(capsys): _, err = capsys.readouterr() assert err == "" - - -# ############################################################################# -# ######## Standalone ############## -# ################################## -if __name__ == "__main__": - pass From 026a249226fc3f215d49ad6668ac09240bd1f494 Mon Sep 17 00:00:00 2001 From: Julien Moura Date: Tue, 23 Apr 2024 12:51:29 +0200 Subject: [PATCH 7/9] quality(sonar): remove unused block code --- tests/test_cli_rules_context_export.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/test_cli_rules_context_export.py b/tests/test_cli_rules_context_export.py index d515f23c..27472771 100644 --- a/tests/test_cli_rules_context_export.py +++ b/tests/test_cli_rules_context_export.py @@ -41,10 +41,3 @@ def test_cli_export_rules_context(capsys): _, err = capsys.readouterr() assert err == "" - - -# ############################################################################# -# ######## Standalone ############## -# ################################## -if __name__ == "__main__": - pass From 3d6bd55133f35c97f676464fab73031571b7d412 Mon Sep 17 00:00:00 2001 From: Julien Moura Date: Tue, 23 Apr 2024 13:02:13 +0200 Subject: [PATCH 8/9] docs(rules): add export command in rules section --- docs/reference/qdt_profile.md | 10 ++++++++- docs/usage/cli.md | 2 -- docs/usage/download_section.md | 37 ++++++++++++++++++++++++++++++---- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/docs/reference/qdt_profile.md b/docs/reference/qdt_profile.md index 4d1f4367..b62676f7 100644 --- a/docs/reference/qdt_profile.md +++ b/docs/reference/qdt_profile.md @@ -2,6 +2,8 @@ ## Rules +> Added in version 0.34 + You can add rules to make the profile deployment conditional. In the following example, the profile will be deployed only on Linux: ```json @@ -35,7 +37,7 @@ You can add rules to make the profile deployment conditional. In the following e The rules engine is based on [Python Rule Engine](https://github.com/santalvarez/python-rule-engine/) project whom rules syntax belongs to [JSON Rules Engine](https://github.com/CacheControl/json-rules-engine). -### Conditions +### Conditions and rules context Rules is a set of conditions that use logical operators to compare values with context (a set of facts) which is exposed as a JSON object. Here comes the context for a Linux environment: @@ -44,6 +46,12 @@ Rules is a set of conditions that use logical operators to compare values with c :language: json ``` +To help you writing rules, QDT provides a [command to export rules context](../usage/cli.md#rules-context-export): + +```sh +qdt export-rules-context -o qdt_rules_context.json +``` + ---- ## Model definition diff --git a/docs/usage/cli.md b/docs/usage/cli.md index 3efa3697..cb28d77a 100644 --- a/docs/usage/cli.md +++ b/docs/usage/cli.md @@ -1,7 +1,5 @@ # Command-line interface usage -## Main command - Aliases : `qdt`, `qgis-deployment-toolbelt`, `qdeploy-toolbelt` ```{sphinx_argparse_cli} diff --git a/docs/usage/download_section.md b/docs/usage/download_section.md index d2a90aa2..99969eff 100644 --- a/docs/usage/download_section.md +++ b/docs/usage/download_section.md @@ -1,5 +1,34 @@ - +```{button-link} https://github.com/Guts/qgis-deployment-cli/releases/download/0.33.0/Ubuntu_QGISDeploymentToolbelt_0-33-0 +:color: primary +:shadow: +:tooltip: Generated with PyInstaller on Ubuntu LTS + +{fab}`linux` Download for Linux +``` +::: +:::{grid-item} + +```{button-link} https://github.com/Guts/qgis-deployment-cli/releases/download/0.33.0/MacOS_QGISDeploymentToolbelt_0-33-0 +:color: primary +:shadow: +:tooltip: Generated with PyInstaller on MacOS 12.6 + +{fab}`apple` Download for MacOS +``` +:::{grid-item} +::: + +```{button-link} https://github.com/Guts/qgis-deployment-cli/releases/download/0.33.0/Windows_QGISDeploymentToolbelt_0-33-0.exe +:color: primary +:shadow: +:tooltip: Generated with PyInstaller on Windows 10 + +{fab}`windows` Download for Windows +``` +::: + +:::: From 2399230f9e358232a84b2fdf346bdadafaf10f43 Mon Sep 17 00:00:00 2001 From: Julien Moura Date: Tue, 23 Apr 2024 14:19:40 +0200 Subject: [PATCH 9/9] git: restore file to main state --- docs/usage/download_section.md | 37 ++++------------------------------ 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/docs/usage/download_section.md b/docs/usage/download_section.md index 99969eff..d2a90aa2 100644 --- a/docs/usage/download_section.md +++ b/docs/usage/download_section.md @@ -1,34 +1,5 @@ -::::{grid} 3 -:gutter: 2 -:::{grid-item} +