netbox-pathways 0.1.0__py3-none-any.whl

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.
Files changed (93) hide show
  1. netbox_pathways/__init__.py +91 -0
  2. netbox_pathways/api/__init__.py +0 -0
  3. netbox_pathways/api/external_geo.py +134 -0
  4. netbox_pathways/api/geo.py +360 -0
  5. netbox_pathways/api/serializers.py +520 -0
  6. netbox_pathways/api/traversal.py +48 -0
  7. netbox_pathways/api/urls.py +46 -0
  8. netbox_pathways/api/views.py +140 -0
  9. netbox_pathways/choices.py +122 -0
  10. netbox_pathways/filterforms.py +381 -0
  11. netbox_pathways/filters.py +622 -0
  12. netbox_pathways/forms.py +1020 -0
  13. netbox_pathways/geo.py +79 -0
  14. netbox_pathways/graph.py +545 -0
  15. netbox_pathways/management/__init__.py +0 -0
  16. netbox_pathways/management/commands/__init__.py +0 -0
  17. netbox_pathways/management/commands/_geodata_worker.py +54 -0
  18. netbox_pathways/management/commands/generate_qgis_project.py +141 -0
  19. netbox_pathways/management/commands/generate_sample_data.py +550 -0
  20. netbox_pathways/management/commands/import_geodata.py +705 -0
  21. netbox_pathways/migrations/0001_initial.py +291 -0
  22. netbox_pathways/migrations/0002_replace_owner_with_tenant.py +54 -0
  23. netbox_pathways/migrations/0003_structure_optional_site_dimensions.py +40 -0
  24. netbox_pathways/migrations/0004_circuit_geometry.py +40 -0
  25. netbox_pathways/migrations/0005_replace_unique_together_with_constraints.py +39 -0
  26. netbox_pathways/migrations/0006_remove_cablesegment_sequence_enter_exit.py +33 -0
  27. netbox_pathways/migrations/0007_cable_routing_redesign.py +36 -0
  28. netbox_pathways/migrations/0008_conduitbank_pathway_subclass.py +108 -0
  29. netbox_pathways/migrations/0009_remove_conduit_unique_position_per_bank_and_more.py +21 -0
  30. netbox_pathways/migrations/0010_structure_status.py +18 -0
  31. netbox_pathways/migrations/0011_rename_name_to_label.py +42 -0
  32. netbox_pathways/migrations/0012_add_filter_field_indexes.py +28 -0
  33. netbox_pathways/migrations/0013_plannedroute.py +45 -0
  34. netbox_pathways/migrations/0014_plannedroute_parent_split.py +19 -0
  35. netbox_pathways/migrations/__init__.py +1 -0
  36. netbox_pathways/models.py +879 -0
  37. netbox_pathways/navigation.py +207 -0
  38. netbox_pathways/registry.py +195 -0
  39. netbox_pathways/route_engine.py +255 -0
  40. netbox_pathways/routing.py +102 -0
  41. netbox_pathways/search.py +126 -0
  42. netbox_pathways/signals.py +24 -0
  43. netbox_pathways/static/netbox_pathways/css/leaflet-theme.css +375 -0
  44. netbox_pathways/static/netbox_pathways/css/pathways-map.css +68 -0
  45. netbox_pathways/static/netbox_pathways/qgis/pathways.qml +34 -0
  46. netbox_pathways/static/netbox_pathways/qgis/structures.qml +38 -0
  47. netbox_pathways/static/netbox_pathways/vendor/MarkerCluster.Default.css +60 -0
  48. netbox_pathways/static/netbox_pathways/vendor/MarkerCluster.css +14 -0
  49. netbox_pathways/static/netbox_pathways/vendor/leaflet.markercluster.js +2 -0
  50. netbox_pathways/tables.py +459 -0
  51. netbox_pathways/template_content.py +337 -0
  52. netbox_pathways/templates/netbox_pathways/aerialspan.html +1 -0
  53. netbox_pathways/templates/netbox_pathways/buttons/apply_route.html +5 -0
  54. netbox_pathways/templates/netbox_pathways/buttons/replan_route.html +5 -0
  55. netbox_pathways/templates/netbox_pathways/buttons/revert_split.html +9 -0
  56. netbox_pathways/templates/netbox_pathways/buttons/split_route.html +5 -0
  57. netbox_pathways/templates/netbox_pathways/buttons/view_in_map.html +5 -0
  58. netbox_pathways/templates/netbox_pathways/cable_route_tab.html +46 -0
  59. netbox_pathways/templates/netbox_pathways/cablesegment.html +32 -0
  60. netbox_pathways/templates/netbox_pathways/conduit.html +1 -0
  61. netbox_pathways/templates/netbox_pathways/conduitbank.html +1 -0
  62. netbox_pathways/templates/netbox_pathways/conduitjunction.html +1 -0
  63. netbox_pathways/templates/netbox_pathways/directburied.html +1 -0
  64. netbox_pathways/templates/netbox_pathways/inc/cable_add_segment_form.html +23 -0
  65. netbox_pathways/templates/netbox_pathways/inc/cable_route_finder_results.html +47 -0
  66. netbox_pathways/templates/netbox_pathways/inc/cable_routing_panel.html +40 -0
  67. netbox_pathways/templates/netbox_pathways/inc/cable_segment_table.html +97 -0
  68. netbox_pathways/templates/netbox_pathways/inc/connected_structures_panel.html +9 -0
  69. netbox_pathways/templates/netbox_pathways/inc/constraint_card.html +22 -0
  70. netbox_pathways/templates/netbox_pathways/inc/geo_map_panel.html +53 -0
  71. netbox_pathways/templates/netbox_pathways/inc/plannedroute_map_panel.html +31 -0
  72. netbox_pathways/templates/netbox_pathways/inc/planner_results.html +80 -0
  73. netbox_pathways/templates/netbox_pathways/innerduct.html +1 -0
  74. netbox_pathways/templates/netbox_pathways/map.html +518 -0
  75. netbox_pathways/templates/netbox_pathways/pathway.html +1 -0
  76. netbox_pathways/templates/netbox_pathways/pathwaylocation.html +1 -0
  77. netbox_pathways/templates/netbox_pathways/plannedroute.html +110 -0
  78. netbox_pathways/templates/netbox_pathways/plannedroute_apply.html +56 -0
  79. netbox_pathways/templates/netbox_pathways/plannedroute_split.html +53 -0
  80. netbox_pathways/templates/netbox_pathways/pullsheet_detail.html +134 -0
  81. netbox_pathways/templates/netbox_pathways/pullsheet_list.html +15 -0
  82. netbox_pathways/templates/netbox_pathways/route_planner.html +713 -0
  83. netbox_pathways/templates/netbox_pathways/sitegeometry.html +1 -0
  84. netbox_pathways/templates/netbox_pathways/structure.html +13 -0
  85. netbox_pathways/templates/netbox_pathways/widgets/map_widget.html +10 -0
  86. netbox_pathways/ui/__init__.py +0 -0
  87. netbox_pathways/ui/panels.py +148 -0
  88. netbox_pathways/urls.py +243 -0
  89. netbox_pathways/views.py +2264 -0
  90. netbox_pathways-0.1.0.dist-info/METADATA +145 -0
  91. netbox_pathways-0.1.0.dist-info/RECORD +93 -0
  92. netbox_pathways-0.1.0.dist-info/WHEEL +5 -0
  93. netbox_pathways-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,520 @@
1
+ from circuits.api.serializers_.circuits import CircuitSerializer
2
+ from dcim.api.serializers_.cables import CableSerializer
3
+ from dcim.api.serializers_.sites import LocationSerializer, SiteSerializer
4
+ from netbox.api.fields import ChoiceField
5
+ from netbox.api.serializers import NetBoxModelSerializer
6
+ from rest_framework import serializers as drf_serializers
7
+ from tenancy.api.serializers_.tenants import TenantSerializer
8
+
9
+ from ..choices import (
10
+ AerialTypeChoices,
11
+ BankFaceChoices,
12
+ ConduitBankConfigChoices,
13
+ ConduitMaterialChoices,
14
+ EncasementTypeChoices,
15
+ PathwayTypeChoices,
16
+ PlannedRouteStatusChoices,
17
+ StructureStatusChoices,
18
+ StructureTypeChoices,
19
+ )
20
+ from ..models import (
21
+ AerialSpan,
22
+ CableSegment,
23
+ CircuitGeometry,
24
+ Conduit,
25
+ ConduitBank,
26
+ ConduitJunction,
27
+ DirectBuried,
28
+ Innerduct,
29
+ Pathway,
30
+ PathwayLocation,
31
+ PlannedRoute,
32
+ SiteGeometry,
33
+ Structure,
34
+ )
35
+
36
+
37
+ class StructureSerializer(NetBoxModelSerializer):
38
+ url = drf_serializers.HyperlinkedIdentityField(
39
+ view_name="plugins-api:netbox_pathways-api:structure-detail",
40
+ )
41
+ status = ChoiceField(choices=StructureStatusChoices, required=False)
42
+ structure_type = ChoiceField(choices=StructureTypeChoices, required=False, allow_blank=True)
43
+ site = SiteSerializer(nested=True, required=False, allow_null=True)
44
+ tenant = TenantSerializer(nested=True, required=False, allow_null=True)
45
+ no_pathways = drf_serializers.SerializerMethodField(read_only=True)
46
+ description = drf_serializers.SerializerMethodField(read_only=True)
47
+
48
+ class Meta:
49
+ model = Structure
50
+ fields = [
51
+ "id",
52
+ "url",
53
+ "display_url",
54
+ "display",
55
+ "name",
56
+ "status",
57
+ "structure_type",
58
+ "site",
59
+ "location",
60
+ "height",
61
+ "width",
62
+ "length",
63
+ "depth",
64
+ "elevation",
65
+ "installation_date",
66
+ "tenant",
67
+ "access_notes",
68
+ "comments",
69
+ "tags",
70
+ "created",
71
+ "last_updated",
72
+ "no_pathways",
73
+ "description",
74
+ ]
75
+ brief_fields = (
76
+ "id",
77
+ "url",
78
+ "display_url",
79
+ "display",
80
+ "label",
81
+ "structure_type",
82
+ "no_pathways",
83
+ "description",
84
+ )
85
+
86
+ def get_no_pathways(self, obj):
87
+ if hasattr(obj, "_has_pathways"):
88
+ return not obj._has_pathways
89
+ return None
90
+
91
+ def get_description(self, obj):
92
+ if hasattr(obj, "_has_pathways") and not obj._has_pathways:
93
+ return "No connected pathways"
94
+ return None
95
+
96
+
97
+ # --- Shared FK field declarations for pathway subtypes ---
98
+
99
+
100
+ def _pathway_fk_fields():
101
+ """Common FK field declarations for all pathway-based serializers."""
102
+ return {
103
+ "start_structure": StructureSerializer(nested=True, required=False, allow_null=True),
104
+ "end_structure": StructureSerializer(nested=True, required=False, allow_null=True),
105
+ "start_location": LocationSerializer(nested=True, required=False, allow_null=True),
106
+ "end_location": LocationSerializer(nested=True, required=False, allow_null=True),
107
+ "tenant": TenantSerializer(nested=True, required=False, allow_null=True),
108
+ }
109
+
110
+
111
+ class ConduitBankSerializer(NetBoxModelSerializer):
112
+ url = drf_serializers.HyperlinkedIdentityField(
113
+ view_name="plugins-api:netbox_pathways-api:conduitbank-detail",
114
+ )
115
+ start_structure = StructureSerializer(nested=True, required=False, allow_null=True)
116
+ end_structure = StructureSerializer(nested=True, required=False, allow_null=True)
117
+ start_face = ChoiceField(choices=BankFaceChoices, required=False, allow_blank=True)
118
+ end_face = ChoiceField(choices=BankFaceChoices, required=False, allow_blank=True)
119
+ configuration = ChoiceField(choices=ConduitBankConfigChoices, required=False, allow_blank=True)
120
+ encasement_type = ChoiceField(choices=EncasementTypeChoices, required=False, allow_blank=True)
121
+ tenant = TenantSerializer(nested=True, required=False, allow_null=True)
122
+
123
+ class Meta:
124
+ model = ConduitBank
125
+ fields = [
126
+ "id",
127
+ "url",
128
+ "display_url",
129
+ "display",
130
+ "label",
131
+ "start_structure",
132
+ "end_structure",
133
+ "start_face",
134
+ "end_face",
135
+ "tenant",
136
+ "path",
137
+ "length",
138
+ "configuration",
139
+ "total_conduits",
140
+ "encasement_type",
141
+ "installation_date",
142
+ "comments",
143
+ "tags",
144
+ "created",
145
+ "last_updated",
146
+ ]
147
+ brief_fields = ("id", "url", "display_url", "display", "label")
148
+
149
+
150
+ class PathwaySerializer(NetBoxModelSerializer):
151
+ url = drf_serializers.HyperlinkedIdentityField(
152
+ view_name="plugins-api:netbox_pathways-api:pathway-detail",
153
+ )
154
+ pathway_type = ChoiceField(choices=PathwayTypeChoices, required=False, allow_blank=True)
155
+ start_structure = StructureSerializer(nested=True, required=False, allow_null=True)
156
+ end_structure = StructureSerializer(nested=True, required=False, allow_null=True)
157
+ start_location = LocationSerializer(nested=True, required=False, allow_null=True)
158
+ end_location = LocationSerializer(nested=True, required=False, allow_null=True)
159
+ tenant = TenantSerializer(nested=True, required=False, allow_null=True)
160
+ cables_routed = drf_serializers.IntegerField(read_only=True)
161
+
162
+ class Meta:
163
+ model = Pathway
164
+ fields = [
165
+ "id",
166
+ "url",
167
+ "display_url",
168
+ "display",
169
+ "label",
170
+ "pathway_type",
171
+ "path",
172
+ "start_structure",
173
+ "end_structure",
174
+ "start_location",
175
+ "end_location",
176
+ "tenant",
177
+ "length",
178
+ "cables_routed",
179
+ "installation_date",
180
+ "comments",
181
+ "tags",
182
+ "created",
183
+ "last_updated",
184
+ ]
185
+ brief_fields = ("id", "url", "display_url", "display", "label", "pathway_type")
186
+
187
+
188
+ class ConduitSerializer(NetBoxModelSerializer):
189
+ url = drf_serializers.HyperlinkedIdentityField(
190
+ view_name="plugins-api:netbox_pathways-api:conduit-detail",
191
+ )
192
+ material = ChoiceField(choices=ConduitMaterialChoices, required=False, allow_blank=True)
193
+ start_structure = StructureSerializer(nested=True, required=False, allow_null=True)
194
+ end_structure = StructureSerializer(nested=True, required=False, allow_null=True)
195
+ start_location = LocationSerializer(nested=True, required=False, allow_null=True)
196
+ end_location = LocationSerializer(nested=True, required=False, allow_null=True)
197
+ conduit_bank = ConduitBankSerializer(nested=True, required=False, allow_null=True)
198
+ tenant = TenantSerializer(nested=True, required=False, allow_null=True)
199
+ cables_routed = drf_serializers.IntegerField(read_only=True)
200
+
201
+ class Meta:
202
+ model = Conduit
203
+ fields = [
204
+ "id",
205
+ "url",
206
+ "display_url",
207
+ "display",
208
+ "label",
209
+ "pathway_type",
210
+ "path",
211
+ "start_structure",
212
+ "end_structure",
213
+ "start_location",
214
+ "end_location",
215
+ "material",
216
+ "inner_diameter",
217
+ "outer_diameter",
218
+ "depth",
219
+ "conduit_bank",
220
+ "bank_position",
221
+ "start_junction",
222
+ "end_junction",
223
+ "length",
224
+ "cables_routed",
225
+ "installation_date",
226
+ "comments",
227
+ "tags",
228
+ "created",
229
+ "last_updated",
230
+ ]
231
+ brief_fields = ("id", "url", "display_url", "display", "label", "material")
232
+
233
+
234
+ class AerialSpanSerializer(NetBoxModelSerializer):
235
+ url = drf_serializers.HyperlinkedIdentityField(
236
+ view_name="plugins-api:netbox_pathways-api:aerialspan-detail",
237
+ )
238
+ aerial_type = ChoiceField(choices=AerialTypeChoices, required=False, allow_blank=True)
239
+ start_structure = StructureSerializer(nested=True, required=False, allow_null=True)
240
+ end_structure = StructureSerializer(nested=True, required=False, allow_null=True)
241
+ start_location = LocationSerializer(nested=True, required=False, allow_null=True)
242
+ end_location = LocationSerializer(nested=True, required=False, allow_null=True)
243
+ tenant = TenantSerializer(nested=True, required=False, allow_null=True)
244
+ cables_routed = drf_serializers.IntegerField(read_only=True)
245
+
246
+ class Meta:
247
+ model = AerialSpan
248
+ fields = [
249
+ "id",
250
+ "url",
251
+ "display_url",
252
+ "display",
253
+ "label",
254
+ "pathway_type",
255
+ "path",
256
+ "start_structure",
257
+ "end_structure",
258
+ "start_location",
259
+ "end_location",
260
+ "aerial_type",
261
+ "attachment_height",
262
+ "sag",
263
+ "messenger_size",
264
+ "wind_loading",
265
+ "ice_loading",
266
+ "length",
267
+ "cables_routed",
268
+ "installation_date",
269
+ "comments",
270
+ "tags",
271
+ "created",
272
+ "last_updated",
273
+ ]
274
+ brief_fields = ("id", "url", "display_url", "display", "label", "aerial_type")
275
+
276
+
277
+ class DirectBuriedSerializer(NetBoxModelSerializer):
278
+ url = drf_serializers.HyperlinkedIdentityField(
279
+ view_name="plugins-api:netbox_pathways-api:directburied-detail",
280
+ )
281
+ start_structure = StructureSerializer(nested=True, required=False, allow_null=True)
282
+ end_structure = StructureSerializer(nested=True, required=False, allow_null=True)
283
+ start_location = LocationSerializer(nested=True, required=False, allow_null=True)
284
+ end_location = LocationSerializer(nested=True, required=False, allow_null=True)
285
+ tenant = TenantSerializer(nested=True, required=False, allow_null=True)
286
+ cables_routed = drf_serializers.IntegerField(read_only=True)
287
+
288
+ class Meta:
289
+ model = DirectBuried
290
+ fields = [
291
+ "id",
292
+ "url",
293
+ "display_url",
294
+ "display",
295
+ "label",
296
+ "pathway_type",
297
+ "path",
298
+ "start_structure",
299
+ "end_structure",
300
+ "start_location",
301
+ "end_location",
302
+ "burial_depth",
303
+ "warning_tape",
304
+ "tracer_wire",
305
+ "armor_type",
306
+ "length",
307
+ "cables_routed",
308
+ "installation_date",
309
+ "comments",
310
+ "tags",
311
+ "created",
312
+ "last_updated",
313
+ ]
314
+ brief_fields = ("id", "url", "display_url", "display", "label")
315
+
316
+
317
+ class InnerductSerializer(NetBoxModelSerializer):
318
+ url = drf_serializers.HyperlinkedIdentityField(
319
+ view_name="plugins-api:netbox_pathways-api:innerduct-detail",
320
+ )
321
+ start_structure = StructureSerializer(nested=True, required=False, allow_null=True)
322
+ end_structure = StructureSerializer(nested=True, required=False, allow_null=True)
323
+ start_location = LocationSerializer(nested=True, required=False, allow_null=True)
324
+ end_location = LocationSerializer(nested=True, required=False, allow_null=True)
325
+ parent_conduit = ConduitSerializer(nested=True, required=False, allow_null=True)
326
+ cables_routed = drf_serializers.IntegerField(read_only=True)
327
+
328
+ class Meta:
329
+ model = Innerduct
330
+ fields = [
331
+ "id",
332
+ "url",
333
+ "display_url",
334
+ "display",
335
+ "label",
336
+ "pathway_type",
337
+ "path",
338
+ "start_structure",
339
+ "end_structure",
340
+ "start_location",
341
+ "end_location",
342
+ "parent_conduit",
343
+ "size",
344
+ "color",
345
+ "position",
346
+ "length",
347
+ "cables_routed",
348
+ "installation_date",
349
+ "comments",
350
+ "tags",
351
+ "created",
352
+ "last_updated",
353
+ ]
354
+ brief_fields = ("id", "url", "display_url", "display", "label", "size")
355
+
356
+
357
+ class ConduitJunctionSerializer(NetBoxModelSerializer):
358
+ url = drf_serializers.HyperlinkedIdentityField(
359
+ view_name="plugins-api:netbox_pathways-api:conduitjunction-detail",
360
+ )
361
+ trunk_conduit = ConduitSerializer(nested=True, required=False, allow_null=True)
362
+ branch_conduit = ConduitSerializer(nested=True, required=False, allow_null=True)
363
+ towards_structure = StructureSerializer(nested=True, required=False, allow_null=True)
364
+
365
+ class Meta:
366
+ model = ConduitJunction
367
+ fields = [
368
+ "id",
369
+ "url",
370
+ "display_url",
371
+ "display",
372
+ "label",
373
+ "trunk_conduit",
374
+ "branch_conduit",
375
+ "towards_structure",
376
+ "position_on_trunk",
377
+ "comments",
378
+ "tags",
379
+ "created",
380
+ "last_updated",
381
+ ]
382
+ brief_fields = ("id", "url", "display_url", "display", "label")
383
+
384
+
385
+ class PathwayLocationSerializer(NetBoxModelSerializer):
386
+ url = drf_serializers.HyperlinkedIdentityField(
387
+ view_name="plugins-api:netbox_pathways-api:pathwaylocation-detail",
388
+ )
389
+ pathway = PathwaySerializer(nested=True, required=False, allow_null=True)
390
+ site = SiteSerializer(nested=True, required=False, allow_null=True)
391
+ location = LocationSerializer(nested=True, required=False, allow_null=True)
392
+
393
+ class Meta:
394
+ model = PathwayLocation
395
+ fields = [
396
+ "id",
397
+ "url",
398
+ "display_url",
399
+ "display",
400
+ "pathway",
401
+ "site",
402
+ "location",
403
+ "sequence",
404
+ "comments",
405
+ "tags",
406
+ "created",
407
+ "last_updated",
408
+ ]
409
+ brief_fields = ("id", "url", "display_url", "display", "pathway", "sequence")
410
+
411
+
412
+ class CableSegmentSerializer(NetBoxModelSerializer):
413
+ url = drf_serializers.HyperlinkedIdentityField(
414
+ view_name="plugins-api:netbox_pathways-api:cablesegment-detail",
415
+ )
416
+ cable = CableSerializer(nested=True, required=False, allow_null=True)
417
+ pathway = PathwaySerializer(nested=True, required=False, allow_null=True)
418
+
419
+ class Meta:
420
+ model = CableSegment
421
+ fields = [
422
+ "id",
423
+ "url",
424
+ "display_url",
425
+ "display",
426
+ "cable",
427
+ "pathway",
428
+ "sequence",
429
+ "comments",
430
+ "tags",
431
+ "created",
432
+ "last_updated",
433
+ ]
434
+ brief_fields = ("id", "url", "display_url", "display", "cable", "pathway", "sequence")
435
+
436
+
437
+ class SiteGeometrySerializer(NetBoxModelSerializer):
438
+ url = drf_serializers.HyperlinkedIdentityField(
439
+ view_name="plugins-api:netbox_pathways-api:sitegeometry-detail",
440
+ )
441
+ site = SiteSerializer(nested=True, required=False, allow_null=True)
442
+ structure = StructureSerializer(nested=True, required=False, allow_null=True)
443
+
444
+ class Meta:
445
+ model = SiteGeometry
446
+ fields = [
447
+ "id",
448
+ "url",
449
+ "display_url",
450
+ "display",
451
+ "site",
452
+ "structure",
453
+ "geometry",
454
+ "comments",
455
+ "tags",
456
+ "created",
457
+ "last_updated",
458
+ ]
459
+ brief_fields = ("id", "url", "display_url", "display", "site")
460
+
461
+
462
+ class CircuitGeometrySerializer(NetBoxModelSerializer):
463
+ url = drf_serializers.HyperlinkedIdentityField(
464
+ view_name="plugins-api:netbox_pathways-api:circuitgeometry-detail",
465
+ )
466
+ circuit = CircuitSerializer(nested=True, required=False, allow_null=True)
467
+
468
+ class Meta:
469
+ model = CircuitGeometry
470
+ fields = [
471
+ "id",
472
+ "url",
473
+ "display_url",
474
+ "display",
475
+ "circuit",
476
+ "path",
477
+ "provider_reference",
478
+ "comments",
479
+ "tags",
480
+ "created",
481
+ "last_updated",
482
+ ]
483
+ brief_fields = ("id", "url", "display_url", "display", "circuit")
484
+
485
+
486
+ class PlannedRouteSerializer(NetBoxModelSerializer):
487
+ url = drf_serializers.HyperlinkedIdentityField(
488
+ view_name="plugins-api:netbox_pathways-api:plannedroute-detail",
489
+ )
490
+ status = ChoiceField(choices=PlannedRouteStatusChoices, required=False)
491
+ start_structure = StructureSerializer(nested=True, required=False, allow_null=True)
492
+ end_structure = StructureSerializer(nested=True, required=False, allow_null=True)
493
+ start_location = LocationSerializer(nested=True, required=False, allow_null=True)
494
+ end_location = LocationSerializer(nested=True, required=False, allow_null=True)
495
+ tenant = TenantSerializer(nested=True, required=False, allow_null=True)
496
+ cable = CableSerializer(nested=True, required=False, allow_null=True)
497
+
498
+ class Meta:
499
+ model = PlannedRoute
500
+ fields = [
501
+ "id",
502
+ "url",
503
+ "display_url",
504
+ "display",
505
+ "name",
506
+ "status",
507
+ "start_structure",
508
+ "end_structure",
509
+ "start_location",
510
+ "end_location",
511
+ "pathway_ids",
512
+ "constraints",
513
+ "tenant",
514
+ "cable",
515
+ "comments",
516
+ "tags",
517
+ "created",
518
+ "last_updated",
519
+ ]
520
+ brief_fields = ("id", "url", "display_url", "display", "name", "status")
@@ -0,0 +1,48 @@
1
+ """
2
+ REST API endpoints for graph traversal operations.
3
+ """
4
+
5
+ from rest_framework import status
6
+ from rest_framework.permissions import IsAuthenticated
7
+ from rest_framework.response import Response
8
+ from rest_framework.views import APIView
9
+
10
+ from ..graph import trace_cable
11
+
12
+
13
+ class CableTraceView(APIView):
14
+ """
15
+ Trace a cable's physical route through pathways.
16
+
17
+ GET /api/plugins/netbox-pathways/traversal/cable-trace/?cable_id=<id>
18
+ """
19
+
20
+ permission_classes = [IsAuthenticated]
21
+
22
+ def get(self, request):
23
+ cable_id = request.query_params.get("cable_id")
24
+ if not cable_id:
25
+ return Response(
26
+ {"error": "cable_id is required"},
27
+ status=status.HTTP_400_BAD_REQUEST,
28
+ )
29
+
30
+ try:
31
+ cable_id = int(cable_id)
32
+ except (TypeError, ValueError):
33
+ return Response(
34
+ {"error": "cable_id must be an integer"},
35
+ status=status.HTTP_400_BAD_REQUEST,
36
+ )
37
+
38
+ segments = trace_cable(cable_id)
39
+ total_length = sum(s["length"] or 0 for s in segments)
40
+
41
+ return Response(
42
+ {
43
+ "cable_id": cable_id,
44
+ "segment_count": len(segments),
45
+ "total_length": total_length,
46
+ "segments": segments,
47
+ }
48
+ )
@@ -0,0 +1,46 @@
1
+ from django.urls import path
2
+ from netbox.api.routers import NetBoxRouter
3
+
4
+ from . import views
5
+ from .external_geo import ExternalLayerGeoView
6
+ from .geo import (
7
+ AerialSpanGeoViewSet,
8
+ CircuitGeoViewSet,
9
+ ConduitBankGeoViewSet,
10
+ ConduitGeoViewSet,
11
+ DirectBuriedGeoViewSet,
12
+ PathwayGeoViewSet,
13
+ StructureGeoViewSet,
14
+ )
15
+ from .traversal import CableTraceView
16
+
17
+ router = NetBoxRouter()
18
+ router.register("structures", views.StructureViewSet)
19
+ router.register("conduit-banks", views.ConduitBankViewSet)
20
+ router.register("pathways", views.PathwayViewSet)
21
+ router.register("conduits", views.ConduitViewSet)
22
+ router.register("aerial-spans", views.AerialSpanViewSet)
23
+ router.register("direct-buried", views.DirectBuriedViewSet)
24
+ router.register("innerducts", views.InnerductViewSet)
25
+ router.register("junctions", views.ConduitJunctionViewSet)
26
+ router.register("pathway-locations", views.PathwayLocationViewSet)
27
+ router.register("cable-segments", views.CableSegmentViewSet)
28
+ router.register("site-geometries", views.SiteGeometryViewSet)
29
+ router.register("circuit-geometries", views.CircuitGeometryViewSet)
30
+ router.register("planned-routes", views.PlannedRouteViewSet)
31
+
32
+ # GeoJSON endpoints for QGIS / GIS client consumption
33
+ router.register("geo/structures", StructureGeoViewSet, basename="geo-structure")
34
+ router.register("geo/pathways", PathwayGeoViewSet, basename="geo-pathway")
35
+ router.register("geo/conduit-banks", ConduitBankGeoViewSet, basename="geo-conduitbank")
36
+ router.register("geo/conduits", ConduitGeoViewSet, basename="geo-conduit")
37
+ router.register("geo/aerial-spans", AerialSpanGeoViewSet, basename="geo-aerialspan")
38
+ router.register("geo/direct-buried", DirectBuriedGeoViewSet, basename="geo-directburied")
39
+ router.register("geo/circuits", CircuitGeoViewSet, basename="geo-circuit")
40
+
41
+ urlpatterns = router.urls + [
42
+ # External plugin map layer endpoint
43
+ path("geo/external/<str:layer_name>/", ExternalLayerGeoView.as_view(), name="external-geo"),
44
+ # Graph traversal endpoints
45
+ path("traversal/cable-trace/", CableTraceView.as_view(), name="traversal-cable-trace"),
46
+ ]