New clean API alongside old calendar_app: API endpoints: - /api/v1/schedule/calendars/ - Calendar CRUD - /api/v1/schedule/events/ - Event CRUD (with timezone) - /api/v1/schedule/rules/ - rrule RFC 5545 rules - /api/v1/schedule/occurrences/ - materialized recurrences - /api/v1/schedule/relations/ - meeting attendees Features: - Timezone support (pytz) - RFC 5545 rrule for recurring events - EventRelation for meeting participants - Clean, tested code (django-scheduler) - Parallel work with old /api/v1/calendar/ Old API untouched, works as before. Gradual migration approach (Strangler Fig Pattern).
141 lines
4.1 KiB
Python
141 lines
4.1 KiB
Python
# backend/api/v1/schedule/serializers.py
|
|
"""
|
|
Serializers для django-scheduler.
|
|
Чистые, проверенные временем модели.
|
|
"""
|
|
from rest_framework import serializers
|
|
from schedule.models import Calendar, Event, Rule, Occurrence, EventRelation
|
|
import pytz
|
|
|
|
|
|
class CalendarSerializer(serializers.ModelSerializer):
|
|
"""Сериализатор для Calendar (django-scheduler)."""
|
|
|
|
events_count = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Calendar
|
|
fields = ['id', 'name', 'slug', 'events_count']
|
|
read_only_fields = ['id']
|
|
|
|
def get_events_count(self, obj):
|
|
"""Количество событий в календаре."""
|
|
return obj.event_set.count()
|
|
|
|
|
|
class RuleSerializer(serializers.ModelSerializer):
|
|
"""Сериализатор для Rule (rrule RFC 5545)."""
|
|
|
|
class Meta:
|
|
model = Rule
|
|
fields = ['id', 'name', 'description', 'frequency', 'params']
|
|
read_only_fields = ['id']
|
|
|
|
|
|
class EventSerializer(serializers.ModelSerializer):
|
|
"""Сериализатор для Event (django-scheduler)."""
|
|
|
|
calendar_name = serializers.CharField(source='calendar.name', read_only=True)
|
|
rule_description = serializers.CharField(source='rule.description', read_only=True, allow_null=True)
|
|
|
|
class Meta:
|
|
model = Event
|
|
fields = [
|
|
'id',
|
|
'title',
|
|
'description',
|
|
'start',
|
|
'end',
|
|
'calendar',
|
|
'calendar_name',
|
|
'rule',
|
|
'rule_description',
|
|
'end_recurring_period',
|
|
'color_event',
|
|
'creator',
|
|
'created_on',
|
|
'updated_on',
|
|
]
|
|
read_only_fields = ['id', 'created_on', 'updated_on']
|
|
|
|
def validate(self, attrs):
|
|
"""Валидация start/end."""
|
|
start = attrs.get('start')
|
|
end = attrs.get('end')
|
|
|
|
if end and start and end < start:
|
|
raise serializers.ValidationError({
|
|
'end': 'Дата окончания не может быть раньше даты начала.'
|
|
})
|
|
|
|
return attrs
|
|
|
|
|
|
class EventListSerializer(EventSerializer):
|
|
"""Упрощённый сериализатор для списка событий."""
|
|
|
|
class Meta(EventSerializer.Meta):
|
|
fields = [
|
|
'id',
|
|
'title',
|
|
'start',
|
|
'end',
|
|
'calendar',
|
|
'calendar_name',
|
|
'color_event',
|
|
]
|
|
|
|
|
|
class OccurrenceSerializer(serializers.ModelSerializer):
|
|
"""Сериализатор для Occurrence (материализованные вхождения)."""
|
|
|
|
event_title = serializers.CharField(source='event.title', read_only=True)
|
|
event_id = serializers.IntegerField(source='event.id', read_only=True)
|
|
|
|
class Meta:
|
|
model = Occurrence
|
|
fields = [
|
|
'id',
|
|
'event',
|
|
'event_id',
|
|
'event_title',
|
|
'title',
|
|
'description',
|
|
'start',
|
|
'end',
|
|
'cancelled',
|
|
'original_start',
|
|
'original_end',
|
|
'created_on',
|
|
'updated_on',
|
|
]
|
|
read_only_fields = ['id', 'created_on', 'updated_on']
|
|
|
|
|
|
class EventRelationSerializer(serializers.ModelSerializer):
|
|
"""Сериализатор для EventRelation (участники встреч)."""
|
|
|
|
event_title = serializers.CharField(source='event.title', read_only=True)
|
|
user_name = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = EventRelation
|
|
fields = [
|
|
'id',
|
|
'event',
|
|
'event_title',
|
|
'content_type',
|
|
'object_id',
|
|
'content_object',
|
|
'user_name',
|
|
'distinction',
|
|
'created_on',
|
|
]
|
|
read_only_fields = ['id', 'created_on']
|
|
|
|
def get_user_name(self, obj):
|
|
"""Имя связанного объекта."""
|
|
if obj.content_object:
|
|
return str(obj.content_object)
|
|
return None
|