From 3add49356bbcd14870360fcdd04ecaa453f7320c Mon Sep 17 00:00:00 2001 From: bakebot Date: Thu, 8 Aug 2024 18:27:31 +0000 Subject: [PATCH 1/4] Cookie updated by NetworkToCode Cookie Drift Manager Tool Template: ``` { "template": "https://github.com/nautobot/cookiecutter-nautobot-app.git", "dir": "nautobot-app", "ref": "refs/tags/nautobot-app-v2.3.0", "path": null } ``` Cookie: ``` { "remote": "https://github.com/nautobot/nautobot-app-floor-plan.git", "path": "/tmp/tmpxip7373v/nautobot-app-floor-plan", "repository_path": "/tmp/tmpxip7373v/nautobot-app-floor-plan", "dir": "", "branch_prefix": "drift-manager", "context": { "codeowner_github_usernames": "\\", "full_name": "Network to Code, LLC", "email": "info@networktocode.com", "github_org": "nautobot", "app_name": "nautobot_floor_plan", "verbose_name": "Nautobot Floor Plan", "app_slug": "nautobot-floor-plan", "project_slug": "nautobot-app-floor-plan", "repo_url": "https://github.com/nautobot/nautobot-app-floor-plan", "base_url": "floor-plan", "min_nautobot_version": "2.0.0", "max_nautobot_version": "2.9999", "camel_name": "FloorPlan", "project_short_description": "Nautobot Floor Plan", "model_class_name": "FloorPlan", "open_source_license": "Apache-2.0", "docs_base_url": "https://docs.nautobot.com", "docs_app_url": "https://docs.nautobot.com/projects/floor-plan/en/latest", "_template": "https://github.com/nautobot/cookiecutter-nautobot-app.git", "_output_dir": "/tmp/tmpxip7373v", "_repo_dir": "/github/home/.cookiecutters/cookiecutter-nautobot-app/nautobot-app", "_checkout": "refs/tags/nautobot-app-v2.3.0" }, "base_branch": "develop", "remote_name": "origin", "pull_request_strategy": "PullRequestStrategy.CREATE", "post_actions": [ "PostAction.BLACK" ], "baked_commit_ref": "24156e9314494943cad462ccff41414f40e977a6", "draft": true } ``` CLI Arguments: ``` { "cookie_dir": "", "input": false, "json_filename": "", "output_dir": "", "push": true, "template": "", "template_dir": "", "template_ref": "refs/tags/nautobot-app-v2.3.0", "pull_request": null, "post_action": [], "disable_post_actions": false, "draft": null } ``` --- .cookiecutter.json | 4 +- .dockerignore | 1 - .flake8 | 10 -- .github/workflows/ci.yml | 24 +--- README.md | 2 +- development/app_config_schema.py | 4 +- development/nautobot_config.py | 17 ++- docs/assets/extra.css | 2 +- docs/dev/contributing.md | 2 +- docs/dev/dev_environment.md | 8 +- docs/requirements.txt | 2 +- mkdocs.yml | 3 +- nautobot_floor_plan/api/serializers.py | 4 +- nautobot_floor_plan/filters.py | 10 +- nautobot_floor_plan/forms.py | 66 ++------- nautobot_floor_plan/models.py | 7 +- nautobot_floor_plan/navigation.py | 33 ++--- nautobot_floor_plan/tests/test_api.py | 3 +- nautobot_floor_plan/tests/test_basic.py | 28 ++-- .../tests/test_filter_floorplan.py | 28 ++++ .../tests/test_model_floorplan.py | 24 ++++ nautobot_floor_plan/urls.py | 6 - pyproject.toml | 54 ++----- tasks.py | 134 ++++++++++-------- 24 files changed, 226 insertions(+), 250 deletions(-) delete mode 100644 .flake8 create mode 100644 nautobot_floor_plan/tests/test_filter_floorplan.py create mode 100644 nautobot_floor_plan/tests/test_model_floorplan.py diff --git a/.cookiecutter.json b/.cookiecutter.json index cd86d31..7aad8ac 100644 --- a/.cookiecutter.json +++ b/.cookiecutter.json @@ -21,7 +21,7 @@ "_drift_manager": { "template": "https://github.com/nautobot/cookiecutter-nautobot-app.git", "template_dir": "nautobot-app", - "template_ref": "refs/tags/nautobot-app-v2.2.1", + "template_ref": "refs/tags/nautobot-app-v2.3.0", "cookie_dir": "", "branch_prefix": "drift-manager", "pull_request_strategy": "create", @@ -29,7 +29,7 @@ "black" ], "draft": true, - "baked_commit_ref": "24156e9314494943cad462ccff41414f40e977a6" + "baked_commit_ref": "656df98eb8f7516e6f851a26557892b0181a3b37" } } } \ No newline at end of file diff --git a/.dockerignore b/.dockerignore index 2270f49..a0bf06f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -19,7 +19,6 @@ FAQ.md .git/ .gitignore .github -tasks.py LICENSE **/*.log **/.vscode/ diff --git a/.flake8 b/.flake8 deleted file mode 100644 index c9f5e84..0000000 --- a/.flake8 +++ /dev/null @@ -1,10 +0,0 @@ -[flake8] -ignore = - E501, # Line length is enforced by Black, so flake8 doesn't need to check it - W503 # Black disagrees with this rule, as does PEP 8; Black wins -exclude = - migrations, - __pycache__, - manage.py, - settings.py, - .venv diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14fb5de..2204300 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ env: APP_NAME: "nautobot-app-floor-plan" jobs: - black: + ruff-format: runs-on: "ubuntu-22.04" env: INVOKE_NAUTOBOT_FLOOR_PLAN_LOCAL: "True" @@ -25,8 +25,8 @@ jobs: uses: "actions/checkout@v4" - name: "Setup environment" uses: "networktocode/gh-action-setup-poetry-environment@v6" - - name: "Linting: black" - run: "poetry run invoke black" + - name: "Linting: ruff format" + run: "poetry run invoke ruff --action format" bandit: runs-on: "ubuntu-22.04" env: @@ -38,7 +38,7 @@ jobs: uses: "networktocode/gh-action-setup-poetry-environment@v6" - name: "Linting: bandit" run: "poetry run invoke bandit" - ruff: + ruff-lint: runs-on: "ubuntu-22.04" env: INVOKE_NAUTOBOT_FLOOR_PLAN_LOCAL: "True" @@ -60,17 +60,6 @@ jobs: uses: "networktocode/gh-action-setup-poetry-environment@v6" - name: "Check Docs Build" run: "poetry run invoke build-and-check-docs" - flake8: - runs-on: "ubuntu-22.04" - env: - INVOKE_NAUTOBOT_FLOOR_PLAN_LOCAL: "True" - steps: - - name: "Check out repository code" - uses: "actions/checkout@v4" - - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v6" - - name: "Linting: flake8" - run: "poetry run invoke flake8" poetry: runs-on: "ubuntu-22.04" env: @@ -96,11 +85,10 @@ jobs: check-in-docker: needs: - "bandit" - - "ruff" - - "flake8" + - "ruff-format" + - "ruff-lint" - "poetry" - "yamllint" - - "black" runs-on: "ubuntu-22.04" strategy: fail-fast: true diff --git a/README.md b/README.md index f09cb09..86eb529 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@
- An App for Nautobot. + An App for Nautobot.

## Overview diff --git a/development/app_config_schema.py b/development/app_config_schema.py index a779b14..e52e247 100644 --- a/development/app_config_schema.py +++ b/development/app_config_schema.py @@ -40,7 +40,9 @@ def _main(): **SchemaBuilder().to_json_schema(app_config), # type: ignore } app_config = import_module(package_name).config - _enrich_object_schema(schema, app_config.default_settings, app_config.required_settings) + _enrich_object_schema( + schema, app_config.default_settings, app_config.required_settings + ) schema_path.write_text(json.dumps(schema, indent=4) + "\n") print(f"\n==================\nGenerated schema:\n\n{schema_path}\n") print( diff --git a/development/nautobot_config.py b/development/nautobot_config.py index b78a4b9..2b388b3 100644 --- a/development/nautobot_config.py +++ b/development/nautobot_config.py @@ -10,7 +10,7 @@ # Debug # -DEBUG = is_truthy(os.getenv("NAUTOBOT_DEBUG", False)) +DEBUG = is_truthy(os.getenv("NAUTOBOT_DEBUG", "false")) _TESTING = len(sys.argv) > 1 and sys.argv[1] == "test" if DEBUG and not _TESTING: @@ -18,8 +18,12 @@ if "debug_toolbar" not in INSTALLED_APPS: # noqa: F405 INSTALLED_APPS.append("debug_toolbar") # noqa: F405 - if "debug_toolbar.middleware.DebugToolbarMiddleware" not in MIDDLEWARE: # noqa: F405 - MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware") # noqa: F405 + if ( + "debug_toolbar.middleware.DebugToolbarMiddleware" not in MIDDLEWARE + ): # noqa: F405 + MIDDLEWARE.insert( + 0, "debug_toolbar.middleware.DebugToolbarMiddleware" + ) # noqa: F405 # # Misc. settings @@ -48,9 +52,12 @@ "PASSWORD": os.getenv("NAUTOBOT_DB_PASSWORD", ""), # Database password "HOST": os.getenv("NAUTOBOT_DB_HOST", "localhost"), # Database server "PORT": os.getenv( - "NAUTOBOT_DB_PORT", default_db_settings[nautobot_db_engine]["NAUTOBOT_DB_PORT"] + "NAUTOBOT_DB_PORT", + default_db_settings[nautobot_db_engine]["NAUTOBOT_DB_PORT"], ), # Database port, default to postgres - "CONN_MAX_AGE": int(os.getenv("NAUTOBOT_DB_TIMEOUT", 300)), # Database timeout + "CONN_MAX_AGE": int( + os.getenv("NAUTOBOT_DB_TIMEOUT", "300") + ), # Database timeout "ENGINE": nautobot_db_engine, } } diff --git a/docs/assets/extra.css b/docs/assets/extra.css index 1eff119..3f3931a 100644 --- a/docs/assets/extra.css +++ b/docs/assets/extra.css @@ -96,7 +96,7 @@ a.autorefs-external:hover::after { } -/* Customization for mkdocs-version-annotations */ +/* Customization for markdown-version-annotations */ :root { /* Icon for "version-added" admonition: Material Design Icons "plus-box-outline" */ --md-admonition-icon--version-added: url('data:image/svg+xml;charset=utf-8,'); diff --git a/docs/dev/contributing.md b/docs/dev/contributing.md index 480d7b9..5386d0f 100644 --- a/docs/dev/contributing.md +++ b/docs/dev/contributing.md @@ -4,7 +4,7 @@ The project is packaged with a light [development environment](dev_environment.m The project is following Network to Code software development guidelines and is leveraging the following: -- Python linting and formatting: `black`, `pylint`, `bandit`, `flake8`, and `ruff`. +- Python linting and formatting: `pylint`, `bandit`, and `ruff`. - YAML linting is done with `yamllint`. - Django unit test to ensure the app is working properly. diff --git a/docs/dev/dev_environment.md b/docs/dev/dev_environment.md index 41b7d8a..3f313dd 100644 --- a/docs/dev/dev_environment.md +++ b/docs/dev/dev_environment.md @@ -124,9 +124,7 @@ Each command can be executed with `invoke `. All commands support the a ```shell bandit Run bandit to validate basic static code security analysis. - black Run black to check that Python files adhere to its style standards. - flake8 Run flake8 to check that Python files adhere to its style standards. - ruff Run ruff to validate docstring formatting adheres to NTC defined standards. + ruff Run ruff to perform code formatting and/or linting. pylint Run pylint code analysis. tests Run all tests for this app. unittest Run Django unit tests for the app. @@ -454,7 +452,7 @@ This is the same as running: ### Tests -To run tests against your code, you can run all of the tests that TravisCI runs against any new PR with: +To run tests against your code, you can run all of the tests that the CI runs against any new PR with: ```bash ➜ invoke tests @@ -465,8 +463,6 @@ To run an individual test, you can run any or all of the following: ```bash ➜ invoke unittest ➜ invoke bandit -➜ invoke black -➜ invoke flake8 ➜ invoke ruff ➜ invoke pylint ``` diff --git a/docs/requirements.txt b/docs/requirements.txt index d168c88..1d89ad0 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ mkdocs==1.5.2 mkdocs-material==9.1.15 -mkdocs-version-annotations==1.0.0 +markdown-version-annotations==1.0.1 mkdocstrings-python==1.5.2 mkdocstrings==0.22.0 diff --git a/mkdocs.yml b/mkdocs.yml index ed86e07..98fe862 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,6 +72,8 @@ extra: link: "https://twitter.com/networktocode" name: "Network to Code Twitter" markdown_extensions: + - "markdown_version_annotations": + admonition_tag: "???" - "admonition" - "toc": permalink: true @@ -89,7 +91,6 @@ markdown_extensions: - "footnotes" plugins: - "search" - - "mkdocs-version-annotations" - "mkdocstrings": default_handler: "python" handlers: diff --git a/nautobot_floor_plan/api/serializers.py b/nautobot_floor_plan/api/serializers.py index e4800a5..d2a0681 100644 --- a/nautobot_floor_plan/api/serializers.py +++ b/nautobot_floor_plan/api/serializers.py @@ -5,7 +5,9 @@ from nautobot_floor_plan import models -class FloorPlanSerializer(NautobotModelSerializer, TaggedModelSerializerMixin): # pylint: disable=too-many-ancestors +class FloorPlanSerializer( + NautobotModelSerializer, TaggedModelSerializerMixin +): # pylint: disable=too-many-ancestors """FloorPlan Serializer.""" class Meta: diff --git a/nautobot_floor_plan/filters.py b/nautobot_floor_plan/filters.py index b2e303d..495d41a 100644 --- a/nautobot_floor_plan/filters.py +++ b/nautobot_floor_plan/filters.py @@ -1,15 +1,13 @@ """Filtering for nautobot_floor_plan.""" -import django_filters - -from nautobot.dcim.models import Location, Rack, RackGroup -from nautobot.apps.filters import NautobotFilterSet -from nautobot.apps.filters import NaturalKeyOrPKMultipleChoiceFilter, SearchFilter +from nautobot.apps.filters import NameSearchFilterSet, NautobotFilterSet from nautobot_floor_plan import models -class FloorPlanFilterSet(NautobotFilterSet): +class FloorPlanFilterSet( + NautobotFilterSet, NameSearchFilterSet +): # pylint: disable=too-many-ancestors """Filter for FloorPlan.""" q = SearchFilter( diff --git a/nautobot_floor_plan/forms.py b/nautobot_floor_plan/forms.py index ed88318..2c66320 100644 --- a/nautobot_floor_plan/forms.py +++ b/nautobot_floor_plan/forms.py @@ -5,6 +5,12 @@ """Forms for nautobot_floor_plan.""" from django import forms +from nautobot.apps.forms import ( + NautobotBulkEditForm, + NautobotFilterForm, + NautobotModelForm, + TagsBulkEditFormMixin, +) from nautobot.dcim.models import Location, Rack, RackGroup from nautobot.apps.forms import ( @@ -59,61 +65,15 @@ def __init__(self, *args, **kwargs): """Overwrite the constructor to set initial values for select widget.""" super().__init__(*args, **kwargs) - if not self.instance.created: - self.initial["x_axis_labels"] = get_app_settings_or_config("nautobot_floor_plan", "default_x_axis_labels") - self.initial["y_axis_labels"] = get_app_settings_or_config("nautobot_floor_plan", "default_y_axis_labels") - self.x_letters = self.initial["x_axis_labels"] == choices.AxisLabelsChoices.LETTERS - self.y_letters = self.initial["y_axis_labels"] == choices.AxisLabelsChoices.LETTERS - self.initial["x_origin_seed"] = "A" if self.x_letters else "1" - self.initial["y_origin_seed"] = "A" if self.y_letters else "1" - else: - self.x_letters = self.instance.x_axis_labels == choices.AxisLabelsChoices.LETTERS - self.y_letters = self.instance.y_axis_labels == choices.AxisLabelsChoices.LETTERS - - if self.x_letters and str(self.initial["y_origin_seed"]).isdigit(): - self.initial["x_origin_seed"] = utils.grid_number_to_letter(self.instance.x_origin_seed) - if self.y_letters and str(self.initial["y_origin_seed"]).isdigit(): - self.initial["y_origin_seed"] = utils.grid_number_to_letter(self.instance.y_origin_seed) - - def _clean_origin_seed(self, field_name, axis): - """Common clean method for origin_seed fields.""" - value = self.cleaned_data.get(field_name) - if not value: - return 1 - - self.x_letters = self.cleaned_data.get("x_axis_labels") == choices.AxisLabelsChoices.LETTERS - self.y_letters = self.cleaned_data.get("y_axis_labels") == choices.AxisLabelsChoices.LETTERS - - if self.x_letters and field_name == "x_origin_seed" or self.y_letters and field_name == "y_origin_seed": - if not str(value).isupper(): - self.add_error(field_name, f"{axis} origin start should use capital letters.") - return 0 - return utils.grid_letter_to_number(value) - - if not str(value).isdigit(): - self.add_error(field_name, f"{axis} origin start should use numbers.") - return 0 - return int(value) - - def clean_x_origin_seed(self): - """Validate input and convert y_origin to an integer.""" - return self._clean_origin_seed("x_origin_seed", "X") - - def clean_y_origin_seed(self): - """Validate input and convert y_origin to an integer.""" - return self._clean_origin_seed("y_origin_seed", "Y") - - -class FloorPlanBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm): +class FloorPlanBulkEditForm( + TagsBulkEditFormMixin, NautobotBulkEditForm +): # pylint: disable=too-many-ancestors """FloorPlan bulk edit form.""" - pk = forms.ModelMultipleChoiceField(queryset=models.FloorPlan.objects.all(), widget=forms.MultipleHiddenInput) - x_size = forms.IntegerField(min_value=1, required=False) - y_size = forms.IntegerField(min_value=1, required=False) - tile_width = forms.IntegerField(min_value=1, required=False) - tile_depth = forms.IntegerField(min_value=1, required=False) - x_axis_labels = forms.ChoiceField(choices=add_blank_choice(choices.AxisLabelsChoices), required=False) - y_axis_labels = forms.ChoiceField(choices=add_blank_choice(choices.AxisLabelsChoices), required=False) + pk = forms.ModelMultipleChoiceField( + queryset=models.FloorPlan.objects.all(), widget=forms.MultipleHiddenInput + ) + description = forms.CharField(required=False) class Meta: """Meta attributes.""" diff --git a/nautobot_floor_plan/models.py b/nautobot_floor_plan/models.py index c2068fc..aeee39b 100644 --- a/nautobot_floor_plan/models.py +++ b/nautobot_floor_plan/models.py @@ -13,8 +13,11 @@ from nautobot_floor_plan.choices import RackOrientationChoices, AxisLabelsChoices, AllocationTypeChoices from nautobot_floor_plan.svg import FloorPlanSVG - -logger = logging.getLogger(__name__) +# from nautobot.extras.utils import extras_features +# If you want to use the extras_features decorator please reference the following documentation +# https://docs.nautobot.com/projects/core/en/latest/plugins/development/#using-the-extras_features-decorator-for-graphql +# Then based on your reading you may decide to put the following decorator before the declaration of your class +# @extras_features("custom_fields", "custom_validators", "relationships", "graphql") @extras_features( diff --git a/nautobot_floor_plan/navigation.py b/nautobot_floor_plan/navigation.py index 9a56c8b..dd757ae 100644 --- a/nautobot_floor_plan/navigation.py +++ b/nautobot_floor_plan/navigation.py @@ -2,30 +2,15 @@ from nautobot.apps.ui import NavMenuGroup, NavMenuItem, NavMenuTab, NavMenuAddButton, NavMenuImportButton -menu_items = ( - NavMenuTab( - name="Organization", - groups=( - NavMenuGroup( - name="Locations", - items=( - NavMenuItem( - name="Location Floor Plans", - link="plugins:nautobot_floor_plan:floorplan_list", - weight=300, - permissions=["nautobot_floor_plan.view_floorplan"], - buttons=( - NavMenuAddButton( - link="plugins:nautobot_floor_plan:floorplan_add", - permissions=["nautobot_floor_plan.add_floorplan"], - ), - NavMenuImportButton( - link="plugins:nautobot_floor_plan:floorplan_import", - permissions=["nautobot_floor_plan.add_floorplan"], - ), - ), - ), - ), +items = ( + NavMenuItem( + link="plugins:nautobot_floor_plan:floorplan_list", + name="Nautobot Floor Plan", + permissions=["nautobot_floor_plan.view_floorplan"], + buttons=( + NavMenuAddButton( + link="plugins:nautobot_floor_plan:floorplan_add", + permissions=["nautobot_floor_plan.add_floorplan"], ), ), ), diff --git a/nautobot_floor_plan/tests/test_api.py b/nautobot_floor_plan/tests/test_api.py index 30bddea..97ca10f 100644 --- a/nautobot_floor_plan/tests/test_api.py +++ b/nautobot_floor_plan/tests/test_api.py @@ -3,11 +3,10 @@ from django.contrib.auth import get_user_model from django.test import TestCase from django.urls import reverse +from nautobot.users.models import Token from rest_framework import status from rest_framework.test import APIClient -from nautobot.users.models import Token - User = get_user_model() diff --git a/nautobot_floor_plan/tests/test_basic.py b/nautobot_floor_plan/tests/test_basic.py index 30fc318..83142fa 100644 --- a/nautobot_floor_plan/tests/test_basic.py +++ b/nautobot_floor_plan/tests/test_basic.py @@ -1,7 +1,8 @@ """Basic tests that do not require Django.""" -import unittest import os +import unittest + import toml @@ -10,14 +11,25 @@ class TestDocsPackaging(unittest.TestCase): def test_version(self): """Verify that pyproject.toml dev dependencies have the same versions as in the docs requirements.txt.""" - parent_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + parent_path = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + ) poetry_path = os.path.join(parent_path, "pyproject.toml") - poetry_details = toml.load(poetry_path)["tool"]["poetry"]["group"]["dev"]["dependencies"] - with open(f"{parent_path}/docs/requirements.txt", "r", encoding="utf-8") as file: - requirements = [line for line in file.read().splitlines() if (len(line) > 0 and not line.startswith("#"))] + poetry_details = toml.load(poetry_path)["tool"]["poetry"]["group"]["dev"][ + "dependencies" + ] + with open( + f"{parent_path}/docs/requirements.txt", "r", encoding="utf-8" + ) as file: + requirements = [ + line + for line in file.read().splitlines() + if (len(line) > 0 and not line.startswith("#")) + ] for pkg in requirements: - if len(pkg.split("==")) == 2: - pkg, version = pkg.split("==") + package_name = pkg + if len(pkg.split("==")) == 2: # noqa: PLR2004 + package_name, version = pkg.split("==") else: version = "*" - self.assertEqual(poetry_details[pkg], version) + self.assertEqual(poetry_details[package_name], version) diff --git a/nautobot_floor_plan/tests/test_filter_floorplan.py b/nautobot_floor_plan/tests/test_filter_floorplan.py new file mode 100644 index 0000000..28333f6 --- /dev/null +++ b/nautobot_floor_plan/tests/test_filter_floorplan.py @@ -0,0 +1,28 @@ +"""Test FloorPlan Filter.""" + +from django.test import TestCase + +from nautobot_floor_plan import filters, models +from nautobot_floor_plan.tests import fixtures + + +class FloorPlanFilterTestCase(TestCase): + """FloorPlan Filter Test Case.""" + + queryset = models.FloorPlan.objects.all() + filterset = filters.FloorPlanFilterSet + + @classmethod + def setUpTestData(cls): + """Setup test data for FloorPlan Model.""" + fixtures.create_floorplan() + + def test_q_search_name(self): + """Test using Q search with name of FloorPlan.""" + params = {"q": "Test One"} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) + + def test_q_invalid(self): + """Test using invalid Q search for FloorPlan.""" + params = {"q": "test-five"} + self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) diff --git a/nautobot_floor_plan/tests/test_model_floorplan.py b/nautobot_floor_plan/tests/test_model_floorplan.py new file mode 100644 index 0000000..f369a51 --- /dev/null +++ b/nautobot_floor_plan/tests/test_model_floorplan.py @@ -0,0 +1,24 @@ +"""Test FloorPlan.""" + +from django.test import TestCase + +from nautobot_floor_plan import models + + +class TestFloorPlan(TestCase): + """Test FloorPlan.""" + + def test_create_floorplan_only_required(self): + """Create with only required fields, and validate null description and __str__.""" + floorplan = models.FloorPlan.objects.create(name="Development") + self.assertEqual(floorplan.name, "Development") + self.assertEqual(floorplan.description, "") + self.assertEqual(str(floorplan), "Development") + + def test_create_floorplan_all_fields_success(self): + """Create FloorPlan with all fields.""" + floorplan = models.FloorPlan.objects.create( + name="Development", description="Development Test" + ) + self.assertEqual(floorplan.name, "Development") + self.assertEqual(floorplan.description, "Development Test") diff --git a/nautobot_floor_plan/urls.py b/nautobot_floor_plan/urls.py index 8fced6f..ebcadcd 100644 --- a/nautobot_floor_plan/urls.py +++ b/nautobot_floor_plan/urls.py @@ -1,16 +1,10 @@ """Django urlpatterns declaration for nautobot_floor_plan app.""" -from django.urls import path -from django.templatetags.static import static -from django.views.generic import RedirectView - from nautobot.apps.urls import NautobotUIViewSetRouter from nautobot.extras.views import ObjectChangeLogView, ObjectNotesView from nautobot_floor_plan import models, views - -app_name = "floor_plan" router = NautobotUIViewSetRouter() router.register("floor-plans", views.FloorPlanUIViewSet) router.register("floor-plan-tiles", views.FloorPlanTileUIViewSet) diff --git a/pyproject.toml b/pyproject.toml index b509f33..bbba61a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,25 +33,23 @@ nautobot = "^2.0.0" [tool.poetry.group.dev.dependencies] bandit = "*" -black = "*" coverage = "*" django-debug-toolbar = "*" -flake8 = "*" invoke = "*" ipython = "*" pylint = "*" pylint-django = "*" pylint-nautobot = "*" -ruff = "*" +ruff = "0.5.5" yamllint = "*" toml = "*" Markdown = "*" +# Render custom markdown for version added/changed/remove notes +markdown-version-annotations = "1.0.1" # Rendering docs to HTML mkdocs = "1.5.2" # Material for MkDocs theme mkdocs-material = "9.1.15" -# Render custom markdown for version added/changed/remove notes -mkdocs-version-annotations = "1.0.0" # Automatic documentation from sources, for MkDocs mkdocstrings = "0.22.0" mkdocstrings-python = "1.5.2" @@ -63,29 +61,6 @@ jsonschema = "*" all = [ ] -[tool.black] -line-length = 120 -target-version = ['py38', 'py39', 'py310', 'py311'] -include = '\.pyi?$' -exclude = ''' -( - /( - \.eggs # exclude a few common directories in the - | \.git # root of the project - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | dist - )/ - | settings.py # This is where you define files that should not be stylized by black - # the root of the project -) -''' - [tool.pylint.master] # Include the pylint_django plugin to avoid spurious warnings about Django patterns load-plugins = [ @@ -106,16 +81,9 @@ no-docstring-rgx="^(_|test_|Meta$)" good-names = """_,i,j,k,x,y""" [tool.pylint.messages_control] -# Line length is enforced by Black, so pylint doesn't need to check it. -# Pylint and Black disagree about how to format multi-line arrays; Black wins. -# too-few-public-methods is just noise -# too-many-ancestors happens all over the place in Django/Nautobot code, so it's pretty much just noise disable = """, - duplicate-code, - line-too-long, - too-few-public-methods, - too-many-ancestors - """ + line-too-long +""" [tool.pylint.miscellaneous] # Don't flag TODO as a failure, let us commit with things that still need to be done in the code @@ -136,6 +104,10 @@ target-version = "py38" [tool.ruff.lint] select = [ "D", # pydocstyle + "F", "E", "W", # flake8 + "PL", # pylint + "S", # bandit + "I", # isort ] ignore = [ # warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. @@ -153,17 +125,19 @@ ignore = [ "D401", # First line of docstring should be in imperative mood "D407", # Missing dashed underline after section "D416", # Section name ends in colon + "E501", # Line too long ] [tool.ruff.lint.pydocstyle] convention = "google" -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "nautobot_floor_plan/migrations/*" = [ - "D", # pydocstyle + "D", ] "nautobot_floor_plan/tests/*" = [ - "D", # pydocstyle + "D", + "S" ] [build-system] diff --git a/tasks.py b/tasks.py index 80e4dcc..1599577 100644 --- a/tasks.py +++ b/tasks.py @@ -70,7 +70,9 @@ def _is_compose_included(context, name): def _await_healthy_service(context, service): - container_id = docker_compose(context, f"ps -q -- {service}", pty=False, echo=False, hide=True).stdout.strip() + container_id = docker_compose( + context, f"ps -q -- {service}", pty=False, echo=False, hide=True + ).stdout.strip() _await_healthy_container(context, container_id) @@ -130,7 +132,9 @@ def docker_compose(context, command, **kwargs): ] for compose_file in context.nautobot_floor_plan.compose_files: - compose_file_path = os.path.join(context.nautobot_floor_plan.compose_dir, compose_file) + compose_file_path = os.path.join( + context.nautobot_floor_plan.compose_dir, compose_file + ) compose_command_tokens.append(f' -f "{compose_file_path}"') compose_command_tokens.append(command) @@ -159,17 +163,19 @@ def run_command(context, command, **kwargs): # Check if nautobot is running, no need to start another nautobot container to run a command docker_compose_status = "ps --services --filter status=running" results = docker_compose(context, docker_compose_status, hide="out") - if "nautobot" in results.stdout: - compose_command = "exec" - else: - compose_command = "run --rm --entrypoint=''" + command_env_args = "" if "command_env" in kwargs: command_env = kwargs.pop("command_env") for key, value in command_env.items(): - compose_command += f' --env="{key}={value}"' + command_env_args += f' --env="{key}={value}"' - compose_command += f" -- nautobot {command}" + if "nautobot" in results.stdout: + compose_command = f"exec{command_env_args} nautobot {command}" + else: + compose_command = ( + f"run{command_env_args} --rm --entrypoint='{command}' nautobot" + ) pty = kwargs.pop("pty", True) @@ -246,7 +252,9 @@ def restart(context, service=""): def stop(context, service=""): """Stop specified or all services, if service is not specified, remove all containers.""" print("Stopping Nautobot...") - docker_compose(context, "stop" if service else "down --remove-orphans", service=service) + docker_compose( + context, "stop" if service else "down --remove-orphans", service=service + ) @task( @@ -265,7 +273,9 @@ def destroy(context, volumes=True, import_db_file=""): return if not volumes: - raise ValueError("Cannot specify `--no-volumes` and `--import-db-file` arguments at the same time.") + raise ValueError( + "Cannot specify `--no-volumes` and `--import-db-file` arguments at the same time." + ) print(f"Importing database file: {import_db_file}...") @@ -282,12 +292,16 @@ def destroy(context, volumes=True, import_db_file=""): "db", ] - container_id = docker_compose(context, " ".join(command), pty=False, echo=False, hide=True).stdout.strip() + container_id = docker_compose( + context, " ".join(command), pty=False, echo=False, hide=True + ).stdout.strip() _await_healthy_container(context, container_id) print("Stopping database container...") context.run(f"docker stop {container_id}", pty=False, echo=False, hide=True) - print("Database import complete, you can start Nautobot with the following command:") + print( + "Database import complete, you can start Nautobot with the following command:" + ) print("invoke start") @@ -459,7 +473,9 @@ def dbshell(context, db_name="", input_file="", output_file="", query=""): if input_file and query: raise ValueError("Cannot specify both, `input_file` and `query` arguments") if output_file and not (input_file or query): - raise ValueError("`output_file` argument requires `input_file` or `query` argument") + raise ValueError( + "`output_file` argument requires `input_file` or `query` argument" + ) env = {} if query: @@ -494,7 +510,12 @@ def dbshell(context, db_name="", input_file="", output_file="", query=""): f"> '{output_file}'" if output_file else "", ] - docker_compose(context, " ".join(command), env=env, pty=not (input_file or output_file or query)) + docker_compose( + context, + " ".join(command), + env=env, + pty=not (input_file or output_file or query), + ) @task( @@ -592,7 +613,9 @@ def backup_db(context, db_name="", output_file="dump.sql", readable=True): docker_compose(context, " ".join(command), pty=False) print(50 * "=") - print("The database backup has been successfully completed and saved to the following file:") + print( + "The database backup has been successfully completed and saved to the following file:" + ) print(output_file) print("You can import this database backup with the following command:") print(f"invoke import-db --input-file '{output_file}'") @@ -649,28 +672,6 @@ def generate_release_notes(context, version=""): # ------------------------------------------------------------------------------ # TESTS # ------------------------------------------------------------------------------ -@task( - help={ - "autoformat": "Apply formatting recommendations automatically, rather than failing if formatting is incorrect.", - } -) -def black(context, autoformat=False): - """Check Python code style with Black.""" - if autoformat: - black_command = "black" - else: - black_command = "black --check --diff" - - command = f"{black_command} ." - - run_command(context, command) - - -@task -def flake8(context): - """Check for PEP8 compliance and other style issues.""" - command = "flake8 . --config .flake8" - run_command(context, command) @task @@ -690,31 +691,39 @@ def pylint(context): @task(aliases=("a",)) def autoformat(context): """Run code autoformatting.""" - black(context, autoformat=True) - ruff(context, fix=True) + ruff(context, action=["format"], fix=True) @task( help={ - "action": "One of 'lint', 'format', or 'both'", - "fix": "Automatically fix selected action. May not be able to fix all.", - "output_format": "see https://docs.astral.sh/ruff/settings/#output-format", + "action": "Available values are `['lint', 'format']`. Can be used multiple times. (default: `['lint', 'format']`)", + "target": "File or directory to inspect, repeatable (default: all files in the project will be inspected)", + "fix": "Automatically fix selected actions. May not be able to fix all issues found. (default: False)", + "output_format": "See https://docs.astral.sh/ruff/settings/#output-format for details. (default: `concise`)", }, + iterable=["action", "target"], ) -def ruff(context, action="lint", fix=False, output_format="full"): +def ruff(context, action=None, target=None, fix=False, output_format="concise"): """Run ruff to perform code formatting and/or linting.""" - if action != "lint": - command = "ruff format" + if not action: + action = ["lint", "format"] + if not target: + target = ["."] + + if "format" in action: + command = "ruff format " if not fix: - command += " --check" - command += " ." - run_command(context, command) - if action != "format": - command = "ruff check" + command += "--check " + command += " ".join(target) + run_command(context, command, warn=True) + + if "lint" in action: + command = "ruff check " if fix: - command += " --fix" - command += f" --output-format {output_format} ." - run_command(context, command) + command += "--fix " + command += f"--output-format {output_format} " + command += " ".join(target) + run_command(context, command, warn=True) @task @@ -753,7 +762,7 @@ def check_migrations(context): "verbose": "Enable verbose test output.", } ) -def unittest( +def unittest( # noqa: PLR0913 context, keepdb=False, label="nautobot_floor_plan", @@ -801,12 +810,8 @@ def tests(context, failfast=False, keepdb=False, lint_only=False): print("Starting Docker Containers...") start(context) # Sorted loosely from fastest to slowest - print("Running black...") - black(context) print("Running ruff...") ruff(context) - print("Running flake8...") - flake8(context) print("Running bandit...") bandit(context) print("Running yamllint...") @@ -841,11 +846,20 @@ def generate_app_config_schema(context): - `NautobotAppConfig.required_settings` """ start(context, service="nautobot") - nbshell(context, file="development/app_config_schema.py", env={"APP_CONFIG_SCHEMA_COMMAND": "generate"}) + nbshell( + context, + file="development/app_config_schema.py", + env={"APP_CONFIG_SCHEMA_COMMAND": "generate"}, + ) @task def validate_app_config(context): """Validate the app config based on the app config schema.""" start(context, service="nautobot") - nbshell(context, plain=True, file="development/app_config_schema.py", env={"APP_CONFIG_SCHEMA_COMMAND": "validate"}) + nbshell( + context, + plain=True, + file="development/app_config_schema.py", + env={"APP_CONFIG_SCHEMA_COMMAND": "validate"}, + ) From d8b11d82306535d6684a5b1628144c140c1d58e9 Mon Sep 17 00:00:00 2001 From: Stephen Kiely Date: Mon, 12 Aug 2024 14:31:37 -0500 Subject: [PATCH 2/4] Remove Bandit --- .bandit.yml | 6 - .github/workflows/ci.yml | 12 -- docs/dev/contributing.md | 2 +- docs/dev/dev_environment.md | 2 - poetry.lock | 240 +++++------------------------------- pyproject.toml | 1 - tasks.py | 9 -- 7 files changed, 29 insertions(+), 243 deletions(-) delete mode 100644 .bandit.yml diff --git a/.bandit.yml b/.bandit.yml deleted file mode 100644 index 56f7a83..0000000 --- a/.bandit.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -skips: [] -# No need to check for security issues in the test scripts! -exclude_dirs: - - "./tests/" - - "./.venv/" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2204300..5f31668 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,17 +27,6 @@ jobs: uses: "networktocode/gh-action-setup-poetry-environment@v6" - name: "Linting: ruff format" run: "poetry run invoke ruff --action format" - bandit: - runs-on: "ubuntu-22.04" - env: - INVOKE_NAUTOBOT_FLOOR_PLAN_LOCAL: "True" - steps: - - name: "Check out repository code" - uses: "actions/checkout@v4" - - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v6" - - name: "Linting: bandit" - run: "poetry run invoke bandit" ruff-lint: runs-on: "ubuntu-22.04" env: @@ -84,7 +73,6 @@ jobs: run: "poetry run invoke yamllint" check-in-docker: needs: - - "bandit" - "ruff-format" - "ruff-lint" - "poetry" diff --git a/docs/dev/contributing.md b/docs/dev/contributing.md index 5386d0f..374b5dd 100644 --- a/docs/dev/contributing.md +++ b/docs/dev/contributing.md @@ -4,7 +4,7 @@ The project is packaged with a light [development environment](dev_environment.m The project is following Network to Code software development guidelines and is leveraging the following: -- Python linting and formatting: `pylint`, `bandit`, and `ruff`. +- Python linting and formatting: `pylint` and `ruff`. - YAML linting is done with `yamllint`. - Django unit test to ensure the app is working properly. diff --git a/docs/dev/dev_environment.md b/docs/dev/dev_environment.md index 3f313dd..18cc967 100644 --- a/docs/dev/dev_environment.md +++ b/docs/dev/dev_environment.md @@ -123,7 +123,6 @@ Each command can be executed with `invoke `. All commands support the a #### Testing ```shell - bandit Run bandit to validate basic static code security analysis. ruff Run ruff to perform code formatting and/or linting. pylint Run pylint code analysis. tests Run all tests for this app. @@ -462,7 +461,6 @@ To run an individual test, you can run any or all of the following: ```bash ➜ invoke unittest -➜ invoke bandit ➜ invoke ruff ➜ invoke pylint ``` diff --git a/poetry.lock b/poetry.lock index cd36700..5ccc1fa 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "amqp" @@ -187,30 +187,6 @@ tzdata = {version = "*", optional = true, markers = "extra == \"tzdata\""} [package.extras] tzdata = ["tzdata"] -[[package]] -name = "bandit" -version = "1.7.9" -description = "Security oriented static analyser for python code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "bandit-1.7.9-py3-none-any.whl", hash = "sha256:52077cb339000f337fb25f7e045995c4ad01511e716e5daac37014b9752de8ec"}, - {file = "bandit-1.7.9.tar.gz", hash = "sha256:7c395a436743018f7be0a4cbb0a4ea9b902b6d87264ddecf8cfdc73b4f78ff61"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -PyYAML = ">=5.3.1" -rich = "*" -stevedore = ">=1.20.0" - -[package.extras] -baseline = ["GitPython (>=3.1.30)"] -sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] -toml = ["tomli (>=1.1.0)"] -yaml = ["PyYAML"] - [[package]] name = "billiard" version = "4.2.0" @@ -222,52 +198,6 @@ files = [ {file = "billiard-4.2.0.tar.gz", hash = "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c"}, ] -[[package]] -name = "black" -version = "24.4.2" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, - {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, - {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, - {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, - {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, - {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, - {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, - {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, - {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, - {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, - {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, - {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, - {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, - {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, - {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, - {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, - {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, - {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, - {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, - {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, - {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, - {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "celery" version = "5.3.6" @@ -1190,22 +1120,6 @@ files = [ [package.extras] tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] -[[package]] -name = "flake8" -version = "5.0.4" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, - {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.9.0,<2.10.0" -pyflakes = ">=2.5.0,<2.6.0" - [[package]] name = "ghp-import" version = "2.1.0" @@ -1638,28 +1552,18 @@ docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-li testing = ["coverage", "pyyaml"] [[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" +name = "markdown-version-annotations" +version = "1.0.1" +description = "Markdown plugin to add custom admonitions for documenting version differences" optional = false -python-versions = ">=3.8" +python-versions = "<4.0,>=3.7" files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, + {file = "markdown_version_annotations-1.0.1-py3-none-any.whl", hash = "sha256:6df0b2ac08bab906c8baa425f59fc0fe342fbe8b3917c144fb75914266b33200"}, + {file = "markdown_version_annotations-1.0.1.tar.gz", hash = "sha256:620aade507ef175ccfb2059db152a34c6a1d2add28c2be16ea4de38d742e6132"}, ] [package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +markdown = ">=3.3.7,<4.0.0" [[package]] name = "markupsafe" @@ -1755,17 +1659,6 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - [[package]] name = "mergedeep" version = "1.3.4" @@ -1857,17 +1750,6 @@ files = [ {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, ] -[[package]] -name = "mkdocs-version-annotations" -version = "1.0.0" -description = "MkDocs plugin to add custom admonitions for documenting version differences" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "mkdocs-version-annotations-1.0.0.tar.gz", hash = "sha256:6786024b37d27b330fda240b76ebec8e7ce48bd5a9d7a66e99804559d088dffa"}, - {file = "mkdocs_version_annotations-1.0.0-py3-none-any.whl", hash = "sha256:385004eb4a7530dd87a227e08cd907ce7a8fe21fdf297720a4149c511bcf05f5"}, -] - [[package]] name = "mkdocstrings" version = "0.22.0" @@ -1909,17 +1791,6 @@ files = [ griffe = ">=0.35" mkdocstrings = ">=0.20" -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - [[package]] name = "nautobot" version = "2.2.7" @@ -2087,17 +1958,6 @@ files = [ {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] -[[package]] -name = "pbr" -version = "6.0.0" -description = "Python Build Reasonableness" -optional = false -python-versions = ">=2.6" -files = [ - {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, - {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, -] - [[package]] name = "pexpect" version = "4.9.0" @@ -2408,17 +2268,6 @@ files = [ {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] -[[package]] -name = "pyflakes" -version = "2.5.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, - {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, -] - [[package]] name = "pygments" version = "2.18.0" @@ -2910,25 +2759,6 @@ requests = ">=2.0.0" [package.extras] rsa = ["oauthlib[signedtoken] (>=3.0.0)"] -[[package]] -name = "rich" -version = "13.7.1" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - [[package]] name = "rpds-py" version = "0.19.0" @@ -3039,29 +2869,29 @@ files = [ [[package]] name = "ruff" -version = "0.5.2" +version = "0.5.5" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.5.2-py3-none-linux_armv6l.whl", hash = "sha256:7bab8345df60f9368d5f4594bfb8b71157496b44c30ff035d1d01972e764d3be"}, - {file = "ruff-0.5.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:1aa7acad382ada0189dbe76095cf0a36cd0036779607c397ffdea16517f535b1"}, - {file = "ruff-0.5.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:aec618d5a0cdba5592c60c2dee7d9c865180627f1a4a691257dea14ac1aa264d"}, - {file = "ruff-0.5.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b62adc5ce81780ff04077e88bac0986363e4a3260ad3ef11ae9c14aa0e67ef"}, - {file = "ruff-0.5.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dc42ebf56ede83cb080a50eba35a06e636775649a1ffd03dc986533f878702a3"}, - {file = "ruff-0.5.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c15c6e9f88c67ffa442681365d11df38afb11059fc44238e71a9d9f1fd51de70"}, - {file = "ruff-0.5.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d3de9a5960f72c335ef00763d861fc5005ef0644cb260ba1b5a115a102157251"}, - {file = "ruff-0.5.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe5a968ae933e8f7627a7b2fc8893336ac2be0eb0aace762d3421f6e8f7b7f83"}, - {file = "ruff-0.5.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a04f54a9018f75615ae52f36ea1c5515e356e5d5e214b22609ddb546baef7132"}, - {file = "ruff-0.5.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed02fb52e3741f0738db5f93e10ae0fb5c71eb33a4f2ba87c9a2fa97462a649"}, - {file = "ruff-0.5.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3cf8fe659f6362530435d97d738eb413e9f090e7e993f88711b0377fbdc99f60"}, - {file = "ruff-0.5.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:237a37e673e9f3cbfff0d2243e797c4862a44c93d2f52a52021c1a1b0899f846"}, - {file = "ruff-0.5.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2a2949ce7c1cbd8317432ada80fe32156df825b2fd611688814c8557824ef060"}, - {file = "ruff-0.5.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:481af57c8e99da92ad168924fd82220266043c8255942a1cb87958b108ac9335"}, - {file = "ruff-0.5.2-py3-none-win32.whl", hash = "sha256:f1aea290c56d913e363066d83d3fc26848814a1fed3d72144ff9c930e8c7c718"}, - {file = "ruff-0.5.2-py3-none-win_amd64.whl", hash = "sha256:8532660b72b5d94d2a0a7a27ae7b9b40053662d00357bb2a6864dd7e38819084"}, - {file = "ruff-0.5.2-py3-none-win_arm64.whl", hash = "sha256:73439805c5cb68f364d826a5c5c4b6c798ded6b7ebaa4011f01ce6c94e4d5583"}, - {file = "ruff-0.5.2.tar.gz", hash = "sha256:2c0df2d2de685433794a14d8d2e240df619b748fbe3367346baa519d8e6f1ca2"}, + {file = "ruff-0.5.5-py3-none-linux_armv6l.whl", hash = "sha256:605d589ec35d1da9213a9d4d7e7a9c761d90bba78fc8790d1c5e65026c1b9eaf"}, + {file = "ruff-0.5.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00817603822a3e42b80f7c3298c8269e09f889ee94640cd1fc7f9329788d7bf8"}, + {file = "ruff-0.5.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:187a60f555e9f865a2ff2c6984b9afeffa7158ba6e1eab56cb830404c942b0f3"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe26fc46fa8c6e0ae3f47ddccfbb136253c831c3289bba044befe68f467bfb16"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad25dd9c5faac95c8e9efb13e15803cd8bbf7f4600645a60ffe17c73f60779b"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f70737c157d7edf749bcb952d13854e8f745cec695a01bdc6e29c29c288fc36e"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:cfd7de17cef6ab559e9f5ab859f0d3296393bc78f69030967ca4d87a541b97a0"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a09b43e02f76ac0145f86a08e045e2ea452066f7ba064fd6b0cdccb486f7c3e7"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0b856cb19c60cd40198be5d8d4b556228e3dcd545b4f423d1ad812bfdca5884"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3687d002f911e8a5faf977e619a034d159a8373514a587249cc00f211c67a091"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ac9dc814e510436e30d0ba535f435a7f3dc97f895f844f5b3f347ec8c228a523"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:af9bdf6c389b5add40d89b201425b531e0a5cceb3cfdcc69f04d3d531c6be74f"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d40a8533ed545390ef8315b8e25c4bb85739b90bd0f3fe1280a29ae364cc55d8"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cab904683bf9e2ecbbe9ff235bfe056f0eba754d0168ad5407832928d579e7ab"}, + {file = "ruff-0.5.5-py3-none-win32.whl", hash = "sha256:696f18463b47a94575db635ebb4c178188645636f05e934fdf361b74edf1bb2d"}, + {file = "ruff-0.5.5-py3-none-win_amd64.whl", hash = "sha256:50f36d77f52d4c9c2f1361ccbfbd09099a1b2ea5d2b2222c586ab08885cf3445"}, + {file = "ruff-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3191317d967af701f1b73a31ed5788795936e423b7acce82a2b63e26eb3e89d6"}, + {file = "ruff-0.5.5.tar.gz", hash = "sha256:cc5516bdb4858d972fbc31d246bdb390eab8df1a26e2353be2dbc0c2d7f5421a"}, ] [[package]] @@ -3186,20 +3016,6 @@ pure-eval = "*" [package.extras] tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] -[[package]] -name = "stevedore" -version = "5.2.0" -description = "Manage dynamic plugins for Python applications" -optional = false -python-versions = ">=3.8" -files = [ - {file = "stevedore-5.2.0-py3-none-any.whl", hash = "sha256:1c15d95766ca0569cad14cb6272d4d31dae66b011a929d7c18219c176ea1b5c9"}, - {file = "stevedore-5.2.0.tar.gz", hash = "sha256:46b93ca40e1114cea93d738a6c1e365396981bb6bb78c27045b7587c9473544d"}, -] - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - [[package]] name = "svgwrite" version = "1.4.3" @@ -3475,4 +3291,4 @@ all = [] [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.12" -content-hash = "8c15399d15f5688fdd73bbe181efb76c4e2f9b0ecb269be5316196efe89796bb" +content-hash = "4935d527b8dfa87bde7c6056c6600f07135f23801a004ae37176ff4655f95c17" diff --git a/pyproject.toml b/pyproject.toml index bbba61a..f8f535b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,6 @@ python = ">=3.8,<3.12" nautobot = "^2.0.0" [tool.poetry.group.dev.dependencies] -bandit = "*" coverage = "*" django-debug-toolbar = "*" invoke = "*" diff --git a/tasks.py b/tasks.py index 1599577..f36851b 100644 --- a/tasks.py +++ b/tasks.py @@ -726,13 +726,6 @@ def ruff(context, action=None, target=None, fix=False, output_format="concise"): run_command(context, command, warn=True) -@task -def bandit(context): - """Run bandit to validate basic static code security analysis.""" - command = "bandit --recursive . --configfile .bandit.yml" - run_command(context, command) - - @task def yamllint(context): """Run yamllint to validate formatting adheres to NTC defined YAML standards. @@ -812,8 +805,6 @@ def tests(context, failfast=False, keepdb=False, lint_only=False): # Sorted loosely from fastest to slowest print("Running ruff...") ruff(context) - print("Running bandit...") - bandit(context) print("Running yamllint...") yamllint(context) print("Running poetry check...") From da9bbdf2986d6004cfc6cf80ce965b6ba13ed2af Mon Sep 17 00:00:00 2001 From: Stephen Kiely Date: Mon, 12 Aug 2024 15:06:03 -0500 Subject: [PATCH 3/4] Ruff fixes and add code that got removed. --- changes/114.housekeeping | 1 + development/app_config_schema.py | 4 +- development/nautobot_config.py | 12 +-- nautobot_floor_plan/__init__.py | 2 +- nautobot_floor_plan/api/serializers.py | 4 +- nautobot_floor_plan/api/views.py | 3 +- nautobot_floor_plan/filter_extensions.py | 1 - nautobot_floor_plan/filters.py | 8 +- nautobot_floor_plan/forms.py | 79 ++++++++++++++----- .../migrations/0001_initial.py | 5 +- .../migrations/0002_fixup_null.py | 1 - .../migrations/0003_auto_20230908_1339.py | 2 +- .../migrations/0005_add_rackgroup.py | 2 +- .../0006_alter_floorplantile_status.py | 3 +- .../migrations/0007_add_axis_origin_seed.py | 1 - nautobot_floor_plan/models.py | 13 +-- nautobot_floor_plan/navigation.py | 35 +++++--- nautobot_floor_plan/svg.py | 6 +- nautobot_floor_plan/template_content.py | 1 - nautobot_floor_plan/tests/fixtures.py | 3 +- nautobot_floor_plan/tests/test_api_views.py | 3 +- nautobot_floor_plan/tests/test_basic.py | 18 +---- .../tests/test_filter_floorplan.py | 28 ------- nautobot_floor_plan/tests/test_filters.py | 4 +- nautobot_floor_plan/tests/test_forms.py | 5 +- .../tests/test_model_floorplan.py | 24 ------ nautobot_floor_plan/tests/test_models.py | 3 +- nautobot_floor_plan/tests/test_views.py | 2 +- nautobot_floor_plan/urls.py | 4 + nautobot_floor_plan/views.py | 8 +- pyproject.toml | 5 +- tasks.py | 36 +++------ 32 files changed, 138 insertions(+), 188 deletions(-) create mode 100644 changes/114.housekeeping delete mode 100644 nautobot_floor_plan/tests/test_filter_floorplan.py delete mode 100644 nautobot_floor_plan/tests/test_model_floorplan.py diff --git a/changes/114.housekeeping b/changes/114.housekeeping new file mode 100644 index 0000000..b36df09 --- /dev/null +++ b/changes/114.housekeeping @@ -0,0 +1 @@ +Rebake from Nautobot-App-Cookiecutter 2.3. \ No newline at end of file diff --git a/development/app_config_schema.py b/development/app_config_schema.py index e52e247..a779b14 100644 --- a/development/app_config_schema.py +++ b/development/app_config_schema.py @@ -40,9 +40,7 @@ def _main(): **SchemaBuilder().to_json_schema(app_config), # type: ignore } app_config = import_module(package_name).config - _enrich_object_schema( - schema, app_config.default_settings, app_config.required_settings - ) + _enrich_object_schema(schema, app_config.default_settings, app_config.required_settings) schema_path.write_text(json.dumps(schema, indent=4) + "\n") print(f"\n==================\nGenerated schema:\n\n{schema_path}\n") print( diff --git a/development/nautobot_config.py b/development/nautobot_config.py index 2b388b3..cfb2026 100644 --- a/development/nautobot_config.py +++ b/development/nautobot_config.py @@ -18,12 +18,8 @@ if "debug_toolbar" not in INSTALLED_APPS: # noqa: F405 INSTALLED_APPS.append("debug_toolbar") # noqa: F405 - if ( - "debug_toolbar.middleware.DebugToolbarMiddleware" not in MIDDLEWARE - ): # noqa: F405 - MIDDLEWARE.insert( - 0, "debug_toolbar.middleware.DebugToolbarMiddleware" - ) # noqa: F405 + if "debug_toolbar.middleware.DebugToolbarMiddleware" not in MIDDLEWARE: # noqa: F405 + MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware") # noqa: F405 # # Misc. settings @@ -55,9 +51,7 @@ "NAUTOBOT_DB_PORT", default_db_settings[nautobot_db_engine]["NAUTOBOT_DB_PORT"], ), # Database port, default to postgres - "CONN_MAX_AGE": int( - os.getenv("NAUTOBOT_DB_TIMEOUT", "300") - ), # Database timeout + "CONN_MAX_AGE": int(os.getenv("NAUTOBOT_DB_TIMEOUT", "300")), # Database timeout "ENGINE": nautobot_db_engine, } } diff --git a/nautobot_floor_plan/__init__.py b/nautobot_floor_plan/__init__.py index b285f91..62173d5 100644 --- a/nautobot_floor_plan/__init__.py +++ b/nautobot_floor_plan/__init__.py @@ -5,9 +5,9 @@ from django.core.exceptions import ImproperlyConfigured from django.db.models.signals import post_migrate - from nautobot.apps import NautobotAppConfig from nautobot.apps.config import get_app_settings_or_config + from nautobot_floor_plan.choices import AxisLabelsChoices __version__ = metadata.version(__name__) diff --git a/nautobot_floor_plan/api/serializers.py b/nautobot_floor_plan/api/serializers.py index d2a0681..e4800a5 100644 --- a/nautobot_floor_plan/api/serializers.py +++ b/nautobot_floor_plan/api/serializers.py @@ -5,9 +5,7 @@ from nautobot_floor_plan import models -class FloorPlanSerializer( - NautobotModelSerializer, TaggedModelSerializerMixin -): # pylint: disable=too-many-ancestors +class FloorPlanSerializer(NautobotModelSerializer, TaggedModelSerializerMixin): # pylint: disable=too-many-ancestors """FloorPlan Serializer.""" class Meta: diff --git a/nautobot_floor_plan/api/views.py b/nautobot_floor_plan/api/views.py index fa58750..16bfa77 100644 --- a/nautobot_floor_plan/api/views.py +++ b/nautobot_floor_plan/api/views.py @@ -4,9 +4,8 @@ from django.shortcuts import get_object_or_404 from django.views.decorators.clickjacking import xframe_options_sameorigin from drf_spectacular.utils import extend_schema -from rest_framework.decorators import action - from nautobot.apps.api import NautobotModelViewSet +from rest_framework.decorators import action from nautobot_floor_plan import filters, models from nautobot_floor_plan.api import serializers diff --git a/nautobot_floor_plan/filter_extensions.py b/nautobot_floor_plan/filter_extensions.py index c8c343f..f683db7 100644 --- a/nautobot_floor_plan/filter_extensions.py +++ b/nautobot_floor_plan/filter_extensions.py @@ -1,7 +1,6 @@ """Extensions to Nautobot core models' filtering functionality.""" import django_filters - from nautobot.extras.plugins import PluginFilterExtension from nautobot_floor_plan import models diff --git a/nautobot_floor_plan/filters.py b/nautobot_floor_plan/filters.py index 495d41a..a5b5f15 100644 --- a/nautobot_floor_plan/filters.py +++ b/nautobot_floor_plan/filters.py @@ -1,13 +1,13 @@ """Filtering for nautobot_floor_plan.""" -from nautobot.apps.filters import NameSearchFilterSet, NautobotFilterSet +import django_filters +from nautobot.apps.filters import NaturalKeyOrPKMultipleChoiceFilter, NautobotFilterSet, SearchFilter +from nautobot.dcim.models import Location, Rack, RackGroup from nautobot_floor_plan import models -class FloorPlanFilterSet( - NautobotFilterSet, NameSearchFilterSet -): # pylint: disable=too-many-ancestors +class FloorPlanFilterSet(NautobotFilterSet): """Filter for FloorPlan.""" q = SearchFilter( diff --git a/nautobot_floor_plan/forms.py b/nautobot_floor_plan/forms.py index 2c66320..127f30e 100644 --- a/nautobot_floor_plan/forms.py +++ b/nautobot_floor_plan/forms.py @@ -5,27 +5,20 @@ """Forms for nautobot_floor_plan.""" from django import forms +from nautobot.apps.config import get_app_settings_or_config from nautobot.apps.forms import ( + DynamicModelChoiceField, + DynamicModelMultipleChoiceField, NautobotBulkEditForm, NautobotFilterForm, NautobotModelForm, - TagsBulkEditFormMixin, -) - -from nautobot.dcim.models import Location, Rack, RackGroup -from nautobot.apps.forms import ( - NautobotBulkEditForm, - NautobotFilterForm, - NautobotModelForm, - TagsBulkEditFormMixin, - DynamicModelChoiceField, - DynamicModelMultipleChoiceField, TagFilterField, + TagsBulkEditFormMixin, add_blank_choice, ) -from nautobot.apps.config import get_app_settings_or_config +from nautobot.dcim.models import Location, Rack, RackGroup -from nautobot_floor_plan import models, choices, utils +from nautobot_floor_plan import choices, models, utils class FloorPlanForm(NautobotModelForm): @@ -65,15 +58,61 @@ def __init__(self, *args, **kwargs): """Overwrite the constructor to set initial values for select widget.""" super().__init__(*args, **kwargs) -class FloorPlanBulkEditForm( - TagsBulkEditFormMixin, NautobotBulkEditForm -): # pylint: disable=too-many-ancestors + if not self.instance.created: + self.initial["x_axis_labels"] = get_app_settings_or_config("nautobot_floor_plan", "default_x_axis_labels") + self.initial["y_axis_labels"] = get_app_settings_or_config("nautobot_floor_plan", "default_y_axis_labels") + self.x_letters = self.initial["x_axis_labels"] == choices.AxisLabelsChoices.LETTERS + self.y_letters = self.initial["y_axis_labels"] == choices.AxisLabelsChoices.LETTERS + self.initial["x_origin_seed"] = "A" if self.x_letters else "1" + self.initial["y_origin_seed"] = "A" if self.y_letters else "1" + else: + self.x_letters = self.instance.x_axis_labels == choices.AxisLabelsChoices.LETTERS + self.y_letters = self.instance.y_axis_labels == choices.AxisLabelsChoices.LETTERS + + if self.x_letters and str(self.initial["y_origin_seed"]).isdigit(): + self.initial["x_origin_seed"] = utils.grid_number_to_letter(self.instance.x_origin_seed) + if self.y_letters and str(self.initial["y_origin_seed"]).isdigit(): + self.initial["y_origin_seed"] = utils.grid_number_to_letter(self.instance.y_origin_seed) + + def _clean_origin_seed(self, field_name, axis): + """Common clean method for origin_seed fields.""" + value = self.cleaned_data.get(field_name) + if not value: + return 1 + + self.x_letters = self.cleaned_data.get("x_axis_labels") == choices.AxisLabelsChoices.LETTERS + self.y_letters = self.cleaned_data.get("y_axis_labels") == choices.AxisLabelsChoices.LETTERS + + if self.x_letters and field_name == "x_origin_seed" or self.y_letters and field_name == "y_origin_seed": + if not str(value).isupper(): + self.add_error(field_name, f"{axis} origin start should use capital letters.") + return 0 + return utils.grid_letter_to_number(value) + + if not str(value).isdigit(): + self.add_error(field_name, f"{axis} origin start should use numbers.") + return 0 + return int(value) + + def clean_x_origin_seed(self): + """Validate input and convert y_origin to an integer.""" + return self._clean_origin_seed("x_origin_seed", "X") + + def clean_y_origin_seed(self): + """Validate input and convert y_origin to an integer.""" + return self._clean_origin_seed("y_origin_seed", "Y") + + +class FloorPlanBulkEditForm(TagsBulkEditFormMixin, NautobotBulkEditForm): # pylint: disable=too-many-ancestors """FloorPlan bulk edit form.""" - pk = forms.ModelMultipleChoiceField( - queryset=models.FloorPlan.objects.all(), widget=forms.MultipleHiddenInput - ) - description = forms.CharField(required=False) + pk = forms.ModelMultipleChoiceField(queryset=models.FloorPlan.objects.all(), widget=forms.MultipleHiddenInput) + x_size = forms.IntegerField(min_value=1, required=False) + y_size = forms.IntegerField(min_value=1, required=False) + tile_width = forms.IntegerField(min_value=1, required=False) + tile_depth = forms.IntegerField(min_value=1, required=False) + x_axis_labels = forms.ChoiceField(choices=add_blank_choice(choices.AxisLabelsChoices), required=False) + y_axis_labels = forms.ChoiceField(choices=add_blank_choice(choices.AxisLabelsChoices), required=False) class Meta: """Meta attributes.""" diff --git a/nautobot_floor_plan/migrations/0001_initial.py b/nautobot_floor_plan/migrations/0001_initial.py index 8124eec..107e73d 100755 --- a/nautobot_floor_plan/migrations/0001_initial.py +++ b/nautobot_floor_plan/migrations/0001_initial.py @@ -1,13 +1,14 @@ # Generated by Django 3.2.16 on 2023-03-02 21:23 +import uuid + import django.core.serializers.json import django.core.validators -from django.db import migrations, models import django.db.models.deletion import nautobot.extras.models.mixins import nautobot.extras.models.statuses import taggit.managers -import uuid +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/nautobot_floor_plan/migrations/0002_fixup_null.py b/nautobot_floor_plan/migrations/0002_fixup_null.py index 0ce6989..ccaf8b0 100644 --- a/nautobot_floor_plan/migrations/0002_fixup_null.py +++ b/nautobot_floor_plan/migrations/0002_fixup_null.py @@ -1,5 +1,4 @@ from django.db import migrations - from nautobot.extras.utils import fixup_null_statuses diff --git a/nautobot_floor_plan/migrations/0003_auto_20230908_1339.py b/nautobot_floor_plan/migrations/0003_auto_20230908_1339.py index 96ebd79..fec62df 100644 --- a/nautobot_floor_plan/migrations/0003_auto_20230908_1339.py +++ b/nautobot_floor_plan/migrations/0003_auto_20230908_1339.py @@ -1,9 +1,9 @@ # Generated by Django 3.2.20 on 2023-09-08 13:39 -from django.db import migrations, models import django.db.models.deletion import nautobot.core.models.fields import nautobot.extras.models.statuses +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/nautobot_floor_plan/migrations/0005_add_rackgroup.py b/nautobot_floor_plan/migrations/0005_add_rackgroup.py index c5f9252..a2cde2e 100644 --- a/nautobot_floor_plan/migrations/0005_add_rackgroup.py +++ b/nautobot_floor_plan/migrations/0005_add_rackgroup.py @@ -1,7 +1,7 @@ # Generated by Django 3.2.21 on 2024-06-21 18:52 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/nautobot_floor_plan/migrations/0006_alter_floorplantile_status.py b/nautobot_floor_plan/migrations/0006_alter_floorplantile_status.py index 300099c..880f2f7 100644 --- a/nautobot_floor_plan/migrations/0006_alter_floorplantile_status.py +++ b/nautobot_floor_plan/migrations/0006_alter_floorplantile_status.py @@ -1,12 +1,11 @@ # Generated by Django 4.2.14 on 2024-08-07 15:39 -from django.db import migrations import django.db.models.deletion import nautobot.extras.models.statuses +from django.db import migrations class Migration(migrations.Migration): - dependencies = [ ("nautobot_floor_plan", "0005_add_rackgroup"), ] diff --git a/nautobot_floor_plan/migrations/0007_add_axis_origin_seed.py b/nautobot_floor_plan/migrations/0007_add_axis_origin_seed.py index b11dc1c..5b28b31 100644 --- a/nautobot_floor_plan/migrations/0007_add_axis_origin_seed.py +++ b/nautobot_floor_plan/migrations/0007_add_axis_origin_seed.py @@ -5,7 +5,6 @@ class Migration(migrations.Migration): - dependencies = [ ("nautobot_floor_plan", "0006_alter_floorplantile_status"), ] diff --git a/nautobot_floor_plan/models.py b/nautobot_floor_plan/models.py index aeee39b..85ee9b5 100644 --- a/nautobot_floor_plan/models.py +++ b/nautobot_floor_plan/models.py @@ -5,19 +5,12 @@ from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator from django.db import models, transaction +from nautobot.apps.models import PrimaryModel, StatusField, extras_features -from nautobot.apps.models import extras_features -from nautobot.apps.models import PrimaryModel -from nautobot.apps.models import StatusField - -from nautobot_floor_plan.choices import RackOrientationChoices, AxisLabelsChoices, AllocationTypeChoices +from nautobot_floor_plan.choices import AllocationTypeChoices, AxisLabelsChoices, RackOrientationChoices from nautobot_floor_plan.svg import FloorPlanSVG -# from nautobot.extras.utils import extras_features -# If you want to use the extras_features decorator please reference the following documentation -# https://docs.nautobot.com/projects/core/en/latest/plugins/development/#using-the-extras_features-decorator-for-graphql -# Then based on your reading you may decide to put the following decorator before the declaration of your class -# @extras_features("custom_fields", "custom_validators", "relationships", "graphql") +logger = logging.getLogger(__name__) @extras_features( diff --git a/nautobot_floor_plan/navigation.py b/nautobot_floor_plan/navigation.py index dd757ae..fc5d79c 100644 --- a/nautobot_floor_plan/navigation.py +++ b/nautobot_floor_plan/navigation.py @@ -1,16 +1,31 @@ """Menu items.""" -from nautobot.apps.ui import NavMenuGroup, NavMenuItem, NavMenuTab, NavMenuAddButton, NavMenuImportButton +from nautobot.apps.ui import NavMenuAddButton, NavMenuGroup, NavMenuImportButton, NavMenuItem, NavMenuTab -items = ( - NavMenuItem( - link="plugins:nautobot_floor_plan:floorplan_list", - name="Nautobot Floor Plan", - permissions=["nautobot_floor_plan.view_floorplan"], - buttons=( - NavMenuAddButton( - link="plugins:nautobot_floor_plan:floorplan_add", - permissions=["nautobot_floor_plan.add_floorplan"], +menu_items = ( + NavMenuTab( + name="Organization", + groups=( + NavMenuGroup( + name="Locations", + items=( + NavMenuItem( + name="Location Floor Plans", + link="plugins:nautobot_floor_plan:floorplan_list", + weight=300, + permissions=["nautobot_floor_plan.view_floorplan"], + buttons=( + NavMenuAddButton( + link="plugins:nautobot_floor_plan:floorplan_add", + permissions=["nautobot_floor_plan.add_floorplan"], + ), + NavMenuImportButton( + link="plugins:nautobot_floor_plan:floorplan_import", + permissions=["nautobot_floor_plan.add_floorplan"], + ), + ), + ), + ), ), ), ), diff --git a/nautobot_floor_plan/svg.py b/nautobot_floor_plan/svg.py index 8297cf8..01193fb 100644 --- a/nautobot_floor_plan/svg.py +++ b/nautobot_floor_plan/svg.py @@ -2,18 +2,16 @@ import logging import os -import svgwrite +import svgwrite from django.urls import reverse from django.utils.functional import cached_property from django.utils.http import urlencode - from nautobot.core.templatetags.helpers import fgcolor -from nautobot_floor_plan.choices import RackOrientationChoices, AxisLabelsChoices, AllocationTypeChoices +from nautobot_floor_plan.choices import AllocationTypeChoices, AxisLabelsChoices, RackOrientationChoices from nautobot_floor_plan.utils import grid_number_to_letter - logger = logging.getLogger(__name__) diff --git a/nautobot_floor_plan/template_content.py b/nautobot_floor_plan/template_content.py index bdb99bb..234c856 100644 --- a/nautobot_floor_plan/template_content.py +++ b/nautobot_floor_plan/template_content.py @@ -2,7 +2,6 @@ from django.core.exceptions import ObjectDoesNotExist from django.urls import reverse - from nautobot.extras.plugins import PluginTemplateExtension diff --git a/nautobot_floor_plan/tests/fixtures.py b/nautobot_floor_plan/tests/fixtures.py index aa24d47..a6c0f33 100644 --- a/nautobot_floor_plan/tests/fixtures.py +++ b/nautobot_floor_plan/tests/fixtures.py @@ -1,8 +1,7 @@ """Fixtures for testing this app.""" from django.contrib.contenttypes.models import ContentType - -from nautobot.dcim.models import LocationType, Rack, RackGroup, Location +from nautobot.dcim.models import Location, LocationType, Rack, RackGroup from nautobot.extras.models import Status from nautobot_floor_plan.models import FloorPlan, FloorPlanTile diff --git a/nautobot_floor_plan/tests/test_api_views.py b/nautobot_floor_plan/tests/test_api_views.py index 6abda30..9e512fc 100644 --- a/nautobot_floor_plan/tests/test_api_views.py +++ b/nautobot_floor_plan/tests/test_api_views.py @@ -1,10 +1,9 @@ """Unit tests for nautobot_floor_plan.""" from django.contrib.contenttypes.models import ContentType - +from nautobot.apps.testing import APIViewTestCases from nautobot.dcim.models import Rack, RackGroup from nautobot.extras.models import Tag -from nautobot.apps.testing import APIViewTestCases from nautobot_floor_plan import choices, models from nautobot_floor_plan.tests import fixtures diff --git a/nautobot_floor_plan/tests/test_basic.py b/nautobot_floor_plan/tests/test_basic.py index 83142fa..d72f2d0 100644 --- a/nautobot_floor_plan/tests/test_basic.py +++ b/nautobot_floor_plan/tests/test_basic.py @@ -11,21 +11,11 @@ class TestDocsPackaging(unittest.TestCase): def test_version(self): """Verify that pyproject.toml dev dependencies have the same versions as in the docs requirements.txt.""" - parent_path = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - ) + parent_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) poetry_path = os.path.join(parent_path, "pyproject.toml") - poetry_details = toml.load(poetry_path)["tool"]["poetry"]["group"]["dev"][ - "dependencies" - ] - with open( - f"{parent_path}/docs/requirements.txt", "r", encoding="utf-8" - ) as file: - requirements = [ - line - for line in file.read().splitlines() - if (len(line) > 0 and not line.startswith("#")) - ] + poetry_details = toml.load(poetry_path)["tool"]["poetry"]["group"]["dev"]["dependencies"] + with open(f"{parent_path}/docs/requirements.txt", "r", encoding="utf-8") as file: + requirements = [line for line in file.read().splitlines() if (len(line) > 0 and not line.startswith("#"))] for pkg in requirements: package_name = pkg if len(pkg.split("==")) == 2: # noqa: PLR2004 diff --git a/nautobot_floor_plan/tests/test_filter_floorplan.py b/nautobot_floor_plan/tests/test_filter_floorplan.py deleted file mode 100644 index 28333f6..0000000 --- a/nautobot_floor_plan/tests/test_filter_floorplan.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Test FloorPlan Filter.""" - -from django.test import TestCase - -from nautobot_floor_plan import filters, models -from nautobot_floor_plan.tests import fixtures - - -class FloorPlanFilterTestCase(TestCase): - """FloorPlan Filter Test Case.""" - - queryset = models.FloorPlan.objects.all() - filterset = filters.FloorPlanFilterSet - - @classmethod - def setUpTestData(cls): - """Setup test data for FloorPlan Model.""" - fixtures.create_floorplan() - - def test_q_search_name(self): - """Test using Q search with name of FloorPlan.""" - params = {"q": "Test One"} - self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1) - - def test_q_invalid(self): - """Test using invalid Q search for FloorPlan.""" - params = {"q": "test-five"} - self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0) diff --git a/nautobot_floor_plan/tests/test_filters.py b/nautobot_floor_plan/tests/test_filters.py index 30c3eaf..f7cba10 100644 --- a/nautobot_floor_plan/tests/test_filters.py +++ b/nautobot_floor_plan/tests/test_filters.py @@ -1,12 +1,10 @@ """Test FloorPlan Filter.""" from django.test import TestCase - from nautobot.dcim.models import Rack, RackGroup from nautobot.extras.models import Tag -from nautobot_floor_plan import filters -from nautobot_floor_plan import models +from nautobot_floor_plan import filters, models from nautobot_floor_plan.tests import fixtures diff --git a/nautobot_floor_plan/tests/test_forms.py b/nautobot_floor_plan/tests/test_forms.py index 20671e7..3d88c73 100644 --- a/nautobot_floor_plan/tests/test_forms.py +++ b/nautobot_floor_plan/tests/test_forms.py @@ -1,11 +1,10 @@ """Test floorplan forms.""" from django.contrib.contenttypes.models import ContentType - -from nautobot.extras.models import Tag from nautobot.core.testing import TestCase +from nautobot.extras.models import Tag -from nautobot_floor_plan import forms, models, choices +from nautobot_floor_plan import choices, forms, models from nautobot_floor_plan.tests import fixtures diff --git a/nautobot_floor_plan/tests/test_model_floorplan.py b/nautobot_floor_plan/tests/test_model_floorplan.py deleted file mode 100644 index f369a51..0000000 --- a/nautobot_floor_plan/tests/test_model_floorplan.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Test FloorPlan.""" - -from django.test import TestCase - -from nautobot_floor_plan import models - - -class TestFloorPlan(TestCase): - """Test FloorPlan.""" - - def test_create_floorplan_only_required(self): - """Create with only required fields, and validate null description and __str__.""" - floorplan = models.FloorPlan.objects.create(name="Development") - self.assertEqual(floorplan.name, "Development") - self.assertEqual(floorplan.description, "") - self.assertEqual(str(floorplan), "Development") - - def test_create_floorplan_all_fields_success(self): - """Create FloorPlan with all fields.""" - floorplan = models.FloorPlan.objects.create( - name="Development", description="Development Test" - ) - self.assertEqual(floorplan.name, "Development") - self.assertEqual(floorplan.description, "Development Test") diff --git a/nautobot_floor_plan/tests/test_models.py b/nautobot_floor_plan/tests/test_models.py index a2fbbae..b078239 100644 --- a/nautobot_floor_plan/tests/test_models.py +++ b/nautobot_floor_plan/tests/test_models.py @@ -1,9 +1,8 @@ """Test FloorPlan.""" from django.core.exceptions import ValidationError - -from nautobot.dcim.models import Rack, RackGroup from nautobot.core.testing import TestCase +from nautobot.dcim.models import Rack, RackGroup from nautobot_floor_plan import models from nautobot_floor_plan.tests import fixtures diff --git a/nautobot_floor_plan/tests/test_views.py b/nautobot_floor_plan/tests/test_views.py index fd2c77e..41877c4 100644 --- a/nautobot_floor_plan/tests/test_views.py +++ b/nautobot_floor_plan/tests/test_views.py @@ -2,7 +2,7 @@ from nautobot.apps.testing import ViewTestCases -from nautobot_floor_plan import models, choices +from nautobot_floor_plan import choices, models from nautobot_floor_plan.tests import fixtures diff --git a/nautobot_floor_plan/urls.py b/nautobot_floor_plan/urls.py index ebcadcd..63551d0 100644 --- a/nautobot_floor_plan/urls.py +++ b/nautobot_floor_plan/urls.py @@ -1,10 +1,14 @@ """Django urlpatterns declaration for nautobot_floor_plan app.""" +from django.templatetags.static import static +from django.urls import path +from django.views.generic import RedirectView from nautobot.apps.urls import NautobotUIViewSetRouter from nautobot.extras.views import ObjectChangeLogView, ObjectNotesView from nautobot_floor_plan import models, views +app_name = "floor_plan" router = NautobotUIViewSetRouter() router.register("floor-plans", views.FloorPlanUIViewSet) router.register("floor-plan-tiles", views.FloorPlanTileUIViewSet) diff --git a/nautobot_floor_plan/views.py b/nautobot_floor_plan/views.py index 8483125..2234c55 100644 --- a/nautobot_floor_plan/views.py +++ b/nautobot_floor_plan/views.py @@ -2,14 +2,14 @@ from nautobot.apps.views import ( NautobotUIViewSet, - ObjectListViewMixin, + ObjectChangeLogViewMixin, + ObjectDestroyViewMixin, ObjectDetailViewMixin, ObjectEditViewMixin, - ObjectDestroyViewMixin, - ObjectChangeLogViewMixin, + ObjectListViewMixin, ObjectNotesViewMixin, + ObjectView, ) -from nautobot.apps.views import ObjectView from nautobot.dcim.models import Location from nautobot_floor_plan import filters, forms, models, tables diff --git a/pyproject.toml b/pyproject.toml index f8f535b..c24ad13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,9 @@ good-names = """_,i,j,k,x,y""" [tool.pylint.messages_control] disable = """, - line-too-long + line-too-long, + too-few-public-methods, + too-many-ancestors """ [tool.pylint.miscellaneous] @@ -104,7 +106,6 @@ target-version = "py38" select = [ "D", # pydocstyle "F", "E", "W", # flake8 - "PL", # pylint "S", # bandit "I", # isort ] diff --git a/tasks.py b/tasks.py index f36851b..3e11422 100644 --- a/tasks.py +++ b/tasks.py @@ -70,9 +70,7 @@ def _is_compose_included(context, name): def _await_healthy_service(context, service): - container_id = docker_compose( - context, f"ps -q -- {service}", pty=False, echo=False, hide=True - ).stdout.strip() + container_id = docker_compose(context, f"ps -q -- {service}", pty=False, echo=False, hide=True).stdout.strip() _await_healthy_container(context, container_id) @@ -132,9 +130,7 @@ def docker_compose(context, command, **kwargs): ] for compose_file in context.nautobot_floor_plan.compose_files: - compose_file_path = os.path.join( - context.nautobot_floor_plan.compose_dir, compose_file - ) + compose_file_path = os.path.join(context.nautobot_floor_plan.compose_dir, compose_file) compose_command_tokens.append(f' -f "{compose_file_path}"') compose_command_tokens.append(command) @@ -173,9 +169,7 @@ def run_command(context, command, **kwargs): if "nautobot" in results.stdout: compose_command = f"exec{command_env_args} nautobot {command}" else: - compose_command = ( - f"run{command_env_args} --rm --entrypoint='{command}' nautobot" - ) + compose_command = f"run{command_env_args} --rm --entrypoint='{command}' nautobot" pty = kwargs.pop("pty", True) @@ -252,9 +246,7 @@ def restart(context, service=""): def stop(context, service=""): """Stop specified or all services, if service is not specified, remove all containers.""" print("Stopping Nautobot...") - docker_compose( - context, "stop" if service else "down --remove-orphans", service=service - ) + docker_compose(context, "stop" if service else "down --remove-orphans", service=service) @task( @@ -273,9 +265,7 @@ def destroy(context, volumes=True, import_db_file=""): return if not volumes: - raise ValueError( - "Cannot specify `--no-volumes` and `--import-db-file` arguments at the same time." - ) + raise ValueError("Cannot specify `--no-volumes` and `--import-db-file` arguments at the same time.") print(f"Importing database file: {import_db_file}...") @@ -292,16 +282,12 @@ def destroy(context, volumes=True, import_db_file=""): "db", ] - container_id = docker_compose( - context, " ".join(command), pty=False, echo=False, hide=True - ).stdout.strip() + container_id = docker_compose(context, " ".join(command), pty=False, echo=False, hide=True).stdout.strip() _await_healthy_container(context, container_id) print("Stopping database container...") context.run(f"docker stop {container_id}", pty=False, echo=False, hide=True) - print( - "Database import complete, you can start Nautobot with the following command:" - ) + print("Database import complete, you can start Nautobot with the following command:") print("invoke start") @@ -473,9 +459,7 @@ def dbshell(context, db_name="", input_file="", output_file="", query=""): if input_file and query: raise ValueError("Cannot specify both, `input_file` and `query` arguments") if output_file and not (input_file or query): - raise ValueError( - "`output_file` argument requires `input_file` or `query` argument" - ) + raise ValueError("`output_file` argument requires `input_file` or `query` argument") env = {} if query: @@ -613,9 +597,7 @@ def backup_db(context, db_name="", output_file="dump.sql", readable=True): docker_compose(context, " ".join(command), pty=False) print(50 * "=") - print( - "The database backup has been successfully completed and saved to the following file:" - ) + print("The database backup has been successfully completed and saved to the following file:") print(output_file) print("You can import this database backup with the following command:") print(f"invoke import-db --input-file '{output_file}'") From 1d9d981cbc366040559fe92c0562cb68d5c9c028 Mon Sep 17 00:00:00 2001 From: Stephen Kiely Date: Thu, 15 Aug 2024 23:27:20 -0500 Subject: [PATCH 4/4] Update changes/114.housekeeping Co-authored-by: Gary Snider <75227981+gsnider2195@users.noreply.github.com> --- changes/114.housekeeping | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes/114.housekeeping b/changes/114.housekeeping index b36df09..f61c7c0 100644 --- a/changes/114.housekeeping +++ b/changes/114.housekeeping @@ -1 +1 @@ -Rebake from Nautobot-App-Cookiecutter 2.3. \ No newline at end of file +Rebaked from the cookie `nautobot-app-v2.3.0`. \ No newline at end of file