from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from core.choices import ( ALL_RELATION_TYPES, INTERACTION_INTENSITY, ) class Contact(models.Model): """Контакт в социальном графе.""" name = models.CharField(max_length=255, verbose_name='Имя') email = models.EmailField(blank=True, verbose_name='Email') phone = models.CharField(max_length=50, blank=True, verbose_name='Телефон') organization = models.CharField(max_length=255, blank=True, verbose_name='Организация') position = models.CharField(max_length=255, blank=True, verbose_name='Должность') notes = models.TextField(blank=True, verbose_name='Заметки') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['name'] verbose_name = 'Контакт' verbose_name_plural = 'Контакты' def __str__(self): return self.name class NetworkMapType(models.Model): """Тип карты сети: настраиваемые секторы и концентрические круги.""" name = models.CharField(max_length=255, verbose_name='Название типа') sectors = models.JSONField(default=list, verbose_name='Секторы') circles = models.JSONField(default=list, verbose_name='Круги') is_default = models.BooleanField(default=False, verbose_name='Тип по умолчанию') conflictology_enabled = models.BooleanField( default=False, verbose_name='Режим конфликтологии', ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['name'] verbose_name = 'Тип карты сети' verbose_name_plural = 'Типы карт сети' def __str__(self): return self.name class NetworkMap(models.Model): """Карта сети — отдельный контекст для визуализации подмножества контактов.""" name = models.CharField(max_length=255, verbose_name='Название') description = models.TextField(blank=True, verbose_name='Описание') map_type = models.ForeignKey( NetworkMapType, on_delete=models.PROTECT, related_name='maps', verbose_name='Тип карты', ) conflict_subject = models.CharField( max_length=255, blank=True, verbose_name='Предмет конфликта (центр карты)', ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['name'] verbose_name = 'Карта сети' verbose_name_plural = 'Карты сети' def __str__(self): return self.name class NetworkMapMembership(models.Model): """Участие контакта на конкретной карте сети.""" map = models.ForeignKey( NetworkMap, on_delete=models.CASCADE, related_name='memberships', verbose_name='Карта', ) contact = models.ForeignKey( Contact, on_delete=models.CASCADE, related_name='map_memberships', verbose_name='Контакт', ) life_sphere = models.CharField( max_length=32, default='other', verbose_name='Сфера жизни', ) network_circle = models.CharField( max_length=32, default='productivity', verbose_name='Круг сети', ) importance = models.PositiveSmallIntegerField( default=3, validators=[MinValueValidator(1), MaxValueValidator(5)], verbose_name='Важность (1–5)', ) conflict_involvement = models.PositiveSmallIntegerField( default=3, validators=[MinValueValidator(1), MaxValueValidator(5)], verbose_name='Вовлечённость в конфликт (1–5)', ) map_angle = models.FloatField(null=True, blank=True, verbose_name='Угол позиции на карте') map_radius_ratio = models.FloatField( null=True, blank=True, validators=[MinValueValidator(0), MaxValueValidator(1)], verbose_name='Радиус позиции на карте (доля)', ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: unique_together = ('map', 'contact') verbose_name = 'Участие на карте' verbose_name_plural = 'Участия на картах' def __str__(self): return f'{self.contact} на {self.map}' class Relation(models.Model): """Связь между двумя контактами.""" source = models.ForeignKey( Contact, on_delete=models.CASCADE, related_name='relations_as_source', verbose_name='Источник', ) target = models.ForeignKey( Contact, on_delete=models.CASCADE, related_name='relations_as_target', verbose_name='Цель', ) relation_type = models.CharField( max_length=50, choices=ALL_RELATION_TYPES, default='acquaintance', verbose_name='Тип связи', ) description = models.CharField(max_length=255, blank=True, verbose_name='Описание') interaction_intensity = models.CharField( max_length=32, choices=INTERACTION_INTENSITY, default='intense', verbose_name='Интенсивность общения', ) created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('source', 'target') verbose_name = 'Связь' verbose_name_plural = 'Связи' def __str__(self): return f'{self.source} → {self.target} ({self.relation_type})'