Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
626 changes: 26 additions & 600 deletions CHANGELOG.md

Large diffs are not rendered by default.

519 changes: 0 additions & 519 deletions CLAUDE.md

This file was deleted.

2 changes: 1 addition & 1 deletion cesnet_service_path_plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class CesnetServicePathPluginConfig(PluginConfig):
base_url = "cesnet-service-path-plugin"
author = __email__
graphql_schema = "graphql.schema"
min_version = "4.5.0"
min_version = "4.5.4"
max_version = "4.5.99"


Expand Down
18 changes: 10 additions & 8 deletions cesnet_service_path_plugin/api/serializers/contract_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ class ContractInfoSerializer(NetBoxModelSerializer):
queryset=ContractInfo.objects.all(),
required=False,
allow_null=True,
help_text="Link to previous version for amendments/renewals"
help_text="Link to previous version for amendments/renewals",
)
contract_type = serializers.CharField(
required=False,
help_text="Type of contract (auto-set to 'amendment' if previous_version exists, can be set to 'renewal')"
help_text="Type of contract (auto-set to 'amendment' if previous_version exists, can be set to 'renewal')",
)
# Read-only versioning fields
superseded_by = serializers.PrimaryKeyRelatedField(read_only=True)
Expand Down Expand Up @@ -122,11 +122,13 @@ def get_segments_detail(self, obj):
"plugins-api:cesnet_service_path_plugin-api:segment-detail", kwargs={"pk": segment.id}
)

segments_list.append({
"id": segment.id,
"url": segment_url,
"display": str(segment),
"name": segment.name,
})
segments_list.append(
{
"id": segment.id,
"url": segment_url,
"display": str(segment),
"name": segment.name,
}
)

return segments_list
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,44 @@ class DarkFiberSegmentDataSerializer(NetBoxModelSerializer):
# Write-only segment ID for POST/PUT/PATCH operations
segment_id = serializers.PrimaryKeyRelatedField(
queryset=Segment.objects.all(),
source='segment',
source="segment",
write_only=True,
required=True,
help_text="ID of the segment this technical data belongs to"
help_text="ID of the segment this technical data belongs to",
)

class Meta:
model = DarkFiberSegmentData
fields = [
'segment',
'segment_id',
'fiber_mode',
'single_mode_subtype',
'multimode_subtype',
'jacket_type',
'fiber_attenuation_max',
'total_loss',
'total_length',
'number_of_fibers',
'connector_type_side_a',
'connector_type_side_b',
'created',
'last_updated',
"segment",
"segment_id",
"fiber_mode",
"single_mode_subtype",
"multimode_subtype",
"jacket_type",
"fiber_attenuation_max",
"total_loss",
"total_length",
"number_of_fibers",
"connector_type_side_a",
"connector_type_side_b",
"created",
"last_updated",
]

def get_segment(self, obj):
"""Return basic segment info for GET operations"""
request = self.context.get('request')
request = self.context.get("request")
if not request:
return {
'id': obj.segment.id,
'name': obj.segment.name,
"id": obj.segment.id,
"name": obj.segment.name,
}

return {
'id': obj.segment.id,
'name': obj.segment.name,
'url': request.build_absolute_uri(
f'/api/plugins/cesnet-service-path-plugin/segments/{obj.segment.id}/'
)
"id": obj.segment.id,
"name": obj.segment.name,
"url": request.build_absolute_uri(f"/api/plugins/cesnet-service-path-plugin/segments/{obj.segment.id}/"),
}

def validate(self, data):
Expand All @@ -68,15 +66,15 @@ def validate(self, data):
"""
from django.core.exceptions import ValidationError as DjangoValidationError

segment = data.get('segment')
segment = data.get("segment")

# Check if this is an update (instance exists) or create (new instance)
if not self.instance and segment:
# Creating new instance - check if segment already has data
if hasattr(segment, 'dark_fiber_data'):
raise serializers.ValidationError({
'segment': f'Segment "{segment.name}" already has dark fiber technical data.'
})
if hasattr(segment, "dark_fiber_data"):
raise serializers.ValidationError(
{"segment": f'Segment "{segment.name}" already has dark fiber technical data.'}
)

# Trigger model-level validation by creating a temporary instance
# This ensures model's clean() method is called
Expand All @@ -93,6 +91,6 @@ def validate(self, data):
instance.clean()
except DjangoValidationError as e:
# Convert Django ValidationError to DRF ValidationError
raise serializers.ValidationError(e.message_dict if hasattr(e, 'message_dict') else str(e))
raise serializers.ValidationError(e.message_dict if hasattr(e, "message_dict") else str(e))

return data
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,40 @@ class EthernetServiceSegmentDataSerializer(NetBoxModelSerializer):
# Write-only segment ID for POST/PUT/PATCH operations
segment_id = serializers.PrimaryKeyRelatedField(
queryset=Segment.objects.all(),
source='segment',
source="segment",
write_only=True,
required=True,
help_text="ID of the segment this technical data belongs to"
help_text="ID of the segment this technical data belongs to",
)

class Meta:
model = EthernetServiceSegmentData
fields = [
'segment',
'segment_id',
'port_speed',
'vlan_id',
'vlan_tags',
'encapsulation_type',
'interface_type',
'mtu_size',
'created',
'last_updated',
"segment",
"segment_id",
"port_speed",
"vlan_id",
"vlan_tags",
"encapsulation_type",
"interface_type",
"mtu_size",
"created",
"last_updated",
]

def get_segment(self, obj):
"""Return basic segment info for GET operations"""
request = self.context.get('request')
request = self.context.get("request")
if not request:
return {
'id': obj.segment.id,
'name': obj.segment.name,
"id": obj.segment.id,
"name": obj.segment.name,
}

return {
'id': obj.segment.id,
'name': obj.segment.name,
'url': request.build_absolute_uri(
f'/api/plugins/cesnet-service-path-plugin/segments/{obj.segment.id}/'
)
"id": obj.segment.id,
"name": obj.segment.name,
"url": request.build_absolute_uri(f"/api/plugins/cesnet-service-path-plugin/segments/{obj.segment.id}/"),
}

def validate(self, data):
Expand All @@ -64,15 +62,15 @@ def validate(self, data):
"""
from django.core.exceptions import ValidationError as DjangoValidationError

segment = data.get('segment')
segment = data.get("segment")

# Check if this is an update (instance exists) or create (new instance)
if not self.instance and segment:
# Creating new instance - check if segment already has data
if hasattr(segment, 'ethernet_service_data'):
raise serializers.ValidationError({
'segment': f'Segment "{segment.name}" already has ethernet service technical data.'
})
if hasattr(segment, "ethernet_service_data"):
raise serializers.ValidationError(
{"segment": f'Segment "{segment.name}" already has ethernet service technical data.'}
)

# Trigger model-level validation by creating a temporary instance
# This ensures model's clean() method is called
Expand All @@ -89,6 +87,6 @@ def validate(self, data):
instance.clean()
except DjangoValidationError as e:
# Convert Django ValidationError to DRF ValidationError
raise serializers.ValidationError(e.message_dict if hasattr(e, 'message_dict') else str(e))
raise serializers.ValidationError(e.message_dict if hasattr(e, "message_dict") else str(e))

return data
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,40 @@ class OpticalSpectrumSegmentDataSerializer(NetBoxModelSerializer):
# Write-only segment ID for POST/PUT/PATCH operations
segment_id = serializers.PrimaryKeyRelatedField(
queryset=Segment.objects.all(),
source='segment',
source="segment",
write_only=True,
required=True,
help_text="ID of the segment this technical data belongs to"
help_text="ID of the segment this technical data belongs to",
)

class Meta:
model = OpticalSpectrumSegmentData
fields = [
'segment',
'segment_id',
'wavelength',
'spectral_slot_width',
'itu_grid_position',
'chromatic_dispersion',
'pmd_tolerance',
'modulation_format',
'created',
'last_updated',
"segment",
"segment_id",
"wavelength",
"spectral_slot_width",
"itu_grid_position",
"chromatic_dispersion",
"pmd_tolerance",
"modulation_format",
"created",
"last_updated",
]

def get_segment(self, obj):
"""Return basic segment info for GET operations"""
request = self.context.get('request')
request = self.context.get("request")
if not request:
return {
'id': obj.segment.id,
'name': obj.segment.name,
"id": obj.segment.id,
"name": obj.segment.name,
}

return {
'id': obj.segment.id,
'name': obj.segment.name,
'url': request.build_absolute_uri(
f'/api/plugins/cesnet-service-path-plugin/segments/{obj.segment.id}/'
)
"id": obj.segment.id,
"name": obj.segment.name,
"url": request.build_absolute_uri(f"/api/plugins/cesnet-service-path-plugin/segments/{obj.segment.id}/"),
}

def validate(self, data):
Expand All @@ -64,15 +62,15 @@ def validate(self, data):
"""
from django.core.exceptions import ValidationError as DjangoValidationError

segment = data.get('segment')
segment = data.get("segment")

# Check if this is an update (instance exists) or create (new instance)
if not self.instance and segment:
# Creating new instance - check if segment already has data
if hasattr(segment, 'optical_spectrum_data'):
raise serializers.ValidationError({
'segment': f'Segment "{segment.name}" already has optical spectrum technical data.'
})
if hasattr(segment, "optical_spectrum_data"):
raise serializers.ValidationError(
{"segment": f'Segment "{segment.name}" already has optical spectrum technical data.'}
)

# Trigger model-level validation by creating a temporary instance
# This ensures model's clean() method is called
Expand All @@ -89,6 +87,6 @@ def validate(self, data):
instance.clean()
except DjangoValidationError as e:
# Convert Django ValidationError to DRF ValidationError
raise serializers.ValidationError(e.message_dict if hasattr(e, 'message_dict') else str(e))
raise serializers.ValidationError(e.message_dict if hasattr(e, "message_dict") else str(e))

return data
6 changes: 3 additions & 3 deletions cesnet_service_path_plugin/api/serializers/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ def get_type_specific_data(self, obj):
or EthernetServiceSegmentData depending on the segment's type.
"""
try:
if obj.segment_type == 'dark_fiber':
if obj.segment_type == "dark_fiber":
try:
return DarkFiberSegmentDataSerializer(obj.dark_fiber_data, context=self.context).data
except obj.dark_fiber_data.RelatedObjectDoesNotExist:
return None
elif obj.segment_type == 'optical_spectrum':
elif obj.segment_type == "optical_spectrum":
try:
return OpticalSpectrumSegmentDataSerializer(obj.optical_spectrum_data, context=self.context).data
except obj.optical_spectrum_data.RelatedObjectDoesNotExist:
return None
elif obj.segment_type == 'ethernet_service':
elif obj.segment_type == "ethernet_service":
try:
return EthernetServiceSegmentDataSerializer(obj.ethernet_service_data, context=self.context).data
except obj.ethernet_service_data.RelatedObjectDoesNotExist:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ class DarkFiberSegmentDataViewSet(NetBoxModelViewSet):
Provides CRUD operations for dark fiber segment technical specifications.
"""

queryset = DarkFiberSegmentData.objects.select_related('segment').all()
queryset = DarkFiberSegmentData.objects.select_related("segment").all()
serializer_class = DarkFiberSegmentDataSerializer
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ class EthernetServiceSegmentDataViewSet(NetBoxModelViewSet):
Provides CRUD operations for ethernet service segment technical specifications.
"""

queryset = EthernetServiceSegmentData.objects.select_related('segment').all()
queryset = EthernetServiceSegmentData.objects.select_related("segment").all()
serializer_class = EthernetServiceSegmentDataSerializer
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ class OpticalSpectrumSegmentDataViewSet(NetBoxModelViewSet):
Provides CRUD operations for optical spectrum segment technical specifications.
"""

queryset = OpticalSpectrumSegmentData.objects.select_related('segment').all()
queryset = OpticalSpectrumSegmentData.objects.select_related("segment").all()
serializer_class = OpticalSpectrumSegmentDataSerializer
4 changes: 1 addition & 3 deletions cesnet_service_path_plugin/filtersets/contract_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,4 @@ def search(self, queryset, name, value):
contract_number = Q(contract_number__icontains=value)
notes = Q(notes__icontains=value)

return queryset.filter(
contract_number | notes
)
return queryset.filter(contract_number | notes)
7 changes: 3 additions & 4 deletions cesnet_service_path_plugin/forms/contract_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ class ContractInfoForm(NetBoxModelForm):
required=False, min_value=0, help_text="Number of recurring charge periods (0 for no recurring charges)"
)


notes = forms.CharField(
required=False, widget=forms.Textarea(attrs={"rows": 3}), help_text="Notes specific to this version"
)
Expand Down Expand Up @@ -89,9 +88,9 @@ def __init__(self, *args, **kwargs):
# For amendments, make currency field disabled in the UI
# Note: disabled fields don't submit values, but we handle this in clean_charge_currency()
self.fields["charge_currency"].disabled = True
self.fields["charge_currency"].help_text = (
"Currency cannot be changed in amendments (inherited from original contract)"
)
self.fields[
"charge_currency"
].help_text = "Currency cannot be changed in amendments (inherited from original contract)"

def clean_charge_currency(self):
"""
Expand Down
Loading
Loading