from rest_framework import serializers from core.access import use_jwt_auth from .models import Contact, Relation, NetworkMap, NetworkMapMembership, NetworkMapType from .map_type_validation import validate_map_type_payload def scoped_map_types_queryset(request): qs = NetworkMapType.objects.all() if use_jwt_auth() and request and request.user.is_authenticated: return qs.filter(owner=request.user) return qs class ContactSerializer(serializers.ModelSerializer): relations_count = serializers.SerializerMethodField() class Meta: model = Contact fields = [ 'id', 'name', 'email', 'phone', 'organization', 'position', 'notes', 'created_at', 'updated_at', 'relations_count', ] read_only_fields = ['id', 'created_at', 'updated_at', 'relations_count'] def get_relations_count(self, obj): return ( obj.relations_as_source.count() + obj.relations_as_target.count() ) class RelationSerializer(serializers.ModelSerializer): source_name = serializers.CharField(source='source.name', read_only=True) target_name = serializers.CharField(source='target.name', read_only=True) class Meta: model = Relation fields = [ 'id', 'source', 'source_name', 'target', 'target_name', 'relation_type', 'description', 'interaction_intensity', 'created_at', ] read_only_fields = ['id', 'created_at', 'source_name', 'target_name'] def validate(self, data): request = self.context.get('request') source = data.get('source') or getattr(self.instance, 'source', None) target = data.get('target') or getattr(self.instance, 'target', None) if source and target and source == target: raise serializers.ValidationError( 'Нельзя создать связь контакта с самим собой.' ) if use_jwt_auth() and request and request.user.is_authenticated: user = request.user for contact in (source, target): if contact and contact.owner_id != user.id: raise serializers.ValidationError( 'Контакт не принадлежит текущему пользователю.' ) return data class NetworkMapTypeSerializer(serializers.ModelSerializer): class Meta: model = NetworkMapType fields = [ 'id', 'name', 'sectors', 'circles', 'is_default', 'conflictology_enabled', 'created_at', 'updated_at', ] read_only_fields = ['id', 'is_default', 'created_at', 'updated_at'] def validate(self, data): sectors = data.get('sectors', getattr(self.instance, 'sectors', None)) circles = data.get('circles', getattr(self.instance, 'circles', None)) errors = validate_map_type_payload(sectors, circles) if errors: raise serializers.ValidationError(errors) return data def _default_exists(self): request = self.context.get('request') qs = scoped_map_types_queryset(request) return qs.filter(is_default=True).exists() def create(self, validated_data): if self._default_exists(): validated_data['is_default'] = False return super().create(validated_data) class NetworkMapSerializer(serializers.ModelSerializer): memberships_count = serializers.SerializerMethodField() map_type = serializers.PrimaryKeyRelatedField( queryset=NetworkMapType.objects.all(), required=False, ) class Meta: model = NetworkMap fields = [ 'id', 'name', 'description', 'map_type', 'conflict_subject', 'created_at', 'updated_at', 'memberships_count', ] read_only_fields = ['id', 'created_at', 'updated_at', 'memberships_count'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) request = self.context.get('request') self.fields['map_type'].queryset = scoped_map_types_queryset(request) def validate(self, data): request = self.context.get('request') if not data.get('map_type') and not getattr(self.instance, 'map_type_id', None): default_type = scoped_map_types_queryset(request).filter(is_default=True).first() if not default_type: raise serializers.ValidationError({'map_type': 'Нет типа карты по умолчанию.'}) data['map_type'] = default_type map_type = data.get('map_type') or getattr(self.instance, 'map_type', None) if use_jwt_auth() and request and request.user.is_authenticated and map_type: if map_type.owner_id != request.user.id: raise serializers.ValidationError({'map_type': 'Тип карты не принадлежит текущему пользователю.'}) return data def get_memberships_count(self, obj): return obj.memberships.count() class NetworkMapMembershipSerializer(serializers.ModelSerializer): contact_name = serializers.CharField(source='contact.name', read_only=True) map_name = serializers.CharField(source='map.name', read_only=True) class Meta: model = NetworkMapMembership fields = [ 'id', 'map', 'map_name', 'contact', 'contact_name', 'life_sphere', 'network_circle', 'importance', 'conflict_involvement', 'map_angle', 'map_radius_ratio', 'created_at', 'updated_at', ] read_only_fields = ['id', 'created_at', 'updated_at', 'contact_name', 'map_name'] class GraphSerializer(serializers.Serializer): """Граф для vis.js: nodes + edges.""" nodes = serializers.SerializerMethodField() edges = serializers.SerializerMethodField() def get_nodes(self, obj): contacts = Contact.objects.all() return [ { 'id': c.id, 'label': c.name, 'title': f'{c.organization}\n{c.position}'.strip() or c.name, 'group': c.organization or 'default', } for c in contacts ] def get_edges(self, obj): relations = Relation.objects.select_related('source', 'target').all() return [ { 'id': r.id, 'from': r.source_id, 'to': r.target_id, 'label': r.get_relation_type_display(), 'title': r.description or r.get_relation_type_display(), 'relation_type': r.relation_type, 'interaction_intensity': r.interaction_intensity, } for r in relations ]