netbox-cadplan 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- netbox_cadplan-0.2.0/CHANGELOG.md +27 -0
- netbox_cadplan-0.2.0/CONTRIBUTING.md +14 -0
- netbox_cadplan-0.2.0/LICENSE +21 -0
- netbox_cadplan-0.2.0/MANIFEST.in +7 -0
- netbox_cadplan-0.2.0/PKG-INFO +124 -0
- netbox_cadplan-0.2.0/README.md +92 -0
- netbox_cadplan-0.2.0/netbox_cadplan/__init__.py +28 -0
- netbox_cadplan-0.2.0/netbox_cadplan/api/__init__.py +0 -0
- netbox_cadplan-0.2.0/netbox_cadplan/api/serializers.py +125 -0
- netbox_cadplan-0.2.0/netbox_cadplan/api/urls.py +12 -0
- netbox_cadplan-0.2.0/netbox_cadplan/api/views.py +30 -0
- netbox_cadplan-0.2.0/netbox_cadplan/choices.py +41 -0
- netbox_cadplan-0.2.0/netbox_cadplan/filtersets.py +20 -0
- netbox_cadplan-0.2.0/netbox_cadplan/forms.py +92 -0
- netbox_cadplan-0.2.0/netbox_cadplan/locale/fr/LC_MESSAGES/django.mo +0 -0
- netbox_cadplan-0.2.0/netbox_cadplan/locale/fr/LC_MESSAGES/django.po +408 -0
- netbox_cadplan-0.2.0/netbox_cadplan/locale/fr/LC_MESSAGES/djangojs.mo +0 -0
- netbox_cadplan-0.2.0/netbox_cadplan/locale/fr/LC_MESSAGES/djangojs.po +236 -0
- netbox_cadplan-0.2.0/netbox_cadplan/migrations/0001_initial.py +265 -0
- netbox_cadplan-0.2.0/netbox_cadplan/migrations/0002_placedobject_outside_wall.py +18 -0
- netbox_cadplan-0.2.0/netbox_cadplan/migrations/0003_planzone_source_polygon.py +18 -0
- netbox_cadplan-0.2.0/netbox_cadplan/migrations/__init__.py +0 -0
- netbox_cadplan-0.2.0/netbox_cadplan/models.py +261 -0
- netbox_cadplan-0.2.0/netbox_cadplan/navigation.py +29 -0
- netbox_cadplan-0.2.0/netbox_cadplan/signals.py +16 -0
- netbox_cadplan-0.2.0/netbox_cadplan/static/netbox_cadplan/css/plan.css +0 -0
- netbox_cadplan-0.2.0/netbox_cadplan/static/netbox_cadplan/js/plan_editor.js +2840 -0
- netbox_cadplan-0.2.0/netbox_cadplan/tables.py +24 -0
- netbox_cadplan-0.2.0/netbox_cadplan/template_content.py +19 -0
- netbox_cadplan-0.2.0/netbox_cadplan/templates/netbox_cadplan/devicetype_shape_edit.html +46 -0
- netbox_cadplan-0.2.0/netbox_cadplan/templates/netbox_cadplan/inc/devicetype_plan_shape_panel.html +34 -0
- netbox_cadplan-0.2.0/netbox_cadplan/templates/netbox_cadplan/location_tab.html +82 -0
- netbox_cadplan-0.2.0/netbox_cadplan/templates/netbox_cadplan/plan.html +180 -0
- netbox_cadplan-0.2.0/netbox_cadplan/templates/netbox_cadplan/plan_tab.html +109 -0
- netbox_cadplan-0.2.0/netbox_cadplan/urls.py +64 -0
- netbox_cadplan-0.2.0/netbox_cadplan/utils.py +630 -0
- netbox_cadplan-0.2.0/netbox_cadplan/views.py +1029 -0
- netbox_cadplan-0.2.0/netbox_cadplan.egg-info/PKG-INFO +124 -0
- netbox_cadplan-0.2.0/netbox_cadplan.egg-info/SOURCES.txt +44 -0
- netbox_cadplan-0.2.0/netbox_cadplan.egg-info/dependency_links.txt +1 -0
- netbox_cadplan-0.2.0/netbox_cadplan.egg-info/requires.txt +8 -0
- netbox_cadplan-0.2.0/netbox_cadplan.egg-info/top_level.txt +1 -0
- netbox_cadplan-0.2.0/pyproject.toml +54 -0
- netbox_cadplan-0.2.0/setup.cfg +15 -0
- netbox_cadplan-0.2.0/tests/test_utils.py +130 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## 0.2.0
|
|
6
|
+
|
|
7
|
+
- Add "Position by distance" tool and object selection highlighting on the
|
|
8
|
+
plan canvas.
|
|
9
|
+
- Add Center view control, fit-to-view canvas sizing, and pickable-list
|
|
10
|
+
refresh.
|
|
11
|
+
- Theme-aware label colors on canvas.
|
|
12
|
+
- Cache parsed DXF documents and only list layers with geometry, for faster
|
|
13
|
+
layer selection.
|
|
14
|
+
- Replace the layer selection list with a searchable dropdown with a loading
|
|
15
|
+
state.
|
|
16
|
+
- Link to device type shape configuration directly from unplaceable devices.
|
|
17
|
+
- Switch the default UI to English, with French available as a translation.
|
|
18
|
+
- Fix Properties panel not refreshing after right-click rotate on a selected
|
|
19
|
+
object.
|
|
20
|
+
|
|
21
|
+
## 0.1.0
|
|
22
|
+
|
|
23
|
+
- Initial release: import DXF/DWG plans linked to Sites and Locations.
|
|
24
|
+
- Automatic zone splitting matched to descendant Locations, with per-location
|
|
25
|
+
SVG display tab.
|
|
26
|
+
- Device type plan shapes and interactive object placement on zones.
|
|
27
|
+
- DXF re-export.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to netbox-cadplan.
|
|
4
|
+
|
|
5
|
+
- Please open issues for bugs or feature requests on the repository issue tracker.
|
|
6
|
+
- For code contributions, fork the repository, create a feature branch, and open a pull request.
|
|
7
|
+
- Ensure linters and tests pass before submitting a PR.
|
|
8
|
+
|
|
9
|
+
Guidelines:
|
|
10
|
+
- Keep changes focused and well-documented.
|
|
11
|
+
- Add tests for new features or bug fixes.
|
|
12
|
+
- Run `ruff` and `black` to format and lint code.
|
|
13
|
+
|
|
14
|
+
Maintainer contact: see repository settings.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Simon Lacroix
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: netbox-cadplan
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: NetBox plugin: visual floor-plan management linked to Sites and Locations, with DXF/DWG import
|
|
5
|
+
Author: G1tHub-PRO, SelfSimon
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/SelfSimon/netbox-cadplan
|
|
8
|
+
Project-URL: Repository, https://github.com/SelfSimon/netbox-cadplan
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/SelfSimon/netbox-cadplan/issues
|
|
10
|
+
Keywords: netbox,plugin,plan,floorplan,dxf,dwg,sites,locations
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Plugins
|
|
13
|
+
Classifier: Framework :: Django
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: System :: Networking
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: ezdxf
|
|
25
|
+
Requires-Dist: svgwrite
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff>=0.12.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=23.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pre-commit>=2.20; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# netbox-cadplan
|
|
34
|
+
|
|
35
|
+
NetBox plugin for visual floor-plan management. A plan is attached to a
|
|
36
|
+
NetBox `Site` (required) and, optionally, to a `Location` within that site,
|
|
37
|
+
based on an imported DXF/DWG file. The plan is split into zones associated
|
|
38
|
+
with locations (descendant Locations of the plan's Location, or the whole
|
|
39
|
+
site if no Location is specified), with the SVG of each location displayed
|
|
40
|
+
in a dedicated tab on its NetBox page.
|
|
41
|
+
|
|
42
|
+
## Compatibility
|
|
43
|
+
|
|
44
|
+
| Package | NetBox | Python |
|
|
45
|
+
|---------|---------------|--------|
|
|
46
|
+
| 0.1.x | 4.6.0 – 4.6.99 | ≥ 3.10 |
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
### 1. Install the package
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install netbox-cadplan
|
|
54
|
+
# or from source:
|
|
55
|
+
pip install git+https://github.com/simonlacroix/netbox-cadplan.git
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. Enable the plugin in NetBox
|
|
59
|
+
|
|
60
|
+
Add the plugin to `configuration.py` (or `configuration/plugins.py` depending
|
|
61
|
+
on your setup):
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
PLUGINS = [
|
|
65
|
+
# ... other plugins
|
|
66
|
+
'netbox_cadplan',
|
|
67
|
+
]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 3. Run migrations and restart NetBox
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
python manage.py migrate netbox_cadplan
|
|
74
|
+
sudo systemctl restart netbox netbox-rq
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
- Import a DXF/DWG file to create a **Plan**, linked to a Site and optionally
|
|
80
|
+
a Location.
|
|
81
|
+
- The plan is automatically split into **zones** matched to Locations
|
|
82
|
+
descending from the plan's scope.
|
|
83
|
+
- Each Location gets a dedicated tab showing the SVG of its zone.
|
|
84
|
+
- Objects (device types with a configured plan shape) can be placed on a
|
|
85
|
+
zone and repositioned interactively.
|
|
86
|
+
- Plans can be re-exported to DXF.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT — see [LICENSE](LICENSE).
|
|
91
|
+
|
|
92
|
+
## Contributors
|
|
93
|
+
|
|
94
|
+
- [Simon Lacroix](https://github.com/simonlacroix) — Original author
|
|
95
|
+
|
|
96
|
+
## Developer setup
|
|
97
|
+
|
|
98
|
+
Quick setup for contributors — creates a virtualenv, installs development
|
|
99
|
+
dependencies and installs the `pre-commit` git hooks.
|
|
100
|
+
|
|
101
|
+
PowerShell (Windows):
|
|
102
|
+
|
|
103
|
+
```powershell
|
|
104
|
+
python -m venv .venv
|
|
105
|
+
.\.venv\Scripts\Activate.ps1
|
|
106
|
+
python -m pip install --upgrade pip
|
|
107
|
+
pip install -e ".[dev]"
|
|
108
|
+
pre-commit install
|
|
109
|
+
pre-commit run --all-files
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
POSIX (macOS / Linux):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python -m venv .venv
|
|
116
|
+
source .venv/bin/activate
|
|
117
|
+
python -m pip install --upgrade pip
|
|
118
|
+
pip install -e '.[dev]'
|
|
119
|
+
pre-commit install
|
|
120
|
+
pre-commit run --all-files
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
If you prefer automation, run `scripts/bootstrap.ps1` on Windows or
|
|
124
|
+
`scripts/bootstrap.sh` on POSIX systems to perform these steps.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# netbox-cadplan
|
|
2
|
+
|
|
3
|
+
NetBox plugin for visual floor-plan management. A plan is attached to a
|
|
4
|
+
NetBox `Site` (required) and, optionally, to a `Location` within that site,
|
|
5
|
+
based on an imported DXF/DWG file. The plan is split into zones associated
|
|
6
|
+
with locations (descendant Locations of the plan's Location, or the whole
|
|
7
|
+
site if no Location is specified), with the SVG of each location displayed
|
|
8
|
+
in a dedicated tab on its NetBox page.
|
|
9
|
+
|
|
10
|
+
## Compatibility
|
|
11
|
+
|
|
12
|
+
| Package | NetBox | Python |
|
|
13
|
+
|---------|---------------|--------|
|
|
14
|
+
| 0.1.x | 4.6.0 – 4.6.99 | ≥ 3.10 |
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### 1. Install the package
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install netbox-cadplan
|
|
22
|
+
# or from source:
|
|
23
|
+
pip install git+https://github.com/simonlacroix/netbox-cadplan.git
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 2. Enable the plugin in NetBox
|
|
27
|
+
|
|
28
|
+
Add the plugin to `configuration.py` (or `configuration/plugins.py` depending
|
|
29
|
+
on your setup):
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
PLUGINS = [
|
|
33
|
+
# ... other plugins
|
|
34
|
+
'netbox_cadplan',
|
|
35
|
+
]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3. Run migrations and restart NetBox
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python manage.py migrate netbox_cadplan
|
|
42
|
+
sudo systemctl restart netbox netbox-rq
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
- Import a DXF/DWG file to create a **Plan**, linked to a Site and optionally
|
|
48
|
+
a Location.
|
|
49
|
+
- The plan is automatically split into **zones** matched to Locations
|
|
50
|
+
descending from the plan's scope.
|
|
51
|
+
- Each Location gets a dedicated tab showing the SVG of its zone.
|
|
52
|
+
- Objects (device types with a configured plan shape) can be placed on a
|
|
53
|
+
zone and repositioned interactively.
|
|
54
|
+
- Plans can be re-exported to DXF.
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
MIT — see [LICENSE](LICENSE).
|
|
59
|
+
|
|
60
|
+
## Contributors
|
|
61
|
+
|
|
62
|
+
- [Simon Lacroix](https://github.com/simonlacroix) — Original author
|
|
63
|
+
|
|
64
|
+
## Developer setup
|
|
65
|
+
|
|
66
|
+
Quick setup for contributors — creates a virtualenv, installs development
|
|
67
|
+
dependencies and installs the `pre-commit` git hooks.
|
|
68
|
+
|
|
69
|
+
PowerShell (Windows):
|
|
70
|
+
|
|
71
|
+
```powershell
|
|
72
|
+
python -m venv .venv
|
|
73
|
+
.\.venv\Scripts\Activate.ps1
|
|
74
|
+
python -m pip install --upgrade pip
|
|
75
|
+
pip install -e ".[dev]"
|
|
76
|
+
pre-commit install
|
|
77
|
+
pre-commit run --all-files
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
POSIX (macOS / Linux):
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
python -m venv .venv
|
|
84
|
+
source .venv/bin/activate
|
|
85
|
+
python -m pip install --upgrade pip
|
|
86
|
+
pip install -e '.[dev]'
|
|
87
|
+
pre-commit install
|
|
88
|
+
pre-commit run --all-files
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
If you prefer automation, run `scripts/bootstrap.ps1` on Windows or
|
|
92
|
+
`scripts/bootstrap.sh` on POSIX systems to perform these steps.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from netbox.plugins import PluginConfig
|
|
3
|
+
except Exception:
|
|
4
|
+
# Minimal fallback for local development / tests when NetBox is not
|
|
5
|
+
# available. This avoids import-time errors during pytest collection.
|
|
6
|
+
class PluginConfig: # type: ignore
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NetBoxCadPlanConfig(PluginConfig):
|
|
11
|
+
name = "netbox_cadplan"
|
|
12
|
+
verbose_name = "NetBox CadPlan"
|
|
13
|
+
description = "Visual floor-plan management linked to Sites and Locations"
|
|
14
|
+
version = "0.2.0"
|
|
15
|
+
author = "Simon Lacroix"
|
|
16
|
+
author_email = "simonlacroix@live.ca"
|
|
17
|
+
base_url = "cadplan"
|
|
18
|
+
min_version = "4.6.0"
|
|
19
|
+
max_version = "4.6.99"
|
|
20
|
+
required_settings = []
|
|
21
|
+
default_settings = {}
|
|
22
|
+
|
|
23
|
+
def ready(self):
|
|
24
|
+
super().ready()
|
|
25
|
+
from . import signals # noqa: F401
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
config = NetBoxCadPlanConfig
|
|
File without changes
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from core.models import ObjectType
|
|
2
|
+
from dcim.api.serializers import (
|
|
3
|
+
DeviceTypeSerializer,
|
|
4
|
+
LocationSerializer,
|
|
5
|
+
SiteSerializer,
|
|
6
|
+
)
|
|
7
|
+
from netbox.api.fields import ContentTypeField
|
|
8
|
+
from netbox.api.gfk_fields import GFKSerializerField
|
|
9
|
+
from netbox.api.serializers import NetBoxModelSerializer
|
|
10
|
+
from rest_framework import serializers
|
|
11
|
+
|
|
12
|
+
from ..models import DeviceTypeShape, PlacedObject, Plan, PlanZone
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PlanSerializer(NetBoxModelSerializer):
|
|
16
|
+
url = serializers.HyperlinkedIdentityField(
|
|
17
|
+
view_name="plugins-api:netbox_cadplan-api:plan-detail"
|
|
18
|
+
)
|
|
19
|
+
site = SiteSerializer(nested=True)
|
|
20
|
+
location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
21
|
+
|
|
22
|
+
class Meta:
|
|
23
|
+
model = Plan
|
|
24
|
+
fields = [
|
|
25
|
+
"id",
|
|
26
|
+
"url",
|
|
27
|
+
"display",
|
|
28
|
+
"name",
|
|
29
|
+
"site",
|
|
30
|
+
"location",
|
|
31
|
+
"dxf_file",
|
|
32
|
+
"selected_layer",
|
|
33
|
+
"width_px",
|
|
34
|
+
"height_px",
|
|
35
|
+
"mm_per_px",
|
|
36
|
+
"tags",
|
|
37
|
+
"custom_fields",
|
|
38
|
+
"created",
|
|
39
|
+
"last_updated",
|
|
40
|
+
]
|
|
41
|
+
brief_fields = ("id", "url", "display", "name")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class PlanZoneSerializer(NetBoxModelSerializer):
|
|
45
|
+
url = serializers.HyperlinkedIdentityField(
|
|
46
|
+
view_name="plugins-api:netbox_cadplan-api:planzone-detail"
|
|
47
|
+
)
|
|
48
|
+
plan = PlanSerializer(nested=True)
|
|
49
|
+
location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
50
|
+
|
|
51
|
+
class Meta:
|
|
52
|
+
model = PlanZone
|
|
53
|
+
fields = [
|
|
54
|
+
"id",
|
|
55
|
+
"url",
|
|
56
|
+
"display",
|
|
57
|
+
"plan",
|
|
58
|
+
"number",
|
|
59
|
+
"location",
|
|
60
|
+
"polygon_data",
|
|
61
|
+
"svg_file",
|
|
62
|
+
"label",
|
|
63
|
+
"tags",
|
|
64
|
+
"custom_fields",
|
|
65
|
+
"created",
|
|
66
|
+
"last_updated",
|
|
67
|
+
]
|
|
68
|
+
brief_fields = ("id", "url", "display", "number", "label")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class DeviceTypeShapeSerializer(NetBoxModelSerializer):
|
|
72
|
+
url = serializers.HyperlinkedIdentityField(
|
|
73
|
+
view_name="plugins-api:netbox_cadplan-api:devicetypeshape-detail"
|
|
74
|
+
)
|
|
75
|
+
device_type = DeviceTypeSerializer(nested=True)
|
|
76
|
+
|
|
77
|
+
class Meta:
|
|
78
|
+
model = DeviceTypeShape
|
|
79
|
+
fields = [
|
|
80
|
+
"id",
|
|
81
|
+
"url",
|
|
82
|
+
"display",
|
|
83
|
+
"device_type",
|
|
84
|
+
"shape",
|
|
85
|
+
"width",
|
|
86
|
+
"depth",
|
|
87
|
+
"diameter",
|
|
88
|
+
"unit",
|
|
89
|
+
"tags",
|
|
90
|
+
"custom_fields",
|
|
91
|
+
"created",
|
|
92
|
+
"last_updated",
|
|
93
|
+
]
|
|
94
|
+
brief_fields = ("id", "url", "display", "device_type", "shape")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class PlacedObjectSerializer(NetBoxModelSerializer):
|
|
98
|
+
url = serializers.HyperlinkedIdentityField(
|
|
99
|
+
view_name="plugins-api:netbox_cadplan-api:placedobject-detail"
|
|
100
|
+
)
|
|
101
|
+
zone = PlanZoneSerializer(nested=True)
|
|
102
|
+
object_type = ContentTypeField(queryset=ObjectType.objects.all())
|
|
103
|
+
object = GFKSerializerField(source="content_object", read_only=True)
|
|
104
|
+
|
|
105
|
+
class Meta:
|
|
106
|
+
model = PlacedObject
|
|
107
|
+
fields = [
|
|
108
|
+
"id",
|
|
109
|
+
"url",
|
|
110
|
+
"display",
|
|
111
|
+
"zone",
|
|
112
|
+
"object_type",
|
|
113
|
+
"object_id",
|
|
114
|
+
"object",
|
|
115
|
+
"x",
|
|
116
|
+
"y",
|
|
117
|
+
"rotation",
|
|
118
|
+
"snap_to_wall",
|
|
119
|
+
"name_position",
|
|
120
|
+
"tags",
|
|
121
|
+
"custom_fields",
|
|
122
|
+
"created",
|
|
123
|
+
"last_updated",
|
|
124
|
+
]
|
|
125
|
+
brief_fields = ("id", "url", "display", "object_type", "object_id")
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from netbox.api.routers import NetBoxRouter
|
|
2
|
+
|
|
3
|
+
from . import views
|
|
4
|
+
|
|
5
|
+
app_name = "netbox_cadplan"
|
|
6
|
+
router = NetBoxRouter()
|
|
7
|
+
router.register("plans", views.PlanViewSet)
|
|
8
|
+
router.register("plan-zones", views.PlanZoneViewSet)
|
|
9
|
+
router.register("device-type-shapes", views.DeviceTypeShapeViewSet)
|
|
10
|
+
router.register("placed-objects", views.PlacedObjectViewSet)
|
|
11
|
+
|
|
12
|
+
urlpatterns = router.urls
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from netbox.api.viewsets import NetBoxModelViewSet
|
|
2
|
+
|
|
3
|
+
from .. import filtersets, models
|
|
4
|
+
from .serializers import (
|
|
5
|
+
DeviceTypeShapeSerializer,
|
|
6
|
+
PlacedObjectSerializer,
|
|
7
|
+
PlanSerializer,
|
|
8
|
+
PlanZoneSerializer,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PlanViewSet(NetBoxModelViewSet):
|
|
13
|
+
queryset = models.Plan.objects.all()
|
|
14
|
+
serializer_class = PlanSerializer
|
|
15
|
+
filterset_class = filtersets.PlanFilterSet
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PlanZoneViewSet(NetBoxModelViewSet):
|
|
19
|
+
queryset = models.PlanZone.objects.all()
|
|
20
|
+
serializer_class = PlanZoneSerializer
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DeviceTypeShapeViewSet(NetBoxModelViewSet):
|
|
24
|
+
queryset = models.DeviceTypeShape.objects.all()
|
|
25
|
+
serializer_class = DeviceTypeShapeSerializer
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PlacedObjectViewSet(NetBoxModelViewSet):
|
|
29
|
+
queryset = models.PlacedObject.objects.all()
|
|
30
|
+
serializer_class = PlacedObjectSerializer
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from django.utils.translation import gettext_lazy as _
|
|
2
|
+
from utilities.choices import ChoiceSet
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ShapeChoices(ChoiceSet):
|
|
6
|
+
|
|
7
|
+
RECTANGLE = "rectangle"
|
|
8
|
+
CIRCLE = "circle"
|
|
9
|
+
|
|
10
|
+
CHOICES = (
|
|
11
|
+
(RECTANGLE, _("Rectangle")),
|
|
12
|
+
(CIRCLE, _("Circle")),
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LengthUnitChoices(ChoiceSet):
|
|
17
|
+
|
|
18
|
+
UNIT_CENTIMETER = "cm"
|
|
19
|
+
UNIT_INCH = "in"
|
|
20
|
+
|
|
21
|
+
CHOICES = (
|
|
22
|
+
(UNIT_CENTIMETER, _("Centimeters")),
|
|
23
|
+
(UNIT_INCH, _("Inches")),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class NamePositionChoices(ChoiceSet):
|
|
28
|
+
|
|
29
|
+
TOP = "top"
|
|
30
|
+
BOTTOM = "bottom"
|
|
31
|
+
LEFT = "left"
|
|
32
|
+
RIGHT = "right"
|
|
33
|
+
CENTER = "center"
|
|
34
|
+
|
|
35
|
+
CHOICES = (
|
|
36
|
+
(TOP, _("Top")),
|
|
37
|
+
(BOTTOM, _("Bottom")),
|
|
38
|
+
(LEFT, _("Left")),
|
|
39
|
+
(RIGHT, _("Right")),
|
|
40
|
+
(CENTER, _("Center")),
|
|
41
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import django_filters
|
|
2
|
+
from django.db.models import Q
|
|
3
|
+
from netbox.filtersets import NetBoxModelFilterSet
|
|
4
|
+
|
|
5
|
+
from .models import Plan
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PlanFilterSet(NetBoxModelFilterSet):
|
|
9
|
+
q = django_filters.CharFilter(method="search", label="Search")
|
|
10
|
+
|
|
11
|
+
class Meta:
|
|
12
|
+
model = Plan
|
|
13
|
+
fields = ("id", "name", "site", "location", "selected_layer")
|
|
14
|
+
|
|
15
|
+
def search(self, queryset, name, value):
|
|
16
|
+
if not value.strip():
|
|
17
|
+
return queryset
|
|
18
|
+
return queryset.filter(
|
|
19
|
+
Q(name__icontains=value) | Q(selected_layer__icontains=value)
|
|
20
|
+
)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from dcim.models import Location, Site
|
|
2
|
+
from django.core.exceptions import ValidationError
|
|
3
|
+
from django.utils.translation import gettext_lazy as _
|
|
4
|
+
from netbox.forms import NetBoxModelForm
|
|
5
|
+
from utilities.forms.fields import DynamicModelChoiceField
|
|
6
|
+
from utilities.forms.rendering import FieldSet, InlineFields
|
|
7
|
+
|
|
8
|
+
from .models import DeviceTypeShape, Plan
|
|
9
|
+
|
|
10
|
+
MAX_DXF_UPLOAD_SIZE = 50 * 1024 * 1024 # 50 Mo
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _validate_dxf_size(dxf_file):
|
|
14
|
+
if dxf_file and dxf_file.size > MAX_DXF_UPLOAD_SIZE:
|
|
15
|
+
raise ValidationError(
|
|
16
|
+
_("The DXF/DWG file exceeds the maximum allowed size (50 MB).")
|
|
17
|
+
)
|
|
18
|
+
return dxf_file
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PlanForm(NetBoxModelForm):
|
|
22
|
+
site = DynamicModelChoiceField(
|
|
23
|
+
queryset=Site.objects.all(),
|
|
24
|
+
required=True,
|
|
25
|
+
)
|
|
26
|
+
location = DynamicModelChoiceField(
|
|
27
|
+
queryset=Location.objects.all(),
|
|
28
|
+
required=False,
|
|
29
|
+
query_params={"site_id": "$site"},
|
|
30
|
+
help_text=_(
|
|
31
|
+
"Optional NetBox Location (leave empty for a plan covering the whole site)"
|
|
32
|
+
),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
fieldsets = (FieldSet("name", "site", "location", "dxf_file", name=_("Plan")),)
|
|
36
|
+
|
|
37
|
+
class Meta:
|
|
38
|
+
model = Plan
|
|
39
|
+
fields = ("name", "site", "location", "dxf_file")
|
|
40
|
+
|
|
41
|
+
def clean_dxf_file(self):
|
|
42
|
+
return _validate_dxf_size(self.cleaned_data.get("dxf_file"))
|
|
43
|
+
|
|
44
|
+
def clean(self):
|
|
45
|
+
super().clean()
|
|
46
|
+
cleaned_data = self.cleaned_data
|
|
47
|
+
site = cleaned_data.get("site")
|
|
48
|
+
location = cleaned_data.get("location")
|
|
49
|
+
if site and location and location.site_id != site.id:
|
|
50
|
+
self.add_error(
|
|
51
|
+
"location",
|
|
52
|
+
_("The selected location does not belong to the chosen site."),
|
|
53
|
+
)
|
|
54
|
+
return cleaned_data
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ReimportDxfForm(NetBoxModelForm):
|
|
58
|
+
"""
|
|
59
|
+
Replaces only the DXF file of an already-confirmed plan (not name/site/location,
|
|
60
|
+
unlike PlanForm) — clears selected_layer on save to make the layer selection
|
|
61
|
+
panel reappear (see PlanReimportDxfView).
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
fieldsets = (FieldSet("dxf_file", name=_("Reimport a DXF")),)
|
|
65
|
+
|
|
66
|
+
class Meta:
|
|
67
|
+
model = Plan
|
|
68
|
+
fields = ("dxf_file",)
|
|
69
|
+
|
|
70
|
+
def clean_dxf_file(self):
|
|
71
|
+
return _validate_dxf_size(self.cleaned_data.get("dxf_file"))
|
|
72
|
+
|
|
73
|
+
def save(self, commit=True):
|
|
74
|
+
instance = super().save(commit=False)
|
|
75
|
+
instance.selected_layer = ""
|
|
76
|
+
if commit:
|
|
77
|
+
instance.save()
|
|
78
|
+
return instance
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class DeviceTypeShapeForm(NetBoxModelForm):
|
|
82
|
+
fieldsets = (
|
|
83
|
+
FieldSet(
|
|
84
|
+
"shape",
|
|
85
|
+
InlineFields("width", "depth", "diameter", "unit", label=_("Dimensions")),
|
|
86
|
+
name=_("Shape"),
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
class Meta:
|
|
91
|
+
model = DeviceTypeShape
|
|
92
|
+
fields = ("shape", "width", "depth", "diameter", "unit")
|
|
Binary file
|