211 lines
7.7 KiB
Python
211 lines
7.7 KiB
Python
from rest_framework import serializers
|
|
from authentication.models import KnownIdentity, ToolshedUser, FriendRequestIncoming
|
|
from authentication.serializers import OwnerSerializer
|
|
from files.models import File
|
|
from files.serializers import FileSerializer
|
|
from toolshed.models import Category, Property, ItemProperty, InventoryItem, Tag, StorageLocation, WorkflowInstance
|
|
|
|
|
|
def parse_handle(handle):
|
|
"""Parse a fully qualified handle like 'git:base#property:length' into (origin, entity_type, name)"""
|
|
if '#' not in handle:
|
|
# Fallback to old format (just name)
|
|
return None, None, handle
|
|
origin, rest = handle.split('#', 1)
|
|
if ':' not in rest:
|
|
return origin, None, rest
|
|
entity_type, name = rest.split(':', 1)
|
|
return origin, entity_type, name
|
|
|
|
|
|
def resolve_category_handle(handle):
|
|
"""Resolve a fully qualified handle to a Category object"""
|
|
origin, entity_type, name = parse_handle(handle)
|
|
if origin and entity_type == 'category':
|
|
return Category.objects.get(origin=origin, name=name)
|
|
# Fallback to name-only lookup
|
|
return Category.objects.get(name=handle.split('/')[-1])
|
|
|
|
|
|
def resolve_property_handle(handle):
|
|
"""Resolve a fully qualified handle to a Property object"""
|
|
origin, entity_type, name = parse_handle(handle)
|
|
if origin and entity_type == 'property':
|
|
return Property.objects.get(origin=origin, name=name)
|
|
# Fallback to name-only lookup
|
|
return Property.objects.get(name=handle)
|
|
|
|
|
|
def resolve_tag_handle(handle):
|
|
"""Resolve a fully qualified handle to a Tag object"""
|
|
origin, entity_type, name = parse_handle(handle)
|
|
if origin and entity_type == 'tag':
|
|
return Tag.objects.get(origin=origin, name=name)
|
|
# Fallback to name-only lookup
|
|
return Tag.objects.get(name=handle)
|
|
|
|
|
|
|
|
class FriendSerializer(serializers.ModelSerializer):
|
|
username = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = KnownIdentity
|
|
fields = ['id', 'username', 'public_key']
|
|
|
|
def get_username(self, obj):
|
|
return obj.username + '@' + obj.domain
|
|
|
|
|
|
class FriendRequestSerializer(serializers.ModelSerializer):
|
|
befriender = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = FriendRequestIncoming
|
|
fields = ['befriender', 'befriender_public_key', 'secret', 'id']
|
|
|
|
def get_befriender(self, obj):
|
|
return obj.befriender_username + '@' + obj.befriender_domain
|
|
|
|
|
|
class PropertySerializer(serializers.ModelSerializer):
|
|
category = serializers.SerializerMethodField()
|
|
handle = serializers.SerializerMethodField()
|
|
|
|
def get_category(self, obj):
|
|
return resolve_category_handle(obj.category.get_handle()) if obj.category else None
|
|
|
|
def get_handle(self, obj):
|
|
return obj.get_handle()
|
|
|
|
class Meta:
|
|
model = Property
|
|
fields = ['name', 'description', 'category', 'unit_symbol', 'unit_name', 'unit_name_plural', 'base2_prefix', 'handle']
|
|
|
|
|
|
class CategorySerializer(serializers.ModelSerializer):
|
|
handle = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Category
|
|
fields = ['name', 'handle']
|
|
|
|
def get_handle(self, obj):
|
|
return obj.get_handle()
|
|
|
|
def to_representation(self, instance):
|
|
return instance.get_handle()
|
|
|
|
def to_internal_value(self, data):
|
|
return resolve_category_handle(data.split("/")[-1])
|
|
|
|
|
|
class StorageLocationSerializer(serializers.ModelSerializer):
|
|
owner = OwnerSerializer(read_only=True)
|
|
category = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
|
path = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = StorageLocation
|
|
fields = ['id', 'name', 'description', 'path', 'category', 'owner', 'parent']
|
|
read_only_fields = ['path']
|
|
|
|
@staticmethod
|
|
def get_path(obj):
|
|
if obj.parent:
|
|
return StorageLocationSerializer.get_path(obj.parent) + "/" + obj.name
|
|
return obj.name
|
|
|
|
|
|
class ItemPropertySerializer(serializers.ModelSerializer):
|
|
property = PropertySerializer(read_only=True)
|
|
handle = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = ItemProperty
|
|
fields = ['property', 'value', 'handle']
|
|
|
|
def get_handle(self, obj):
|
|
return obj.property.get_handle()
|
|
|
|
def to_representation(self, instance):
|
|
return {'value': instance.value, 'name': instance.property.name, 'handle': instance.property.get_handle()}
|
|
|
|
def to_internal_value(self, data):
|
|
prop = resolve_property_handle(data.get('name') or data.get('handle'))
|
|
value = data['value']
|
|
return {'property': prop, 'value': value}
|
|
|
|
|
|
class InventoryItemSerializer(serializers.ModelSerializer):
|
|
owner = OwnerSerializer(read_only=True)
|
|
tags = serializers.SerializerMethodField()
|
|
tags_input = serializers.ListField(child=serializers.CharField(), write_only=True, required=False)
|
|
properties = ItemPropertySerializer(many=True, required=False, source='itemproperty_set')
|
|
category = CategorySerializer(required=False, allow_null=True)
|
|
files = FileSerializer(many=True, read_only=True)
|
|
|
|
class Meta:
|
|
model = InventoryItem
|
|
fields = ['id', 'name', 'description', 'owner', 'category', 'availability_policy', 'owned_quantity', 'owner',
|
|
'tags', 'tags_input', 'properties', 'files', 'storage_location']
|
|
|
|
def get_tags(self, obj):
|
|
return [tag.get_handle() for tag in obj.tags.all()]
|
|
|
|
def to_internal_value(self, data):
|
|
files = data.pop('files', [])
|
|
tags_input = data.pop('tags_input', data.pop('tags', []))
|
|
ret = super().to_internal_value(data)
|
|
ret['tags'] = [resolve_tag_handle(tag) for tag in tags_input]
|
|
ret['files'] = files
|
|
return ret
|
|
|
|
def create(self, validated_data):
|
|
tags = validated_data.pop('tags', [])
|
|
props = validated_data.pop('itemproperty_set', [])
|
|
files = validated_data.pop('files', [])
|
|
item = InventoryItem.objects.create(**validated_data)
|
|
for tag in tags:
|
|
item.tags.add(tag, through_defaults={})
|
|
for prop in props:
|
|
ItemProperty.objects.create(inventory_item=item, property=prop['property'], value=prop['value'])
|
|
for file in files:
|
|
if type(file) == dict:
|
|
file_serializer = FileSerializer(data=file)
|
|
if file_serializer.is_valid():
|
|
file_serializer.save()
|
|
item.files.add(file_serializer.instance)
|
|
else:
|
|
raise serializers.ValidationError(file_serializer.errors)
|
|
elif type(file) == int:
|
|
if File.objects.filter(id=file).exists():
|
|
item.files.add(File.objects.get(id=file))
|
|
else:
|
|
raise serializers.ValidationError("File with id {} does not exist".format(file))
|
|
item.save()
|
|
return item
|
|
|
|
def update(self, instance, validated_data):
|
|
tags = validated_data.pop('tags', [])
|
|
props = validated_data.pop('itemproperty_set', [])
|
|
item = super().update(instance, validated_data)
|
|
item.tags.clear()
|
|
item.properties.clear()
|
|
if 'category' not in validated_data:
|
|
item.category = None
|
|
for tag in tags:
|
|
item.tags.add(tag)
|
|
for prop in props:
|
|
ItemProperty.objects.create(inventory_item=item, property=prop['property'], value=prop['value'])
|
|
item.save()
|
|
return item
|
|
|
|
class WorkflowInstanceSerializer(serializers.ModelSerializer):
|
|
owner = serializers.StringRelatedField(read_only=True)
|
|
|
|
class Meta:
|
|
model = WorkflowInstance
|
|
fields = ['id', 'name', 'state', 'payload', 'owner', 'created_at', 'updated_at']
|
|
read_only_fields = ['owner', 'created_at', 'updated_at']
|
|
|