netbox-pathways 0.1.0__tar.gz → 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_pathways-0.1.0 → netbox_pathways-0.2.0}/PKG-INFO +49 -6
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/README.md +47 -4
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/__init__.py +8 -1
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/api/serializers.py +31 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/choices.py +2 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/filterforms.py +27 -4
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/filters.py +26 -7
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/forms.py +164 -20
- netbox_pathways-0.2.0/netbox_pathways/graphql/filters.py +167 -0
- netbox_pathways-0.2.0/netbox_pathways/graphql/schema.py +67 -0
- netbox_pathways-0.2.0/netbox_pathways/graphql/types.py +116 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/management/commands/import_geodata.py +30 -2
- netbox_pathways-0.2.0/netbox_pathways/migrations/0015_installer_and_commissioned_date.py +39 -0
- netbox_pathways-0.2.0/netbox_pathways/migrations/0016_cablesegment_lashed_with.py +18 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/models.py +43 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/search.py +1 -1
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/detail-map.min.js +2 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/detail-map.min.js.map +7 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/endpoint-markers.min.js +2 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/endpoint-markers.min.js.map +7 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/pathways-field.min.js +2 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/pathways-field.min.js.map +7 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/pathways-map.min.js +2 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/pathways-map.min.js.map +7 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/route-planner-map.min.js +2 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/dist/route-planner-map.min.js.map +7 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/vendor/geoman/leaflet-geoman.css +2 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/vendor/geoman/leaflet-geoman.js +2 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/vendor/leaflet/images/layers-2x.png +0 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/vendor/leaflet/images/layers.png +0 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/vendor/leaflet/images/marker-icon-2x.png +0 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/vendor/leaflet/images/marker-icon.png +0 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/vendor/leaflet/images/marker-shadow.png +0 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/vendor/leaflet/leaflet.css +661 -0
- netbox_pathways-0.2.0/netbox_pathways/static/netbox_pathways/vendor/leaflet/leaflet.js +6 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/tables.py +21 -0
- netbox_pathways-0.2.0/netbox_pathways/ui/__init__.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/ui/panels.py +39 -1
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/views.py +8 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways.egg-info/PKG-INFO +49 -6
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways.egg-info/SOURCES.txt +29 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways.egg-info/requires.txt +1 -1
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/pyproject.toml +26 -24
- netbox_pathways-0.2.0/tests/test_field_additions.py +113 -0
- netbox_pathways-0.2.0/tests/test_graphql.py +325 -0
- netbox_pathways-0.2.0/tests/test_import_geodata.py +36 -0
- netbox_pathways-0.2.0/tests/test_lashed_with.py +94 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/api/__init__.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/api/external_geo.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/api/geo.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/api/traversal.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/api/urls.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/api/views.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/geo.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/graph.py +0 -0
- {netbox_pathways-0.1.0/netbox_pathways/management → netbox_pathways-0.2.0/netbox_pathways/graphql}/__init__.py +0 -0
- {netbox_pathways-0.1.0/netbox_pathways/management/commands → netbox_pathways-0.2.0/netbox_pathways/management}/__init__.py +0 -0
- {netbox_pathways-0.1.0/netbox_pathways/ui → netbox_pathways-0.2.0/netbox_pathways/management/commands}/__init__.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/management/commands/_geodata_worker.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/management/commands/generate_qgis_project.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/management/commands/generate_sample_data.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0001_initial.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0002_replace_owner_with_tenant.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0003_structure_optional_site_dimensions.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0004_circuit_geometry.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0005_replace_unique_together_with_constraints.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0006_remove_cablesegment_sequence_enter_exit.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0007_cable_routing_redesign.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0008_conduitbank_pathway_subclass.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0009_remove_conduit_unique_position_per_bank_and_more.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0010_structure_status.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0011_rename_name_to_label.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0012_add_filter_field_indexes.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0013_plannedroute.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/0014_plannedroute_parent_split.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/migrations/__init__.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/navigation.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/registry.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/route_engine.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/routing.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/signals.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/static/netbox_pathways/css/leaflet-theme.css +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/static/netbox_pathways/css/pathways-map.css +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/static/netbox_pathways/qgis/pathways.qml +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/static/netbox_pathways/qgis/structures.qml +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/static/netbox_pathways/vendor/MarkerCluster.Default.css +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/static/netbox_pathways/vendor/MarkerCluster.css +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/static/netbox_pathways/vendor/leaflet.markercluster.js +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/template_content.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/aerialspan.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/buttons/apply_route.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/buttons/replan_route.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/buttons/revert_split.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/buttons/split_route.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/buttons/view_in_map.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/cable_route_tab.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/cablesegment.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/conduit.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/conduitbank.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/conduitjunction.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/directburied.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/inc/cable_add_segment_form.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/inc/cable_route_finder_results.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/inc/cable_routing_panel.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/inc/cable_segment_table.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/inc/connected_structures_panel.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/inc/constraint_card.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/inc/geo_map_panel.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/inc/plannedroute_map_panel.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/inc/planner_results.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/innerduct.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/map.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/pathway.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/pathwaylocation.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/plannedroute.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/plannedroute_apply.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/plannedroute_split.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/pullsheet_detail.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/pullsheet_list.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/route_planner.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/sitegeometry.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/structure.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/templates/netbox_pathways/widgets/map_widget.html +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways/urls.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways.egg-info/dependency_links.txt +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/netbox_pathways.egg-info/top_level.txt +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/setup.cfg +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_adjacency.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_cable_segment.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_circuit_geometry.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_endpoint_validation.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_external_geo.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_geo_api.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_graph.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_map_view.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_planned_route.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_registry.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_route_engine.py +0 -0
- {netbox_pathways-0.1.0 → netbox_pathways-0.2.0}/tests/test_routing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netbox-pathways
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: NetBox plugin for physical cable plant infrastructure documentation with GIS capabilities
|
|
5
5
|
Author-email: Jonathan Senecal <contact@jonathansenecal.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/jsenecal/netbox-pathways
|
|
@@ -19,7 +19,7 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
Requires-Dist: djangorestframework-gis>=1.2.0
|
|
20
20
|
Requires-Dist: networkx>=3.0
|
|
21
21
|
Provides-Extra: dev
|
|
22
|
-
Requires-Dist:
|
|
22
|
+
Requires-Dist: bump-my-version; extra == "dev"
|
|
23
23
|
Requires-Dist: pre-commit>=4.0.0; extra == "dev"
|
|
24
24
|
Requires-Dist: pytest; extra == "dev"
|
|
25
25
|
Requires-Dist: pytest-django>=4.5.0; extra == "dev"
|
|
@@ -30,6 +30,8 @@ Requires-Dist: zensical; extra == "docs"
|
|
|
30
30
|
|
|
31
31
|
# netbox-pathways
|
|
32
32
|
|
|
33
|
+
> **Under active development -- alpha.** netbox-pathways is pre-1.0 and changing fast. Models, migrations, REST/GeoJSON endpoints, and configuration keys may break between releases without deprecation cycles. Pin an exact version in production, expect to read the [CHANGELOG](CHANGELOG.md) before every upgrade, and back up your database before running migrations. Issue reports and PRs are very welcome.
|
|
34
|
+
|
|
33
35
|
> A NetBox plugin for documenting physical cable plant infrastructure with PostGIS integration. Track conduits, aerial spans, structures, and cable routing with geographic data, comparable to SmallWorld or ArcGIS with ArcFM for outside/inside plant documentation.
|
|
34
36
|
|
|
35
37
|
[](https://pypi.org/project/netbox-pathways/)
|
|
@@ -37,6 +39,7 @@ Requires-Dist: zensical; extra == "docs"
|
|
|
37
39
|
[](https://github.com/netbox-community/netbox)
|
|
38
40
|
[](https://github.com/jsenecal/netbox-pathways/actions/workflows/ci.yml)
|
|
39
41
|
[](https://codecov.io/gh/jsenecal/netbox-pathways)
|
|
42
|
+
[](https://jsenecal.github.io/netbox-pathways/)
|
|
40
43
|

|
|
41
44
|
[](LICENSE)
|
|
42
45
|
|
|
@@ -61,18 +64,50 @@ Requires-Dist: zensical; extra == "docs"
|
|
|
61
64
|
|
|
62
65
|
## Installation
|
|
63
66
|
|
|
67
|
+
> NetBox runs on plain PostgreSQL by default. This plugin requires PostGIS, so installing it on an existing NetBox deployment means changing your database setup. The short version is below; see [PostGIS Setup](https://jsenecal.github.io/netbox-pathways/getting-started/postgis-setup/) in the docs for the full walkthrough (system libraries, container images, migrating an existing database).
|
|
68
|
+
|
|
69
|
+
### 1. PostGIS prerequisites
|
|
70
|
+
|
|
71
|
+
Install the GIS system libraries on every NetBox host (web workers and `rq`), and PostGIS on the database server:
|
|
72
|
+
|
|
64
73
|
```bash
|
|
65
|
-
|
|
74
|
+
# Debian / Ubuntu (NetBox host)
|
|
75
|
+
sudo apt-get install -y gdal-bin libgdal-dev libgeos-dev libproj-dev binutils
|
|
76
|
+
|
|
77
|
+
# Database server: PostgreSQL 16+ with the PostGIS 3.4 package, then:
|
|
78
|
+
psql -d netbox -c "CREATE EXTENSION IF NOT EXISTS postgis;"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2. Switch NetBox to the PostGIS database backend
|
|
82
|
+
|
|
83
|
+
In `configuration.py`, the default `DATABASES` engine is plain PostgreSQL. Change `ENGINE` to the PostGIS backend (the standard `django.db.backends.postgresql` engine appears to work but fails the first time a geometry column is created):
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
DATABASES = {
|
|
87
|
+
"default": {
|
|
88
|
+
"ENGINE": "django.contrib.gis.db.backends.postgis", # was django.db.backends.postgresql
|
|
89
|
+
"NAME": "netbox",
|
|
90
|
+
"USER": "netbox",
|
|
91
|
+
"PASSWORD": "...",
|
|
92
|
+
"HOST": "localhost",
|
|
93
|
+
"PORT": "",
|
|
94
|
+
"CONN_MAX_AGE": 300,
|
|
95
|
+
},
|
|
96
|
+
}
|
|
66
97
|
```
|
|
67
98
|
|
|
68
|
-
|
|
99
|
+
### 3. Install the plugin and configure it
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pip install netbox-pathways
|
|
103
|
+
```
|
|
69
104
|
|
|
70
105
|
```python
|
|
71
106
|
PLUGINS = ["netbox_pathways"]
|
|
72
107
|
|
|
73
108
|
PLUGINS_CONFIG = {
|
|
74
109
|
"netbox_pathways": {
|
|
75
|
-
"srid": 3348, # REQUIRED -- your EPSG code (see warning below)
|
|
110
|
+
"srid": 3348, # REQUIRED -- your EPSG code (see SRID warning below)
|
|
76
111
|
"map_center_lat": 45.5, # default map center latitude (optional)
|
|
77
112
|
"map_center_lon": -73.5,# default map center longitude (optional)
|
|
78
113
|
"map_zoom": 10, # default map zoom level (optional)
|
|
@@ -80,7 +115,7 @@ PLUGINS_CONFIG = {
|
|
|
80
115
|
}
|
|
81
116
|
```
|
|
82
117
|
|
|
83
|
-
|
|
118
|
+
### 4. Migrate, collect static, restart
|
|
84
119
|
|
|
85
120
|
```bash
|
|
86
121
|
cd /opt/netbox/netbox
|
|
@@ -89,6 +124,8 @@ python manage.py collectstatic --no-input
|
|
|
89
124
|
sudo systemctl restart netbox netbox-rq
|
|
90
125
|
```
|
|
91
126
|
|
|
127
|
+
If `migrate` fails with errors mentioning `postgis`, `gdal`, or `geos`, the database backend is still on plain PostgreSQL or the GIS system libraries are missing on the NetBox host. The [PostGIS Setup](https://jsenecal.github.io/netbox-pathways/getting-started/postgis-setup/) page covers diagnosis and recovery.
|
|
128
|
+
|
|
92
129
|
## Configuration
|
|
93
130
|
|
|
94
131
|
### SRID is immutable after installation
|
|
@@ -125,11 +162,17 @@ Open the generated `.qgs` file in QGIS. Style files (`.qml`) ship under `static/
|
|
|
125
162
|
|
|
126
163
|
All resources are exposed under `/api/plugins/pathways/`. GeoJSON variants live under `/api/plugins/pathways/geo/` for direct QGIS / OGR consumption. See [API Examples](https://jsenecal.github.io/netbox-pathways/developer/api-examples/) for full endpoint coverage.
|
|
127
164
|
|
|
165
|
+
## GraphQL
|
|
166
|
+
|
|
167
|
+
Every Pathways model is exposed on NetBox's `/graphql/` endpoint (single and `_list` queries: `structure`, `structure_list`, `pathway`, `pathway_list`, `conduit`, `conduit_list`, etc.). Geometry fields are intentionally omitted -- query the GeoJSON REST endpoints for spatial data.
|
|
168
|
+
|
|
128
169
|
## Documentation
|
|
129
170
|
|
|
130
171
|
Full documentation: **[jsenecal.github.io/netbox-pathways](https://jsenecal.github.io/netbox-pathways/)**
|
|
131
172
|
|
|
132
173
|
- [Installation](https://jsenecal.github.io/netbox-pathways/getting-started/installation/)
|
|
174
|
+
- [PostGIS Setup](https://jsenecal.github.io/netbox-pathways/getting-started/postgis-setup/)
|
|
175
|
+
- [SRID Selection](https://jsenecal.github.io/netbox-pathways/getting-started/srid/)
|
|
133
176
|
- [Configuration](https://jsenecal.github.io/netbox-pathways/getting-started/configuration/)
|
|
134
177
|
- [Concepts](https://jsenecal.github.io/netbox-pathways/user-guide/concepts/)
|
|
135
178
|
- [QGIS Integration](https://jsenecal.github.io/netbox-pathways/user-guide/qgis-integration/)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# netbox-pathways
|
|
2
2
|
|
|
3
|
+
> **Under active development -- alpha.** netbox-pathways is pre-1.0 and changing fast. Models, migrations, REST/GeoJSON endpoints, and configuration keys may break between releases without deprecation cycles. Pin an exact version in production, expect to read the [CHANGELOG](CHANGELOG.md) before every upgrade, and back up your database before running migrations. Issue reports and PRs are very welcome.
|
|
4
|
+
|
|
3
5
|
> A NetBox plugin for documenting physical cable plant infrastructure with PostGIS integration. Track conduits, aerial spans, structures, and cable routing with geographic data, comparable to SmallWorld or ArcGIS with ArcFM for outside/inside plant documentation.
|
|
4
6
|
|
|
5
7
|
[](https://pypi.org/project/netbox-pathways/)
|
|
@@ -7,6 +9,7 @@
|
|
|
7
9
|
[](https://github.com/netbox-community/netbox)
|
|
8
10
|
[](https://github.com/jsenecal/netbox-pathways/actions/workflows/ci.yml)
|
|
9
11
|
[](https://codecov.io/gh/jsenecal/netbox-pathways)
|
|
12
|
+
[](https://jsenecal.github.io/netbox-pathways/)
|
|
10
13
|

|
|
11
14
|
[](LICENSE)
|
|
12
15
|
|
|
@@ -31,18 +34,50 @@
|
|
|
31
34
|
|
|
32
35
|
## Installation
|
|
33
36
|
|
|
37
|
+
> NetBox runs on plain PostgreSQL by default. This plugin requires PostGIS, so installing it on an existing NetBox deployment means changing your database setup. The short version is below; see [PostGIS Setup](https://jsenecal.github.io/netbox-pathways/getting-started/postgis-setup/) in the docs for the full walkthrough (system libraries, container images, migrating an existing database).
|
|
38
|
+
|
|
39
|
+
### 1. PostGIS prerequisites
|
|
40
|
+
|
|
41
|
+
Install the GIS system libraries on every NetBox host (web workers and `rq`), and PostGIS on the database server:
|
|
42
|
+
|
|
34
43
|
```bash
|
|
35
|
-
|
|
44
|
+
# Debian / Ubuntu (NetBox host)
|
|
45
|
+
sudo apt-get install -y gdal-bin libgdal-dev libgeos-dev libproj-dev binutils
|
|
46
|
+
|
|
47
|
+
# Database server: PostgreSQL 16+ with the PostGIS 3.4 package, then:
|
|
48
|
+
psql -d netbox -c "CREATE EXTENSION IF NOT EXISTS postgis;"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. Switch NetBox to the PostGIS database backend
|
|
52
|
+
|
|
53
|
+
In `configuration.py`, the default `DATABASES` engine is plain PostgreSQL. Change `ENGINE` to the PostGIS backend (the standard `django.db.backends.postgresql` engine appears to work but fails the first time a geometry column is created):
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
DATABASES = {
|
|
57
|
+
"default": {
|
|
58
|
+
"ENGINE": "django.contrib.gis.db.backends.postgis", # was django.db.backends.postgresql
|
|
59
|
+
"NAME": "netbox",
|
|
60
|
+
"USER": "netbox",
|
|
61
|
+
"PASSWORD": "...",
|
|
62
|
+
"HOST": "localhost",
|
|
63
|
+
"PORT": "",
|
|
64
|
+
"CONN_MAX_AGE": 300,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
36
67
|
```
|
|
37
68
|
|
|
38
|
-
|
|
69
|
+
### 3. Install the plugin and configure it
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install netbox-pathways
|
|
73
|
+
```
|
|
39
74
|
|
|
40
75
|
```python
|
|
41
76
|
PLUGINS = ["netbox_pathways"]
|
|
42
77
|
|
|
43
78
|
PLUGINS_CONFIG = {
|
|
44
79
|
"netbox_pathways": {
|
|
45
|
-
"srid": 3348, # REQUIRED -- your EPSG code (see warning below)
|
|
80
|
+
"srid": 3348, # REQUIRED -- your EPSG code (see SRID warning below)
|
|
46
81
|
"map_center_lat": 45.5, # default map center latitude (optional)
|
|
47
82
|
"map_center_lon": -73.5,# default map center longitude (optional)
|
|
48
83
|
"map_zoom": 10, # default map zoom level (optional)
|
|
@@ -50,7 +85,7 @@ PLUGINS_CONFIG = {
|
|
|
50
85
|
}
|
|
51
86
|
```
|
|
52
87
|
|
|
53
|
-
|
|
88
|
+
### 4. Migrate, collect static, restart
|
|
54
89
|
|
|
55
90
|
```bash
|
|
56
91
|
cd /opt/netbox/netbox
|
|
@@ -59,6 +94,8 @@ python manage.py collectstatic --no-input
|
|
|
59
94
|
sudo systemctl restart netbox netbox-rq
|
|
60
95
|
```
|
|
61
96
|
|
|
97
|
+
If `migrate` fails with errors mentioning `postgis`, `gdal`, or `geos`, the database backend is still on plain PostgreSQL or the GIS system libraries are missing on the NetBox host. The [PostGIS Setup](https://jsenecal.github.io/netbox-pathways/getting-started/postgis-setup/) page covers diagnosis and recovery.
|
|
98
|
+
|
|
62
99
|
## Configuration
|
|
63
100
|
|
|
64
101
|
### SRID is immutable after installation
|
|
@@ -95,11 +132,17 @@ Open the generated `.qgs` file in QGIS. Style files (`.qml`) ship under `static/
|
|
|
95
132
|
|
|
96
133
|
All resources are exposed under `/api/plugins/pathways/`. GeoJSON variants live under `/api/plugins/pathways/geo/` for direct QGIS / OGR consumption. See [API Examples](https://jsenecal.github.io/netbox-pathways/developer/api-examples/) for full endpoint coverage.
|
|
97
134
|
|
|
135
|
+
## GraphQL
|
|
136
|
+
|
|
137
|
+
Every Pathways model is exposed on NetBox's `/graphql/` endpoint (single and `_list` queries: `structure`, `structure_list`, `pathway`, `pathway_list`, `conduit`, `conduit_list`, etc.). Geometry fields are intentionally omitted -- query the GeoJSON REST endpoints for spatial data.
|
|
138
|
+
|
|
98
139
|
## Documentation
|
|
99
140
|
|
|
100
141
|
Full documentation: **[jsenecal.github.io/netbox-pathways](https://jsenecal.github.io/netbox-pathways/)**
|
|
101
142
|
|
|
102
143
|
- [Installation](https://jsenecal.github.io/netbox-pathways/getting-started/installation/)
|
|
144
|
+
- [PostGIS Setup](https://jsenecal.github.io/netbox-pathways/getting-started/postgis-setup/)
|
|
145
|
+
- [SRID Selection](https://jsenecal.github.io/netbox-pathways/getting-started/srid/)
|
|
103
146
|
- [Configuration](https://jsenecal.github.io/netbox-pathways/getting-started/configuration/)
|
|
104
147
|
- [Concepts](https://jsenecal.github.io/netbox-pathways/user-guide/concepts/)
|
|
105
148
|
- [QGIS Integration](https://jsenecal.github.io/netbox-pathways/user-guide/qgis-integration/)
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
1
3
|
from netbox.plugins import PluginConfig
|
|
2
4
|
|
|
3
|
-
__version__ = "0.
|
|
5
|
+
__version__ = "0.2.0"
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
class NetBoxPathwaysConfig(PluginConfig):
|
|
@@ -11,6 +15,7 @@ class NetBoxPathwaysConfig(PluginConfig):
|
|
|
11
15
|
author = "Jonathan Senecal"
|
|
12
16
|
author_email = "contact@jonathansenecal.com"
|
|
13
17
|
base_url = "pathways"
|
|
18
|
+
graphql_schema = "graphql.schema.schema"
|
|
14
19
|
required_settings = ["srid"]
|
|
15
20
|
default_settings = {
|
|
16
21
|
"map_center_lat": 45.5017,
|
|
@@ -87,5 +92,7 @@ class NetBoxPathwaysConfig(PluginConfig):
|
|
|
87
92
|
# Register signals
|
|
88
93
|
from . import signals # noqa: F401
|
|
89
94
|
|
|
95
|
+
logger.info("%s plugin loaded", self.name)
|
|
96
|
+
|
|
90
97
|
|
|
91
98
|
config = NetBoxPathwaysConfig
|
|
@@ -42,6 +42,7 @@ class StructureSerializer(NetBoxModelSerializer):
|
|
|
42
42
|
structure_type = ChoiceField(choices=StructureTypeChoices, required=False, allow_blank=True)
|
|
43
43
|
site = SiteSerializer(nested=True, required=False, allow_null=True)
|
|
44
44
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
45
|
+
installed_by = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
45
46
|
no_pathways = drf_serializers.SerializerMethodField(read_only=True)
|
|
46
47
|
description = drf_serializers.SerializerMethodField(read_only=True)
|
|
47
48
|
|
|
@@ -63,7 +64,9 @@ class StructureSerializer(NetBoxModelSerializer):
|
|
|
63
64
|
"depth",
|
|
64
65
|
"elevation",
|
|
65
66
|
"installation_date",
|
|
67
|
+
"commissioned_date",
|
|
66
68
|
"tenant",
|
|
69
|
+
"installed_by",
|
|
67
70
|
"access_notes",
|
|
68
71
|
"comments",
|
|
69
72
|
"tags",
|
|
@@ -119,6 +122,7 @@ class ConduitBankSerializer(NetBoxModelSerializer):
|
|
|
119
122
|
configuration = ChoiceField(choices=ConduitBankConfigChoices, required=False, allow_blank=True)
|
|
120
123
|
encasement_type = ChoiceField(choices=EncasementTypeChoices, required=False, allow_blank=True)
|
|
121
124
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
125
|
+
installed_by = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
122
126
|
|
|
123
127
|
class Meta:
|
|
124
128
|
model = ConduitBank
|
|
@@ -133,12 +137,14 @@ class ConduitBankSerializer(NetBoxModelSerializer):
|
|
|
133
137
|
"start_face",
|
|
134
138
|
"end_face",
|
|
135
139
|
"tenant",
|
|
140
|
+
"installed_by",
|
|
136
141
|
"path",
|
|
137
142
|
"length",
|
|
138
143
|
"configuration",
|
|
139
144
|
"total_conduits",
|
|
140
145
|
"encasement_type",
|
|
141
146
|
"installation_date",
|
|
147
|
+
"commissioned_date",
|
|
142
148
|
"comments",
|
|
143
149
|
"tags",
|
|
144
150
|
"created",
|
|
@@ -157,6 +163,7 @@ class PathwaySerializer(NetBoxModelSerializer):
|
|
|
157
163
|
start_location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
158
164
|
end_location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
159
165
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
166
|
+
installed_by = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
160
167
|
cables_routed = drf_serializers.IntegerField(read_only=True)
|
|
161
168
|
|
|
162
169
|
class Meta:
|
|
@@ -174,9 +181,11 @@ class PathwaySerializer(NetBoxModelSerializer):
|
|
|
174
181
|
"start_location",
|
|
175
182
|
"end_location",
|
|
176
183
|
"tenant",
|
|
184
|
+
"installed_by",
|
|
177
185
|
"length",
|
|
178
186
|
"cables_routed",
|
|
179
187
|
"installation_date",
|
|
188
|
+
"commissioned_date",
|
|
180
189
|
"comments",
|
|
181
190
|
"tags",
|
|
182
191
|
"created",
|
|
@@ -196,6 +205,7 @@ class ConduitSerializer(NetBoxModelSerializer):
|
|
|
196
205
|
end_location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
197
206
|
conduit_bank = ConduitBankSerializer(nested=True, required=False, allow_null=True)
|
|
198
207
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
208
|
+
installed_by = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
199
209
|
cables_routed = drf_serializers.IntegerField(read_only=True)
|
|
200
210
|
|
|
201
211
|
class Meta:
|
|
@@ -220,9 +230,12 @@ class ConduitSerializer(NetBoxModelSerializer):
|
|
|
220
230
|
"bank_position",
|
|
221
231
|
"start_junction",
|
|
222
232
|
"end_junction",
|
|
233
|
+
"tenant",
|
|
234
|
+
"installed_by",
|
|
223
235
|
"length",
|
|
224
236
|
"cables_routed",
|
|
225
237
|
"installation_date",
|
|
238
|
+
"commissioned_date",
|
|
226
239
|
"comments",
|
|
227
240
|
"tags",
|
|
228
241
|
"created",
|
|
@@ -241,6 +254,7 @@ class AerialSpanSerializer(NetBoxModelSerializer):
|
|
|
241
254
|
start_location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
242
255
|
end_location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
243
256
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
257
|
+
installed_by = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
244
258
|
cables_routed = drf_serializers.IntegerField(read_only=True)
|
|
245
259
|
|
|
246
260
|
class Meta:
|
|
@@ -263,9 +277,12 @@ class AerialSpanSerializer(NetBoxModelSerializer):
|
|
|
263
277
|
"messenger_size",
|
|
264
278
|
"wind_loading",
|
|
265
279
|
"ice_loading",
|
|
280
|
+
"tenant",
|
|
281
|
+
"installed_by",
|
|
266
282
|
"length",
|
|
267
283
|
"cables_routed",
|
|
268
284
|
"installation_date",
|
|
285
|
+
"commissioned_date",
|
|
269
286
|
"comments",
|
|
270
287
|
"tags",
|
|
271
288
|
"created",
|
|
@@ -283,6 +300,7 @@ class DirectBuriedSerializer(NetBoxModelSerializer):
|
|
|
283
300
|
start_location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
284
301
|
end_location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
285
302
|
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
303
|
+
installed_by = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
286
304
|
cables_routed = drf_serializers.IntegerField(read_only=True)
|
|
287
305
|
|
|
288
306
|
class Meta:
|
|
@@ -303,9 +321,12 @@ class DirectBuriedSerializer(NetBoxModelSerializer):
|
|
|
303
321
|
"warning_tape",
|
|
304
322
|
"tracer_wire",
|
|
305
323
|
"armor_type",
|
|
324
|
+
"tenant",
|
|
325
|
+
"installed_by",
|
|
306
326
|
"length",
|
|
307
327
|
"cables_routed",
|
|
308
328
|
"installation_date",
|
|
329
|
+
"commissioned_date",
|
|
309
330
|
"comments",
|
|
310
331
|
"tags",
|
|
311
332
|
"created",
|
|
@@ -323,6 +344,7 @@ class InnerductSerializer(NetBoxModelSerializer):
|
|
|
323
344
|
start_location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
324
345
|
end_location = LocationSerializer(nested=True, required=False, allow_null=True)
|
|
325
346
|
parent_conduit = ConduitSerializer(nested=True, required=False, allow_null=True)
|
|
347
|
+
installed_by = TenantSerializer(nested=True, required=False, allow_null=True)
|
|
326
348
|
cables_routed = drf_serializers.IntegerField(read_only=True)
|
|
327
349
|
|
|
328
350
|
class Meta:
|
|
@@ -343,9 +365,11 @@ class InnerductSerializer(NetBoxModelSerializer):
|
|
|
343
365
|
"size",
|
|
344
366
|
"color",
|
|
345
367
|
"position",
|
|
368
|
+
"installed_by",
|
|
346
369
|
"length",
|
|
347
370
|
"cables_routed",
|
|
348
371
|
"installation_date",
|
|
372
|
+
"commissioned_date",
|
|
349
373
|
"comments",
|
|
350
374
|
"tags",
|
|
351
375
|
"created",
|
|
@@ -415,6 +439,12 @@ class CableSegmentSerializer(NetBoxModelSerializer):
|
|
|
415
439
|
)
|
|
416
440
|
cable = CableSerializer(nested=True, required=False, allow_null=True)
|
|
417
441
|
pathway = PathwaySerializer(nested=True, required=False, allow_null=True)
|
|
442
|
+
lashed_with = drf_serializers.PrimaryKeyRelatedField(
|
|
443
|
+
many=True,
|
|
444
|
+
required=False,
|
|
445
|
+
queryset=CableSegment.objects.all(),
|
|
446
|
+
help_text="Other cable segments lashed together with this one (symmetrical).",
|
|
447
|
+
)
|
|
418
448
|
|
|
419
449
|
class Meta:
|
|
420
450
|
model = CableSegment
|
|
@@ -426,6 +456,7 @@ class CableSegmentSerializer(NetBoxModelSerializer):
|
|
|
426
456
|
"cable",
|
|
427
457
|
"pathway",
|
|
428
458
|
"sequence",
|
|
459
|
+
"lashed_with",
|
|
429
460
|
"comments",
|
|
430
461
|
"tags",
|
|
431
462
|
"created",
|
|
@@ -9,6 +9,7 @@ class StructureStatusChoices(ChoiceSet):
|
|
|
9
9
|
STATUS_CONSTRUCTION = "construction"
|
|
10
10
|
STATUS_DECOMMISSIONING = "decommissioning"
|
|
11
11
|
STATUS_RETIRED = "retired"
|
|
12
|
+
STATUS_ABANDONED = "abandoned"
|
|
12
13
|
|
|
13
14
|
CHOICES = [
|
|
14
15
|
(STATUS_PLANNED, "Planned", "cyan"),
|
|
@@ -16,6 +17,7 @@ class StructureStatusChoices(ChoiceSet):
|
|
|
16
17
|
(STATUS_CONSTRUCTION, "Under Construction", "blue"),
|
|
17
18
|
(STATUS_DECOMMISSIONING, "Decommissioning", "yellow"),
|
|
18
19
|
(STATUS_RETIRED, "Retired", "red"),
|
|
20
|
+
(STATUS_ABANDONED, "Abandoned in place", "gray"),
|
|
19
21
|
]
|
|
20
22
|
|
|
21
23
|
|
|
@@ -41,7 +41,7 @@ class StructureFilterForm(NetBoxModelFilterSetForm):
|
|
|
41
41
|
fieldsets = (
|
|
42
42
|
FieldSet("q", "filter_id", "tag"),
|
|
43
43
|
FieldSet("status", "structure_type", "site_id", name="Attributes"),
|
|
44
|
-
FieldSet("tenant_id", name="Tenant"),
|
|
44
|
+
FieldSet("tenant_id", "installed_by_id", name="Tenant"),
|
|
45
45
|
)
|
|
46
46
|
status = forms.MultipleChoiceField(choices=StructureStatusChoices, required=False)
|
|
47
47
|
structure_type = forms.MultipleChoiceField(choices=StructureTypeChoices, required=False)
|
|
@@ -56,6 +56,12 @@ class StructureFilterForm(NetBoxModelFilterSetForm):
|
|
|
56
56
|
null_option="None",
|
|
57
57
|
label="Tenant",
|
|
58
58
|
)
|
|
59
|
+
installed_by_id = DynamicModelMultipleChoiceField(
|
|
60
|
+
queryset=Tenant.objects.all(),
|
|
61
|
+
required=False,
|
|
62
|
+
null_option="None",
|
|
63
|
+
label="Installed by",
|
|
64
|
+
)
|
|
59
65
|
tag = TagFilterField(model)
|
|
60
66
|
|
|
61
67
|
|
|
@@ -65,7 +71,7 @@ class PathwayFilterForm(NetBoxModelFilterSetForm):
|
|
|
65
71
|
FieldSet("q", "filter_id", "tag"),
|
|
66
72
|
FieldSet("pathway_type", name="Attributes"),
|
|
67
73
|
FieldSet("start_structure_id", "end_structure_id", "start_location_id", "end_location_id", name="Endpoints"),
|
|
68
|
-
FieldSet("tenant_id", name="Tenant"),
|
|
74
|
+
FieldSet("tenant_id", "installed_by_id", name="Tenant"),
|
|
69
75
|
)
|
|
70
76
|
pathway_type = forms.MultipleChoiceField(choices=PathwayTypeChoices, required=False)
|
|
71
77
|
start_structure_id = DynamicModelMultipleChoiceField(
|
|
@@ -94,6 +100,12 @@ class PathwayFilterForm(NetBoxModelFilterSetForm):
|
|
|
94
100
|
null_option="None",
|
|
95
101
|
label="Tenant",
|
|
96
102
|
)
|
|
103
|
+
installed_by_id = DynamicModelMultipleChoiceField(
|
|
104
|
+
queryset=Tenant.objects.all(),
|
|
105
|
+
required=False,
|
|
106
|
+
null_option="None",
|
|
107
|
+
label="Installed by",
|
|
108
|
+
)
|
|
97
109
|
tag = TagFilterField(model)
|
|
98
110
|
|
|
99
111
|
|
|
@@ -213,7 +225,7 @@ class ConduitBankFilterForm(NetBoxModelFilterSetForm):
|
|
|
213
225
|
FieldSet("q", "filter_id", "tag"),
|
|
214
226
|
FieldSet("configuration", "encasement_type", "start_face", "end_face", name="Attributes"),
|
|
215
227
|
FieldSet("start_structure_id", "end_structure_id", name="Endpoints"),
|
|
216
|
-
FieldSet("tenant_id", name="Tenant"),
|
|
228
|
+
FieldSet("tenant_id", "installed_by_id", name="Tenant"),
|
|
217
229
|
)
|
|
218
230
|
configuration = forms.MultipleChoiceField(choices=ConduitBankConfigChoices, required=False)
|
|
219
231
|
encasement_type = forms.MultipleChoiceField(choices=EncasementTypeChoices, required=False)
|
|
@@ -235,6 +247,12 @@ class ConduitBankFilterForm(NetBoxModelFilterSetForm):
|
|
|
235
247
|
null_option="None",
|
|
236
248
|
label="Tenant",
|
|
237
249
|
)
|
|
250
|
+
installed_by_id = DynamicModelMultipleChoiceField(
|
|
251
|
+
queryset=Tenant.objects.all(),
|
|
252
|
+
required=False,
|
|
253
|
+
null_option="None",
|
|
254
|
+
label="Installed by",
|
|
255
|
+
)
|
|
238
256
|
tag = TagFilterField(model)
|
|
239
257
|
|
|
240
258
|
|
|
@@ -261,7 +279,7 @@ class CableSegmentFilterForm(NetBoxModelFilterSetForm):
|
|
|
261
279
|
model = CableSegment
|
|
262
280
|
fieldsets = (
|
|
263
281
|
FieldSet("q", "filter_id", "tag"),
|
|
264
|
-
FieldSet("cable_id", "pathway_id", name="Attributes"),
|
|
282
|
+
FieldSet("cable_id", "pathway_id", "lashed_with_id", name="Attributes"),
|
|
265
283
|
)
|
|
266
284
|
cable_id = DynamicModelMultipleChoiceField(
|
|
267
285
|
queryset=Cable.objects.all(),
|
|
@@ -273,6 +291,11 @@ class CableSegmentFilterForm(NetBoxModelFilterSetForm):
|
|
|
273
291
|
required=False,
|
|
274
292
|
label="Pathway",
|
|
275
293
|
)
|
|
294
|
+
lashed_with_id = DynamicModelMultipleChoiceField(
|
|
295
|
+
queryset=CableSegment.objects.all(),
|
|
296
|
+
required=False,
|
|
297
|
+
label="Lashed with segment",
|
|
298
|
+
)
|
|
276
299
|
tag = TagFilterField(model)
|
|
277
300
|
|
|
278
301
|
|
|
@@ -4,6 +4,7 @@ from dcim.models import Cable, Location, Site
|
|
|
4
4
|
from django.db.models import Q
|
|
5
5
|
from netbox.filtersets import NetBoxModelFilterSet
|
|
6
6
|
from tenancy.filtersets import TenancyFilterSet
|
|
7
|
+
from tenancy.models import Tenant
|
|
7
8
|
from utilities.filters import MultiValueCharFilter, MultiValueNumberFilter
|
|
8
9
|
|
|
9
10
|
from .choices import (
|
|
@@ -58,6 +59,12 @@ class StructureFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|
|
58
59
|
distinct=False,
|
|
59
60
|
label="Site (slug)",
|
|
60
61
|
)
|
|
62
|
+
installed_by_id = django_filters.ModelMultipleChoiceFilter(
|
|
63
|
+
field_name="installed_by",
|
|
64
|
+
queryset=Tenant.objects.all(),
|
|
65
|
+
distinct=False,
|
|
66
|
+
label="Installed by (ID)",
|
|
67
|
+
)
|
|
61
68
|
height = MultiValueNumberFilter()
|
|
62
69
|
width = MultiValueNumberFilter()
|
|
63
70
|
length = MultiValueNumberFilter()
|
|
@@ -74,7 +81,7 @@ class StructureFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|
|
74
81
|
|
|
75
82
|
class Meta:
|
|
76
83
|
model = Structure
|
|
77
|
-
fields = ["id", "installation_date"]
|
|
84
|
+
fields = ["id", "installation_date", "commissioned_date"]
|
|
78
85
|
|
|
79
86
|
def filter_occupied(self, queryset, name, value):
|
|
80
87
|
occupied_pws = CableSegment.objects.values_list("pathway_id", flat=True)
|
|
@@ -165,6 +172,12 @@ class PathwayFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|
|
165
172
|
label="End Location (slug)",
|
|
166
173
|
)
|
|
167
174
|
length = MultiValueNumberFilter()
|
|
175
|
+
installed_by_id = django_filters.ModelMultipleChoiceFilter(
|
|
176
|
+
field_name="installed_by",
|
|
177
|
+
queryset=Tenant.objects.all(),
|
|
178
|
+
distinct=False,
|
|
179
|
+
label="Installed by (ID)",
|
|
180
|
+
)
|
|
168
181
|
occupied = django_filters.BooleanFilter(
|
|
169
182
|
method="filter_occupied",
|
|
170
183
|
label="Occupied (has routed cables)",
|
|
@@ -172,7 +185,7 @@ class PathwayFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|
|
172
185
|
|
|
173
186
|
class Meta:
|
|
174
187
|
model = Pathway
|
|
175
|
-
fields = ["id", "installation_date"]
|
|
188
|
+
fields = ["id", "installation_date", "commissioned_date"]
|
|
176
189
|
|
|
177
190
|
def filter_occupied(self, queryset, name, value):
|
|
178
191
|
occupied_pws = CableSegment.objects.values_list("pathway_id", flat=True)
|
|
@@ -244,7 +257,7 @@ class ConduitFilterSet(NetBoxModelFilterSet):
|
|
|
244
257
|
|
|
245
258
|
class Meta:
|
|
246
259
|
model = Conduit
|
|
247
|
-
fields = ["id", "installation_date"]
|
|
260
|
+
fields = ["id", "installation_date", "commissioned_date"]
|
|
248
261
|
|
|
249
262
|
def search(self, queryset, name, value):
|
|
250
263
|
if not value.strip():
|
|
@@ -292,7 +305,7 @@ class AerialSpanFilterSet(NetBoxModelFilterSet):
|
|
|
292
305
|
|
|
293
306
|
class Meta:
|
|
294
307
|
model = AerialSpan
|
|
295
|
-
fields = ["id", "installation_date"]
|
|
308
|
+
fields = ["id", "installation_date", "commissioned_date"]
|
|
296
309
|
|
|
297
310
|
def search(self, queryset, name, value):
|
|
298
311
|
if not value.strip():
|
|
@@ -334,7 +347,7 @@ class DirectBuriedFilterSet(NetBoxModelFilterSet):
|
|
|
334
347
|
|
|
335
348
|
class Meta:
|
|
336
349
|
model = DirectBuried
|
|
337
|
-
fields = ["id", "installation_date"]
|
|
350
|
+
fields = ["id", "installation_date", "commissioned_date"]
|
|
338
351
|
|
|
339
352
|
def search(self, queryset, name, value):
|
|
340
353
|
if not value.strip():
|
|
@@ -356,7 +369,7 @@ class InnerductFilterSet(NetBoxModelFilterSet):
|
|
|
356
369
|
|
|
357
370
|
class Meta:
|
|
358
371
|
model = Innerduct
|
|
359
|
-
fields = ["id", "installation_date"]
|
|
372
|
+
fields = ["id", "installation_date", "commissioned_date"]
|
|
360
373
|
|
|
361
374
|
def search(self, queryset, name, value):
|
|
362
375
|
if not value.strip():
|
|
@@ -403,7 +416,7 @@ class ConduitBankFilterSet(TenancyFilterSet, NetBoxModelFilterSet):
|
|
|
403
416
|
|
|
404
417
|
class Meta:
|
|
405
418
|
model = ConduitBank
|
|
406
|
-
fields = ["id", "installation_date"]
|
|
419
|
+
fields = ["id", "installation_date", "commissioned_date"]
|
|
407
420
|
|
|
408
421
|
def search(self, queryset, name, value):
|
|
409
422
|
if not value.strip():
|
|
@@ -456,6 +469,12 @@ class CableSegmentFilterSet(NetBoxModelFilterSet):
|
|
|
456
469
|
distinct=False,
|
|
457
470
|
label="Pathway (ID)",
|
|
458
471
|
)
|
|
472
|
+
lashed_with_id = django_filters.ModelMultipleChoiceFilter(
|
|
473
|
+
field_name="lashed_with",
|
|
474
|
+
queryset=CableSegment.objects.all(),
|
|
475
|
+
distinct=False,
|
|
476
|
+
label="Lashed with segment (ID)",
|
|
477
|
+
)
|
|
459
478
|
sequence = MultiValueNumberFilter()
|
|
460
479
|
|
|
461
480
|
class Meta:
|