Introduce network map view/components with filtering and positioning, expand contacts/map backend fields and migrations, and add a persistent light/dark theme toggle with graph label color updates for readability. Co-authored-by: Cursor <cursoragent@cursor.com>
84 lines
2.8 KiB
Python
84 lines
2.8 KiB
Python
from rest_framework import serializers
|
|
from .models import Contact, Relation
|
|
|
|
|
|
class ContactSerializer(serializers.ModelSerializer):
|
|
relations_count = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Contact
|
|
fields = [
|
|
'id', 'name', 'email', 'phone',
|
|
'organization', 'position', 'notes',
|
|
'life_sphere', 'network_circle', 'importance',
|
|
'include_on_network_map',
|
|
'map_angle', 'map_radius_ratio',
|
|
'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 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',
|
|
'life_sphere': c.life_sphere,
|
|
'network_circle': c.network_circle,
|
|
'importance': c.importance,
|
|
}
|
|
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
|
|
]
|