Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
366 changes: 0 additions & 366 deletions docs/event_architecture_audit.md

This file was deleted.

118 changes: 115 additions & 3 deletions pets/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.contrib import admin

from .models import Event, Pet
from .models import AnimalTypeEventRule, Event, EventType, Pet, PetEventSetting


@admin.register(Pet)
Expand Down Expand Up @@ -87,6 +87,113 @@ class PetAdmin(admin.ModelAdmin):
)


@admin.register(EventType)
class EventTypeAdmin(admin.ModelAdmin):
"""Настройки отображения справочника типов событий."""

list_display = (
"id",
"slug",
"name",
"is_system",
"owner",
"can_be_recurring",
"can_be_planned",
"affects_status",
"is_active",
)
list_filter = (
"is_system",
"can_be_recurring",
"can_be_planned",
"affects_status",
"is_active",
"created_at",
)
search_fields = (
"slug",
"name",
"owner__email",
)
autocomplete_fields = ("owner",)
readonly_fields = ("created_at", "updated_at")
list_select_related = ("owner",)
ordering = ("name",)


@admin.register(AnimalTypeEventRule)
class AnimalTypeEventRuleAdmin(admin.ModelAdmin):
"""Настройки отображения системных правил событий по типам животных."""

list_display = (
"id",
"animal_type",
"event_type",
"is_available",
"is_default_tracked",
"show_quick_action_by_default",
"affects_status",
"handling_policy",
"show_no_handling_field",
"shedding_policy",
"sort_order",
"is_active",
)
list_filter = (
"animal_type",
"event_type",
"is_available",
"is_default_tracked",
"show_quick_action_by_default",
"affects_status",
"handling_policy",
"show_no_handling_field",
"shedding_policy",
"is_active",
)
search_fields = (
"event_type__slug",
"event_type__name",
)
autocomplete_fields = ("event_type",)
readonly_fields = ("created_at", "updated_at")
list_select_related = ("event_type",)
ordering = ("animal_type", "sort_order", "event_type__name")


@admin.register(PetEventSetting)
class PetEventSettingAdmin(admin.ModelAdmin):
"""Настройки отображения отслеживаемых событий питомцев."""

list_display = (
"id",
"pet",
"event_type",
"is_tracked",
"show_quick_action",
"source",
"created_at",
)
list_filter = (
"event_type",
"is_tracked",
"show_quick_action",
"source",
"created_at",
)
search_fields = (
"pet__name",
"pet__species_name",
"pet__owner__email",
"event_type__slug",
"event_type__name",
)
autocomplete_fields = ("pet", "event_type")
readonly_fields = ("created_at", "updated_at")
list_select_related = ("pet", "event_type", "pet__owner")
ordering = ("pet__name", "event_type__name")


@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
"""Настройки отображения модели события в админке."""
Expand All @@ -95,6 +202,7 @@ class EventAdmin(admin.ModelAdmin):
"id",
"pet",
"event_type",
"event_type_ref",
"event_datetime",
"owner",
"repeat_after_days",
Expand All @@ -103,6 +211,7 @@ class EventAdmin(admin.ModelAdmin):
)
list_filter = (
"event_type",
"event_type_ref",
"event_datetime",
"created_at",
)
Expand All @@ -112,10 +221,12 @@ class EventAdmin(admin.ModelAdmin):
"owner__email",
"title",
"comment",
"event_type_ref__slug",
"event_type_ref__name",
)
autocomplete_fields = ("owner", "pet")
autocomplete_fields = ("owner", "pet", "event_type_ref")
readonly_fields = ("created_at",)
list_select_related = ("owner", "pet")
list_select_related = ("owner", "pet", "event_type_ref")
ordering = ("-event_datetime",)

fieldsets = (
Expand All @@ -126,6 +237,7 @@ class EventAdmin(admin.ModelAdmin):
"owner",
"pet",
"event_type",
"event_type_ref",
"title",
"event_datetime",
"comment",
Expand Down
143 changes: 143 additions & 0 deletions pets/migrations/0010_eventtype_event_event_type_ref.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Generated by Django 5.2.13 on 2026-07-28 09:47

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models

SYSTEM_EVENT_TYPES = (
{
"slug": "feeding",
"name": "Кормление",
"can_be_recurring": True,
"can_be_planned": True,
"affects_status": True,
},
{
"slug": "measurement",
"name": "Измерение",
"can_be_recurring": True,
"can_be_planned": True,
"affects_status": True,
},
{
"slug": "cleaning",
"name": "Уборка",
"can_be_recurring": True,
"can_be_planned": True,
"affects_status": False,
},
{
"slug": "shedding",
"name": "Линька",
"can_be_recurring": False,
"can_be_planned": False,
"affects_status": True,
},
{
"slug": "custom",
"name": "Другое",
"can_be_recurring": True,
"can_be_planned": True,
"affects_status": False,
},
)


def seed_event_types_and_link_events(apps, schema_editor):
EventType = apps.get_model("pets", "EventType")
Event = apps.get_model("pets", "Event")

event_types_by_slug = {}
for event_type_data in SYSTEM_EVENT_TYPES:
slug = event_type_data["slug"]
event_type, _created = EventType.objects.update_or_create(
slug=slug,
defaults={
"name": event_type_data["name"],
"is_system": True,
"owner": None,
"can_be_recurring": event_type_data["can_be_recurring"],
"can_be_planned": event_type_data["can_be_planned"],
"affects_status": event_type_data["affects_status"],
"allowed_animal_types": [],
"is_active": True,
},
)
event_types_by_slug[slug] = event_type

for slug, event_type in event_types_by_slug.items():
Event.objects.filter(event_type=slug, event_type_ref__isnull=True).update(event_type_ref=event_type)


def unlink_events_and_remove_seeded_event_types(apps, schema_editor):
EventType = apps.get_model("pets", "EventType")
Event = apps.get_model("pets", "Event")

slugs = [event_type_data["slug"] for event_type_data in SYSTEM_EVENT_TYPES]
Event.objects.filter(event_type_ref__slug__in=slugs).update(event_type_ref=None)
EventType.objects.filter(slug__in=slugs, is_system=True, owner__isnull=True).delete()


class Migration(migrations.Migration):

dependencies = [
("pets", "0009_alter_pet_is_public"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="EventType",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("slug", models.SlugField(unique=True, verbose_name="Slug")),
("name", models.CharField(max_length=100, verbose_name="Название")),
("is_system", models.BooleanField(default=False, verbose_name="Системный тип")),
("can_be_recurring", models.BooleanField(default=False, verbose_name="Можно повторять")),
("can_be_planned", models.BooleanField(default=False, verbose_name="Можно планировать")),
("affects_status", models.BooleanField(default=False, verbose_name="Влияет на статус")),
(
"allowed_animal_types",
models.JSONField(
blank=True,
default=list,
help_text="Список slug-ов типов животных. Пустой список означает без ограничений.",
verbose_name="Доступные типы животных",
),
),
("is_active", models.BooleanField(default=True, verbose_name="Активен")),
("created_at", models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")),
("updated_at", models.DateTimeField(auto_now=True, verbose_name="Дата обновления")),
(
"owner",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="event_types",
to=settings.AUTH_USER_MODEL,
verbose_name="Владелец",
),
),
],
options={
"verbose_name": "тип события",
"verbose_name_plural": "типы событий",
"ordering": ["name"],
},
),
migrations.AddField(
model_name="event",
name="event_type_ref",
field=models.ForeignKey(
blank=True,
help_text="Переходное поле для будущей архитектуры событий. Старое поле event_type остаётся рабочим.",
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="events",
to="pets.eventtype",
verbose_name="Тип события из справочника",
),
),
migrations.RunPython(seed_event_types_and_link_events, unlink_events_and_remove_seeded_event_types),
]
Loading
Loading