# Описание модели данных
Звездочкой (*) отмечены обязательные поля.
В качестве уникального идентификатора, первичного ключа моделей используется автоматически создаваемое Django для моделей поле **id** типа BigAutoField.
## Аккаунт
Используется встроенная модель [User](https://docs.djangoproject.com/en/3.2/ref/contrib/auth/) из django.contrib.auth.models.
Поля email, first_name и last_name заполнять данными из user_info Яндекс ID:
`first_name: user_info['first_name']`, `last_name: user_info['last_name']`, `email: user_info['default_email']`.
Полю username принять решение заполнять яндекс.id или login. Сохранить возможность изменения пользователем полей first_name и last_name в профиле пользователя.
Роли (Permission) назначаются c использованием моделей и методов, предоставляемых django.contrib.auth.models.
### Используемые роли
**Главный администратор (main_admin)** - (все права + может удалять курсы). Выборочно назначает роли и дает им права.
**Администратор (admin)** — загружает материалы, добавляет студентов, подтверждает статусы студентов, может переключаться на "вид студента".
Администратор может поставить ограничение для преподавателей:
- преподаватель может редактировать другие направления;
- преподаватель может редактировать только в рамках своего направления.
**Преподаватель (teacher)** — загружает материалы, видит, кто проходит курс, и кто его уже прошёл.
Выполняет проверку тестов.
**Пользователь (volunteer)** — доброволец, который проходит курсы.
У пользователя есть признак «Направление» (может быть несколько направлений) и уровень «Новичок/Бывалый/Профессионал».
Пользователь может по ним фильтровать доступные курсы + видит все открытые курсы.
При первоначальной регистрации признак «Направление» пуст.
По умолчанию при первой регистрации для пользователя устанавливается уровень «Новичок».
Для пользователей с уровнем «Новичок» одобрение (активация) учетной записи автоматическая,
без подтверждения администратора.
Новому пользователю открыты все курсы с уровнем «Новичок».
Если новый пользователь вводит уровень «Бывалый/Профессионал» — это отправляется на подтверждение администратору,
и только после подтверждения администратором указанный уровень закрепляется в профиле.
Предусмотреть сброс флага подтверждения уровня пользователя и отправку уведомления администратору
в случае изменения пользователем в профиле уровня на более высокий.
Регион - важная информация для пользователя, по нему открываются курсы этого региона.
_Примечания:_
_1. Следует предусмотреть вывод подтверждения пользователю в случае попытки установить более низкий
уровень, например Бывалый при текущем уровне Профессионал._
_2. Обсудить с заказчиком необходимость запрета смены региона в профиле пользователя, например,
пользователь может изменить регион для доступа к курсам, которые отсутствуют в его регионе или определить
процедуру смены региона пользователем._
### Аутентификация и авторизация
Аутентификация пользователя на платформе с помощью OAuth2 через Яндекс ID.
Дальнейшая авторизация на платформе с использованием JWT токенов (access_token и refresh_token),
формируемых после успешной аутентификации через OAuth2.
Токены Яндекс ID не храним, поскольку обращение к сервисам Яндекса не предполагается.
Авторизация в системе с использованием JWT токенов (access_token и refresh_token).
Реализовать с использованием [плагина](https://django-rest-framework-simplejwt.readthedocs.io/en/latest/index.html) аутентификации JWT для Django REST Framework.
Определиться с плагином для аутентификации с помощью OAuth2 через Яндекс ID.
Роли передаются в виде списка в поле roles payload access-токена.
E-mail пользователя берется из user_info Яндекс ID и не может быть изменен пользователем в профиле самостоятельно.
В этом случае нет необходимости использовать Celery для подтверждения e-mail.
**Для всех таблиц ниже используется отдельная схема БД Postgresql (например, content)**
## Волонтер
Имя модели - **Volunteer**
Таблица БД - **volunteers**
Поля модели:
- **user*** - пользователь, тип OneToOneField, отношение один-к-одному к модели **User**
- **phone_number*** - номер телефона, тип PhoneNumberField*
- **birth_date*** - дата рождения, тип DateField
- **location*** - географический регион, тип ForeignKey (один ко многим) к модели **Location** (Географический регион)
- **direction** - направление, тип ForeignKey (один ко многим) к модели **Direction** (Направление)
- **call_sign** - позывной на форуме, тип CharField, длина 50
- **photo** - путь к фотографии волонтера и ее миниатюре, тип ThumbnailerImageField**
- **level** - уровень, тип ManyToManyField (многие ко многим) к модели **Level** (Уровень), через модель **VolunteerLevel**
- **badges** - значки, тип ManyToManyField (многие ко многим) к модели **Badge** (Значки), через модель **VolunteerBadge**
- **courses** - курсы, тип ManyToManyField (многие ко многим) к модели **Сourse** (Курсы), через модель **VolunteerCourse**
- **created_at*** - дата создания записи об учащемся, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи об учащемся, тип DateTimeField с таймстамп, сделать автоматическое проставление текущего времени
*Использовать библиотеку [django-phonenumber-field[phonenumbers]==6.0.0](https://pypi.org/project/django-phonenumber-field/6.0.0/)
**Использовать библиотеку [easy-thumbnails](https://pypi.org/project/easy-thumbnails/2.7.2/)
## Уровень
Имя модели - **Level**
Таблица БД - **levels**
Поля модели:
- **name*** - наименование уровня, тип CharField, длина 20, (выбор из Новичок, Бывалый, Профессионал)
- **description** - описание уровня и условий его достижения, тип TextField
## Учащиеся <-> Уровень (многие-ко-многим)
Имя модели - **VolunteerLevel**
Таблица БД - **volunteers_levels**
Поля модели:
- **volunteer*** - тип ForeignKey к модели волонтера **Volunteer**
- **level*** - тип ForeignKey к модели уровень **Level**
- **confirmed*** - статус подтверждения волонтера Администратором, тип BooleanField, по умолчанию False
- **who_confirmed** - кто подтвердил статус пользователя, тип ForeignKey (один ко многим) к модели **User**
- **created_at*** - дата создания записи связи учащийся - уровень, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи связи учащийся - уровень, тип DateTimeField с таймстамп, сделать автоматическое проставление текущего времени
Создать уникальный индекс (UniqueConstraint) по полям [volunteer, level] чтобы конкретный уровень был привязан к пользователю только один раз.
## Географический регион
Имя модели - **Location**
Таблица БД - **locations**
Поля модели:
- **code** - код региона, тип SmallIntegerField
- **region*** - наименование региона, тип CharField, длина 120
## Направление
Имя модели - **Direction**
Таблица БД - **directions**
Поля модели:
- **title*** - наименование направления, тип CharField, длина 120
- **description** - описание направления, тип TextField
## Значок
Имя модели - **Badge**
Таблица БД - **badges**
Поля модели:
- **name*** - наименование значка, тип CharField, длина 40
- **description** - описание значка и условий его получения, тип TextField
## Учащиеся <-> Значки (многие-ко-многим)
Имя модели - **VolunteerBadge**
Таблица БД - **volunteers_badges**
Поля модели:
- **volunteer*** - тип ForeignKey к модели волонтера **Volunteer**
- **badge*** - тип ForeignKey к модели значка **Badge**
- **created_at*** - дата создания записи о связи волонтер - значок, тип DateTimeField, сделать автоматическое проставление текущего времени
## Учащиеся <-> Курсы (многие-ко-многим)
Имя модели - **VolunteerCourse**
Таблица БД - **volunteers_courses**
Поля модели:
- **volunteer*** - тип ForeignKey к модели волонтер **Volunteer**
- **course*** - тип ForeignKey к модели курса **Course**
- **status*** - статус курса, тип CharField, длина 20, выбор из Активный, Пройден, Вы записаны
- **assessment*** - оценка по результатам прохождения курса, тип FloatField, валидация диапазона: от 0 до 100,0. Если курс не проходился, то 0.
- **created_at*** - дата создания записи о связи волонтер - курс, тип DateTimeField, сделать автоматическое проставление текущего времени
Создать уникальный индекс (UniqueConstraint) по полям [volunteer, course, status] чтобы конкретный курс был привязан к пользователю только с одним из возможных статусов.
Реализовать метод модели создания записей в таблице volunteers_lessons по всем урокам курса при изменении status на Вы записаны.
## Сертификат
Имя модели - **Sertificate**
Таблица БД - **sertificates**
Поля модели:
- **volunteer*** - тип ForeignKey к модели волонтер **Volunteer**
- **course*** - тип ForeignKey к модели курса **Course**
- **final_assessment*** - итоговая оценка по результатам прохождения курса, тип FloatField, валидация диапазона: от (_уточнить нижний процент_) до 100,0.
- **sertificate_path*** - путь к файлу с сертификатом, тип FileField
- **issued** - руководитель курса, кто выдал сертификат, тип ForeignKey (один ко многим) к модели **User**
- **created_at*** - дата создания записи о сертификате, тип DateTimeField, сделать автоматическое проставление текущего времени
## Курс
Имя модели - **Course**
Таблица БД - **courses**
Поля модели:
- **title*** - название курса, тип CharField, длина 120
- **direction*** - направление, тип CharField, длина 60, выбор из заданного списка (_уточнить список направлений_)
- **location** - географический регион, ManyToManyField (многие ко многим) к модели **Location** (Географический регион), через модель **CourseLocation**
- **level*** - уровень, тип ForeignKey (один ко многим) к модели **Level** (Уровень)
- **format*** - формат курса, длина 60, выбор из заданного списка (_уточнить список форматов_)
- **start_date*** - дата начала курса, тип DateField
- **cover_path** - путь к обложке курса, тип FileField
- **short_description*** - краткое описание курса, тип CharField, длина 120
- **full_description*** - полное описание курса, тип TextField
- **skills** - тип ManyToManyField (многие ко многим) к модели **Skill** (Навыки), через модель **CourseSkill**
- **faq** - тип ManyToManyField (многие ко многим) к модели **FAQ** (Часто задаваемые вопросы), через модель **CourseFAQ**
- **contents** - ManyToManyField (многие ко многим) к модели **Chapter** (Главы), через модель **CourseChapter**
- **duration** - продолжительность курса (вычисляемое поле на основании информации о продолжительности всех уроков курса), тип SmallIntegerField
- **number_of_classes** - количество занятий (вычисляемое поле на основании количества уроков в курсе), тип SmallIntegerField
- **attempts_limit*** - количество попыток на прохождение курса. Если задано 0, то количество попыток не ограничено. Тип SmallIntegerField, валидация от 0 до 10 (_уточнить возможную верхнюю границу диапазона_)
- **user_created*** - пользователь создавший курс, тип ForeignKey (один ко многим) к модели **User**
- **user_modified*** - последний редактировавший курс, тип ForeignKey (один ко многим) к модели **User**
- **ready*** - статус готовности готов/не готов, тип BooleanField
- **visible*** - статус видимости курса вкл./выкл., тип BooleanField
- **published*** - статус публикации курса, тип BooleanField
- **created_at*** - дата создания записи о курсе, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи о курсе, тип DateTimeField, сделать автоматическое проставление текущего времени
## Навыки
Имя модели - **Skill**
Таблица БД - **skills**
Поля модели:
- **title*** - название навыка, тип CharField, длина 120
- **description*** - описание навыка, тип CharField, длина 255
- **user_created*** - пользователь создавший описание навыка, тип ForeignKey (один ко многим) к модели **User**
- **user_modified*** - последний редактировавший описания навыка, тип ForeignKey (один ко многим) к модели **User**
- **created_at*** - дата создания записи о навыке, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи о навыке, тип DateTimeField, сделать автоматическое проставление текущего времени
## Курсы <-> Географический регион (многие-ко-многим)
Имя модели - **CourseLocation**
Таблица БД - courses_locations
Поля модели:
- **course*** - тип ForeignKey к модели курса **Course**
- **location*** - тип ForeignKey к модели навыка **Location**
- **created_at*** - дата создания записи о связи курс - географический регион, тип DateTimeField, сделать автоматическое проставление текущего времени
Создать уникальный индекс (UniqueConstraint) по полям [course, location], чтобы конкретный курс был привязан к конкретному региону один раз.
## Курсы <-> Навыки (многие-ко-многим)
Имя модели - **CourseSkill**
Таблица БД - courses_skills
Поля модели:
- **course*** - тип ForeignKey к модели курса **Course**
- **skill*** - тип ForeignKey к модели навыка **Skill**
- **created_at*** - дата создания записи о связи курс - навык, тип DateTimeField, сделать автоматическое проставление текущего времени
## FAQ
Имя модели - **FAQ**
Таблица БД - **faq**
Поля модели:
- **question*** - вопрос, тип CharField, длина 120
- **answer*** - ответ на вопрос, тип CharField, длина 255
- **user_created*** - пользователь создавший ответ, тип ForeignKey (один ко многим) к модели **User**
- **user_modified*** - пользователь последний редактировавший ответ, тип ForeignKey (один ко многим) к модели **User**
- **created_at*** - дата создания записи об ответе на вопрос, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи об ответе на вопрос, тип DateTimeField, сделать автоматическое проставление текущего времени
## Курсы <-> FAQ (многие-ко-многим)
Имя модели - **CourseFAQ**
Таблица БД - **courses_faq**
Поля модели:
- **course*** - тип ForeignKey к модели курса **Course**
- **faq*** - тип ForeignKey к модели часто задаваемых вопросов **FAQ**
- **created_at*** - дата создания записи о связи курс - ответ на вопрос, тип DateTimeField, сделать автоматическое проставление текущего времени
## Глава
Имя модели - **Chapter**
Таблица БД - **capters**
Поля модели:
- **title** - название главы, тип CharField, длина 120 _(необязательно, если уроки не делятся на главы)_
- **lessons** - тип ManyToManyField (многие ко многим) к модели **Lesson** (Урок), через модель **СhapterLesson**
- **user_created*** - пользователь создавший главу, тип ForeignKey (один ко многим) к модели **User**
- **user_modified*** - пользователь последний редактировавший главу, тип ForeignKey (один ко многим) к модели **User**
- **created_at*** - дата создания записи о главе, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи о главе, тип DateTimeField, сделать автоматическое проставление текущего времени
Примечание: _поле **lessons** тип ManyToManyField имеет смысл, только если предполагается, что одни и те же уроки
могут быть привязаны к разным курсам (главам в рамках курса). Например, какие-либо вводные уроки или правила безопасности.
Если нет, то достаточно будет типа ForeignKey от каждого урока к главе (один-ко-многим)_
## Курсы <-> Главы (многие-ко-многим)
Имя модели - **CourseChapter**
Таблица БД - **courses_chapters**
Поля модели:
- **course*** - тип ForeignKey к модели курса **Course**
- **chapter*** - тип ForeignKey к модели главы урока **Chapter**
- **order_number*** - порядковый номер главы, тип SmallIntegerField, проверка нижнего дипазона >= 1
- **created_at*** - дата создания записи о связи курс - глава урока, тип DateTimeField, сделать автоматическое проставление текущего времени
Создать уникальный индекс (UniqueConstraint) по полям [course, chapter, order_number] чтобы конкретная глава в рамках курса имела уникальный порядковый номер.
_Требует уточнения, так как в случае множественного изменения порядка глав в курсе изменения необходимо будет проводить в одной транзакции с отключенным контролем конфиликтов до завершения транзакции_
## Уроки
Имя модели - **Lesson**
Таблица БД - **lessons**
Поля модели:
- **title*** - название урока, тип CharField, длина 120
- **description** - описание урока, тип TextField (_требует уточнения: как хранить форматированный текст в БД и в каком виде отдавать в frontend_)
- **type*** - тип урока, тип CharField, длина 20, выбор из перечня Урок, Видеоурок, Вебинар, Тест (Квиз)
- **tags** - ключевые слова урока, тип CharField, длина 255
- **duration*** - продолжительность урока, минут, тип SmallIntegerField
- **user_created*** - пользователь создавший урок, тип ForeignKey (один ко многим) к модели **User**
- **user_modified*** - последний редактировавший урок, тип ForeignKey (один ко многим) к модели **User**
- **ready*** - статус готовности готов/не готов, тип BooleanField
- **visible*** - статус видимости урока вкл./выкл., тип BooleanField
- **published*** - статус публикации урока, опубликован - да/нет,тип BooleanField
- **additional*** - дополнительный урок - да/нет, тип BooleanField
- **diploma*** - дипломный урок - да/нет, тип BooleanField
- **created_at*** - дата создания записи об уроке, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи об уроке, тип DateTimeField, сделать автоматическое проставление текущего времени
## Главы <-> Уроки (многие-ко-многим)
Имя модели - **ChapterLesson**
Таблица БД - **chapters_lessons**
Поля модели:
- **chapter*** - тип ForeignKey к модели главы урока **Chapter**
- **lesson*** - тип ForeignKey к модели курса **Lesson**
- **order_number*** - порядковый номер урока в главе, тип SmallIntegerField, проверка нижнего дипазона >= 1
- **created_at*** - дата создания записи о связи курс - глава урока, тип DateTimeField, сделать автоматическое проставление текущего времени
Создать уникальный индекс (UniqueConstraint) по полям [chapter, lesson, order_number] чтобы конкретный урок в рамках главы имел уникальный порядковый номер. Необходимо для сортировки.
_Требует уточнения, так как в случае множественного изменения порядка уроков в главе или добавления урока между имеющимися уроками изменения необходимо будет проводить по всем урокам в одной транзакции с отключенным контролем конфиликтов до завершения транзакции_
## Видеоурок
Имя модели - **VideoLesson**
Таблица БД - **video_lessons**
Поля модели:
- **lesson*** - урок с описанием, тип ForeignKey (один-ко-многим) к модели урока **Lesson**
- **video*** - путь к файлу с видео, тип FileField (_требует уточнения_)
- **user_created*** - пользователь создавший видеоурок (загрузивший видео), тип ForeignKey (один ко многим) к модели **User**
- **user_modified*** - последний редактировавший видеоурок (обновивший видео), тип ForeignKey (один ко многим) к модели **User**
- **created_at*** - дата создания записи о видеоуроке, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи о видеоуроке, тип DateTimeField, сделать автоматическое проставление текущего времени
## Вебинар
Имя модели - **Webinar**
Таблица БД - **webinars**
Поля модели:
- **lesson*** - урок с описанием, тип ForeignKey (один-ко-многим) к модели урока **Lesson**
- **url_webinar*** - ссылка на вебинар, тип URLField
- **start_date*** - дата и время начала вебинара, тип DateTimeField
- **user_created*** - пользователь создавший ссылку на вебинар, тип ForeignKey (один ко многим) к модели **User**
- **user_modified*** - последний редактировавший ссылку на вебинар, тип ForeignKey (один ко многим) к модели **User**
- **created_at*** - дата создания записи о ссылке на вебинар, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи о ссылке на вебинар, тип DateTimeField, сделать автоматическое проставление текущего времени
## Волонтер <-> Урок (многие-ко-многим)
Имя модели - **VolunteerLesson**
Таблица БД - **volunteers_lessons**
Поля модели:
- **volunteer*** - тип ForeignKey к модели волонтера **Volunteer**
- **lesson*** - тип ForeignKey к модели урока **Lesson**
- **passed*** - статус прохождения урока, тип BooleanField, по умолчанию False
- **start_date** - дата начала (первое отрытие урока), тип DateTimeField
- **end_date** - дата завершения урока (переход к следующему уроку или тесту), тип DateTimeField
- **created_at*** - дата создания записи о связи волонтер - урок, тип DateTimeField, сделать автоматическое проставление текущего времени
- **updated_at*** - дата обновления записи о связи волонтер - урок, тип DateTimeField, сделать автоматическое проставление текущего времени
Создать уникальный индекс (UniqueConstraint) по полям [volunteer, lesson, passed] чтобы конкретный урок для пользователя имел уникальный статус прохождения.
При активации курса с помощью метода модели **VolunteerCourse** инициировать создание записей в БД по всем урокам курса с заполнением поля passed значением по умолчанию.