702 lines
27 KiB
Python
702 lines
27 KiB
Python
# Generated by Django 4.2.7 on 2025-12-09 21:02
|
||
|
||
import apps.schedule.models
|
||
from django.conf import settings
|
||
import django.core.validators
|
||
from django.db import migrations, models
|
||
import django.db.models.deletion
|
||
|
||
|
||
class Migration(migrations.Migration):
|
||
initial = True
|
||
|
||
dependencies = [
|
||
("materials", "0001_initial"),
|
||
("users", "0001_initial"),
|
||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||
]
|
||
|
||
operations = [
|
||
migrations.CreateModel(
|
||
name="Lesson",
|
||
fields=[
|
||
(
|
||
"id",
|
||
models.BigAutoField(
|
||
auto_created=True,
|
||
primary_key=True,
|
||
serialize=False,
|
||
verbose_name="ID",
|
||
),
|
||
),
|
||
(
|
||
"start_time",
|
||
models.DateTimeField(db_index=True, verbose_name="Время начала"),
|
||
),
|
||
(
|
||
"end_time",
|
||
models.DateTimeField(db_index=True, verbose_name="Время окончания"),
|
||
),
|
||
(
|
||
"duration",
|
||
models.IntegerField(
|
||
default=60,
|
||
help_text="Длительность занятия в минутах (15-480)",
|
||
validators=[
|
||
django.core.validators.MinValueValidator(15),
|
||
django.core.validators.MaxValueValidator(480),
|
||
],
|
||
verbose_name="Длительность (минуты)",
|
||
),
|
||
),
|
||
(
|
||
"title",
|
||
models.CharField(
|
||
help_text="Краткое название занятия",
|
||
max_length=200,
|
||
verbose_name="Название",
|
||
),
|
||
),
|
||
(
|
||
"description",
|
||
models.TextField(
|
||
blank=True,
|
||
help_text="Подробное описание занятия",
|
||
verbose_name="Описание",
|
||
),
|
||
),
|
||
(
|
||
"subject",
|
||
models.CharField(
|
||
blank=True,
|
||
help_text="Предмет обучения (математика, физика и т.д.)",
|
||
max_length=100,
|
||
verbose_name="Предмет",
|
||
),
|
||
),
|
||
(
|
||
"status",
|
||
models.CharField(
|
||
choices=[
|
||
("scheduled", "Запланировано"),
|
||
("in_progress", "В процессе"),
|
||
("completed", "Завершено"),
|
||
("cancelled", "Отменено"),
|
||
("rescheduled", "Перенесено"),
|
||
],
|
||
db_index=True,
|
||
default="scheduled",
|
||
max_length=20,
|
||
verbose_name="Статус",
|
||
),
|
||
),
|
||
(
|
||
"cancellation_reason",
|
||
models.TextField(blank=True, verbose_name="Причина отмены"),
|
||
),
|
||
(
|
||
"cancelled_at",
|
||
models.DateTimeField(
|
||
blank=True, null=True, verbose_name="Дата отмены"
|
||
),
|
||
),
|
||
(
|
||
"meeting_url",
|
||
models.URLField(
|
||
blank=True,
|
||
help_text="Ссылка на видеоконференцию",
|
||
max_length=500,
|
||
verbose_name="Ссылка на встречу",
|
||
),
|
||
),
|
||
(
|
||
"mentor_notes",
|
||
models.TextField(
|
||
blank=True,
|
||
help_text="Приватные заметки ментора о занятии",
|
||
verbose_name="Заметки ментора",
|
||
),
|
||
),
|
||
(
|
||
"homework_text",
|
||
models.TextField(
|
||
blank=True,
|
||
help_text="Описание домашнего задания, выданного по результатам занятия",
|
||
verbose_name="Домашнее задание",
|
||
),
|
||
),
|
||
(
|
||
"mentor_grade",
|
||
models.IntegerField(
|
||
blank=True,
|
||
help_text="Оценка работы студента на занятии (0-100)",
|
||
null=True,
|
||
verbose_name="Оценка ментора",
|
||
),
|
||
),
|
||
(
|
||
"school_grade",
|
||
models.IntegerField(
|
||
blank=True,
|
||
help_text="Текущая оценка студента в школе (0-100)",
|
||
null=True,
|
||
verbose_name="Оценка в школе",
|
||
),
|
||
),
|
||
(
|
||
"price",
|
||
models.DecimalField(
|
||
blank=True,
|
||
decimal_places=2,
|
||
help_text="Стоимость занятия в рублях",
|
||
max_digits=10,
|
||
null=True,
|
||
verbose_name="Стоимость",
|
||
),
|
||
),
|
||
(
|
||
"reminder_sent",
|
||
models.BooleanField(
|
||
default=False, verbose_name="Напоминание отправлено"
|
||
),
|
||
),
|
||
(
|
||
"created_at",
|
||
models.DateTimeField(
|
||
auto_now_add=True, null=True, verbose_name="Дата создания"
|
||
),
|
||
),
|
||
(
|
||
"updated_at",
|
||
models.DateTimeField(auto_now=True, verbose_name="Дата обновления"),
|
||
),
|
||
(
|
||
"cancelled_by",
|
||
models.ForeignKey(
|
||
blank=True,
|
||
null=True,
|
||
on_delete=django.db.models.deletion.SET_NULL,
|
||
related_name="cancelled_lessons",
|
||
to=settings.AUTH_USER_MODEL,
|
||
verbose_name="Отменено пользователем",
|
||
),
|
||
),
|
||
(
|
||
"client",
|
||
models.ForeignKey(
|
||
on_delete=django.db.models.deletion.CASCADE,
|
||
related_name="lessons",
|
||
to="users.client",
|
||
verbose_name="Клиент",
|
||
),
|
||
),
|
||
(
|
||
"group",
|
||
models.ForeignKey(
|
||
blank=True,
|
||
help_text="Учебная группа, если занятие групповое",
|
||
null=True,
|
||
on_delete=django.db.models.deletion.SET_NULL,
|
||
related_name="lessons",
|
||
to="users.group",
|
||
verbose_name="Группа",
|
||
),
|
||
),
|
||
(
|
||
"mentor",
|
||
models.ForeignKey(
|
||
limit_choices_to={"role": "mentor"},
|
||
on_delete=django.db.models.deletion.CASCADE,
|
||
related_name="mentor_lessons",
|
||
to=settings.AUTH_USER_MODEL,
|
||
verbose_name="Ментор",
|
||
),
|
||
),
|
||
(
|
||
"rescheduled_from",
|
||
models.ForeignKey(
|
||
blank=True,
|
||
null=True,
|
||
on_delete=django.db.models.deletion.SET_NULL,
|
||
related_name="rescheduled_to",
|
||
to="schedule.lesson",
|
||
verbose_name="Перенесено из",
|
||
),
|
||
),
|
||
],
|
||
options={
|
||
"verbose_name": "Занятие",
|
||
"verbose_name_plural": "Занятия",
|
||
"db_table": "lessons",
|
||
"ordering": ["start_time"],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name="LessonTemplate",
|
||
fields=[
|
||
(
|
||
"id",
|
||
models.BigAutoField(
|
||
auto_created=True,
|
||
primary_key=True,
|
||
serialize=False,
|
||
verbose_name="ID",
|
||
),
|
||
),
|
||
("title", models.CharField(max_length=200, verbose_name="Название")),
|
||
("description", models.TextField(blank=True, verbose_name="Описание")),
|
||
(
|
||
"subject",
|
||
models.CharField(
|
||
blank=True, max_length=100, verbose_name="Предмет"
|
||
),
|
||
),
|
||
(
|
||
"duration",
|
||
models.IntegerField(
|
||
default=60,
|
||
validators=[
|
||
django.core.validators.MinValueValidator(15),
|
||
django.core.validators.MaxValueValidator(480),
|
||
],
|
||
verbose_name="Длительность (минуты)",
|
||
),
|
||
),
|
||
(
|
||
"is_active",
|
||
models.BooleanField(default=True, verbose_name="Активен"),
|
||
),
|
||
(
|
||
"meeting_url",
|
||
models.URLField(
|
||
blank=True, max_length=500, verbose_name="Ссылка на встречу"
|
||
),
|
||
),
|
||
(
|
||
"color",
|
||
models.CharField(
|
||
default="#3B82F6",
|
||
help_text="HEX код цвета для отображения в календаре",
|
||
max_length=7,
|
||
verbose_name="Цвет",
|
||
),
|
||
),
|
||
(
|
||
"created_at",
|
||
models.DateTimeField(
|
||
auto_now_add=True, null=True, verbose_name="Дата создания"
|
||
),
|
||
),
|
||
(
|
||
"updated_at",
|
||
models.DateTimeField(auto_now=True, verbose_name="Дата обновления"),
|
||
),
|
||
(
|
||
"mentor",
|
||
models.ForeignKey(
|
||
limit_choices_to={"role": "mentor"},
|
||
on_delete=django.db.models.deletion.CASCADE,
|
||
related_name="lesson_templates",
|
||
to=settings.AUTH_USER_MODEL,
|
||
verbose_name="Ментор",
|
||
),
|
||
),
|
||
],
|
||
options={
|
||
"verbose_name": "Шаблон занятия",
|
||
"verbose_name_plural": "Шаблоны занятий",
|
||
"db_table": "lesson_templates",
|
||
"ordering": ["mentor", "title"],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name="LessonFile",
|
||
fields=[
|
||
(
|
||
"id",
|
||
models.BigAutoField(
|
||
auto_created=True,
|
||
primary_key=True,
|
||
serialize=False,
|
||
verbose_name="ID",
|
||
),
|
||
),
|
||
(
|
||
"file",
|
||
models.FileField(
|
||
blank=True,
|
||
max_length=500,
|
||
null=True,
|
||
upload_to=apps.schedule.models.lesson_file_upload_path,
|
||
validators=[
|
||
django.core.validators.FileExtensionValidator(
|
||
allowed_extensions=[
|
||
"pdf",
|
||
"doc",
|
||
"docx",
|
||
"txt",
|
||
"jpg",
|
||
"jpeg",
|
||
"png",
|
||
"zip",
|
||
"rar",
|
||
]
|
||
)
|
||
],
|
||
verbose_name="Файл",
|
||
),
|
||
),
|
||
(
|
||
"source",
|
||
models.CharField(
|
||
choices=[
|
||
("uploaded", "Загружен при завершении"),
|
||
("material", "Из учебных материалов"),
|
||
],
|
||
default="uploaded",
|
||
max_length=20,
|
||
verbose_name="Источник",
|
||
),
|
||
),
|
||
(
|
||
"filename",
|
||
models.CharField(max_length=255, verbose_name="Название файла"),
|
||
),
|
||
(
|
||
"file_size",
|
||
models.BigIntegerField(
|
||
blank=True, null=True, verbose_name="Размер файла (bytes)"
|
||
),
|
||
),
|
||
("description", models.TextField(blank=True, verbose_name="Описание")),
|
||
(
|
||
"created_at",
|
||
models.DateTimeField(
|
||
auto_now_add=True, verbose_name="Дата создания"
|
||
),
|
||
),
|
||
(
|
||
"lesson",
|
||
models.ForeignKey(
|
||
on_delete=django.db.models.deletion.CASCADE,
|
||
related_name="files",
|
||
to="schedule.lesson",
|
||
verbose_name="Урок",
|
||
),
|
||
),
|
||
(
|
||
"material",
|
||
models.ForeignKey(
|
||
blank=True,
|
||
null=True,
|
||
on_delete=django.db.models.deletion.SET_NULL,
|
||
related_name="lesson_files",
|
||
to="materials.material",
|
||
verbose_name="Учебный материал",
|
||
),
|
||
),
|
||
(
|
||
"uploaded_by",
|
||
models.ForeignKey(
|
||
null=True,
|
||
on_delete=django.db.models.deletion.SET_NULL,
|
||
related_name="uploaded_lesson_files",
|
||
to=settings.AUTH_USER_MODEL,
|
||
verbose_name="Загрузил",
|
||
),
|
||
),
|
||
],
|
||
options={
|
||
"verbose_name": "Файл урока",
|
||
"verbose_name_plural": "Файлы уроков",
|
||
"db_table": "lesson_files",
|
||
"ordering": ["-created_at"],
|
||
},
|
||
),
|
||
migrations.AddField(
|
||
model_name="lesson",
|
||
name="template",
|
||
field=models.ForeignKey(
|
||
blank=True,
|
||
null=True,
|
||
on_delete=django.db.models.deletion.SET_NULL,
|
||
related_name="lessons",
|
||
to="schedule.lessontemplate",
|
||
verbose_name="Шаблон",
|
||
),
|
||
),
|
||
migrations.CreateModel(
|
||
name="Availability",
|
||
fields=[
|
||
(
|
||
"id",
|
||
models.BigAutoField(
|
||
auto_created=True,
|
||
primary_key=True,
|
||
serialize=False,
|
||
verbose_name="ID",
|
||
),
|
||
),
|
||
(
|
||
"day_of_week",
|
||
models.IntegerField(
|
||
blank=True,
|
||
choices=[
|
||
(0, "Понедельник"),
|
||
(1, "Вторник"),
|
||
(2, "Среда"),
|
||
(3, "Четверг"),
|
||
(4, "Пятница"),
|
||
(5, "Суббота"),
|
||
(6, "Воскресенье"),
|
||
],
|
||
help_text="Для еженедельного повторения",
|
||
null=True,
|
||
verbose_name="День недели",
|
||
),
|
||
),
|
||
(
|
||
"specific_date",
|
||
models.DateField(
|
||
blank=True,
|
||
help_text="Для разовой доступности",
|
||
null=True,
|
||
verbose_name="Конкретная дата",
|
||
),
|
||
),
|
||
("start_time", models.TimeField(verbose_name="Время начала")),
|
||
("end_time", models.TimeField(verbose_name="Время окончания")),
|
||
(
|
||
"is_recurring",
|
||
models.BooleanField(
|
||
default=True,
|
||
help_text="Повторяется ли еженедельно",
|
||
verbose_name="Повторяющаяся",
|
||
),
|
||
),
|
||
(
|
||
"is_active",
|
||
models.BooleanField(default=True, verbose_name="Активна"),
|
||
),
|
||
(
|
||
"exception_dates",
|
||
models.JSONField(
|
||
blank=True,
|
||
default=list,
|
||
help_text="Список дат в формате YYYY-MM-DD",
|
||
verbose_name="Даты исключений",
|
||
),
|
||
),
|
||
("notes", models.TextField(blank=True, verbose_name="Заметки")),
|
||
(
|
||
"created_at",
|
||
models.DateTimeField(
|
||
auto_now_add=True, verbose_name="Дата создания"
|
||
),
|
||
),
|
||
(
|
||
"updated_at",
|
||
models.DateTimeField(auto_now=True, verbose_name="Дата обновления"),
|
||
),
|
||
(
|
||
"mentor",
|
||
models.ForeignKey(
|
||
limit_choices_to={"role": "mentor"},
|
||
on_delete=django.db.models.deletion.CASCADE,
|
||
related_name="availabilities",
|
||
to=settings.AUTH_USER_MODEL,
|
||
verbose_name="Ментор",
|
||
),
|
||
),
|
||
],
|
||
options={
|
||
"verbose_name": "Доступность",
|
||
"verbose_name_plural": "Доступность",
|
||
"db_table": "availabilities",
|
||
"ordering": ["mentor", "day_of_week", "start_time"],
|
||
},
|
||
),
|
||
migrations.CreateModel(
|
||
name="TimeSlot",
|
||
fields=[
|
||
(
|
||
"id",
|
||
models.BigAutoField(
|
||
auto_created=True,
|
||
primary_key=True,
|
||
serialize=False,
|
||
verbose_name="ID",
|
||
),
|
||
),
|
||
(
|
||
"start_time",
|
||
models.DateTimeField(db_index=True, verbose_name="Время начала"),
|
||
),
|
||
(
|
||
"end_time",
|
||
models.DateTimeField(db_index=True, verbose_name="Время окончания"),
|
||
),
|
||
(
|
||
"is_available",
|
||
models.BooleanField(
|
||
default=True,
|
||
help_text="Свободен ли слот для бронирования",
|
||
verbose_name="Доступен",
|
||
),
|
||
),
|
||
(
|
||
"is_booked",
|
||
models.BooleanField(default=False, verbose_name="Забронирован"),
|
||
),
|
||
(
|
||
"is_recurring",
|
||
models.BooleanField(
|
||
default=False,
|
||
help_text="Повторяется ли этот слот еженедельно",
|
||
verbose_name="Повторяющийся",
|
||
),
|
||
),
|
||
(
|
||
"recurring_day",
|
||
models.IntegerField(
|
||
blank=True,
|
||
help_text="0=Понедельник, 6=Воскресенье",
|
||
null=True,
|
||
validators=[
|
||
django.core.validators.MinValueValidator(0),
|
||
django.core.validators.MaxValueValidator(6),
|
||
],
|
||
verbose_name="День недели",
|
||
),
|
||
),
|
||
(
|
||
"created_at",
|
||
models.DateTimeField(
|
||
auto_now_add=True, null=True, verbose_name="Дата создания"
|
||
),
|
||
),
|
||
(
|
||
"updated_at",
|
||
models.DateTimeField(auto_now=True, verbose_name="Дата обновления"),
|
||
),
|
||
(
|
||
"lesson",
|
||
models.OneToOneField(
|
||
blank=True,
|
||
null=True,
|
||
on_delete=django.db.models.deletion.SET_NULL,
|
||
related_name="time_slot",
|
||
to="schedule.lesson",
|
||
verbose_name="Занятие",
|
||
),
|
||
),
|
||
(
|
||
"mentor",
|
||
models.ForeignKey(
|
||
limit_choices_to={"role": "mentor"},
|
||
on_delete=django.db.models.deletion.CASCADE,
|
||
related_name="time_slots",
|
||
to=settings.AUTH_USER_MODEL,
|
||
verbose_name="Ментор",
|
||
),
|
||
),
|
||
],
|
||
options={
|
||
"verbose_name": "Временной слот",
|
||
"verbose_name_plural": "Временные слоты",
|
||
"db_table": "time_slots",
|
||
"ordering": ["start_time"],
|
||
"indexes": [
|
||
models.Index(
|
||
fields=["mentor", "start_time"],
|
||
name="time_slots_mentor__87e9a7_idx",
|
||
),
|
||
models.Index(
|
||
fields=["is_available", "is_booked"],
|
||
name="time_slots_is_avai_b83db3_idx",
|
||
),
|
||
models.Index(
|
||
fields=["start_time", "end_time"],
|
||
name="time_slots_start_t_7c83d2_idx",
|
||
),
|
||
],
|
||
},
|
||
),
|
||
migrations.AddConstraint(
|
||
model_name="timeslot",
|
||
constraint=models.CheckConstraint(
|
||
check=models.Q(("end_time__gt", models.F("start_time"))),
|
||
name="timeslot_end_after_start",
|
||
),
|
||
),
|
||
migrations.AddIndex(
|
||
model_name="lessonfile",
|
||
index=models.Index(
|
||
fields=["lesson"], name="lesson_file_lesson__d6fb1a_idx"
|
||
),
|
||
),
|
||
migrations.AddIndex(
|
||
model_name="lessonfile",
|
||
index=models.Index(
|
||
fields=["material"], name="lesson_file_materia_35443f_idx"
|
||
),
|
||
),
|
||
migrations.AddIndex(
|
||
model_name="lesson",
|
||
index=models.Index(
|
||
fields=["mentor", "start_time"], name="lessons_mentor__e6e1ba_idx"
|
||
),
|
||
),
|
||
migrations.AddIndex(
|
||
model_name="lesson",
|
||
index=models.Index(
|
||
fields=["client", "start_time"], name="lessons_client__6fbdab_idx"
|
||
),
|
||
),
|
||
migrations.AddIndex(
|
||
model_name="lesson",
|
||
index=models.Index(
|
||
fields=["status", "start_time"], name="lessons_status_a82e95_idx"
|
||
),
|
||
),
|
||
migrations.AddIndex(
|
||
model_name="lesson",
|
||
index=models.Index(
|
||
fields=["start_time", "end_time"], name="lessons_start_t_f960c9_idx"
|
||
),
|
||
),
|
||
migrations.AddConstraint(
|
||
model_name="lesson",
|
||
constraint=models.CheckConstraint(
|
||
check=models.Q(("end_time__gt", models.F("start_time"))),
|
||
name="lesson_end_after_start",
|
||
),
|
||
),
|
||
migrations.AddIndex(
|
||
model_name="availability",
|
||
index=models.Index(
|
||
fields=["mentor", "is_active"], name="availabilit_mentor__4001a0_idx"
|
||
),
|
||
),
|
||
migrations.AddIndex(
|
||
model_name="availability",
|
||
index=models.Index(
|
||
fields=["day_of_week", "start_time"],
|
||
name="availabilit_day_of__7b54ec_idx",
|
||
),
|
||
),
|
||
migrations.AddIndex(
|
||
model_name="availability",
|
||
index=models.Index(
|
||
fields=["specific_date"], name="availabilit_specifi_49965a_idx"
|
||
),
|
||
),
|
||
migrations.AddConstraint(
|
||
model_name="availability",
|
||
constraint=models.CheckConstraint(
|
||
check=models.Q(("end_time__gt", models.F("start_time"))),
|
||
name="availability_end_after_start",
|
||
),
|
||
),
|
||
]
|