from rest_framework import serializers from .models import Contact, Relation, NetworkMap, NetworkMapMembership, NetworkMapType from .map_type_validation import validate_map_type_payload 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): if data.get('source') == data.get('target'): 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 create(self, validated_data): if NetworkMapType.objects.filter(is_default=True).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 validate(self, data): if not data.get('map_type') and not getattr(self.instance, 'map_type_id', None): default_type = NetworkMapType.objects.filter(is_default=True).first() if not default_type: raise serializers.ValidationError({'map_type': 'Нет типа карты по умолчанию.'}) data['map_type'] = default_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 ]