diff --git a/src/backend/apps/project/__init__.py b/src/backend/apps/project/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/backend/apps/project/admin.py b/src/backend/apps/project/admin.py new file mode 100644 index 0000000..3e21395 --- /dev/null +++ b/src/backend/apps/project/admin.py @@ -0,0 +1,54 @@ +from django.contrib import admin + +from .constants import LIST_PER_PAGE +from .models import Level, Project, Skill, Specialist, Specialization, Status + + +@admin.register(Skill) +class SkillAdmin(admin.ModelAdmin): + list_display = ("name",) + search_fields = ("name",) + + +@admin.register(Status) +class StatusAdmin(admin.ModelAdmin): + list_display = ("name",) + search_fields = ("name",) + + +@admin.register(Level) +class LevelAdmin(admin.ModelAdmin): + list_display = ("name",) + search_fields = ("name",) + + +@admin.register(Specialist) +class SpecialistAdmin(admin.ModelAdmin): + list_display = ("name", "specialization") + list_filter = ("specialization",) + search_fields = ("name", "specialization__name") + + +@admin.register(Specialization) +class SpecializationAdmin(admin.ModelAdmin): + list_display = ("name",) + search_fields = ("name",) + + +@admin.register(Project) +class ProjectAdmin(admin.ModelAdmin): + list_display = ( + "name", + "creator", + "started", + "ended", + "contacts", + "level", + "busyness", + "recruitment_status", + "status", + "direction", + ) + list_filter = ("level", "busyness", "status") + search_fields = ("name", "description", "purpose", "creator__username") + list_per_page = LIST_PER_PAGE diff --git a/src/backend/apps/project/apps.py b/src/backend/apps/project/apps.py new file mode 100644 index 0000000..7fe3abc --- /dev/null +++ b/src/backend/apps/project/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ProjectConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "apps.project" diff --git a/src/backend/apps/project/constants.py b/src/backend/apps/project/constants.py new file mode 100644 index 0000000..77309e2 --- /dev/null +++ b/src/backend/apps/project/constants.py @@ -0,0 +1,19 @@ +BUSYNESS_CHOICES = [ + (1, "10"), + (2, "20"), + (3, "30"), + (4, "40"), +] +CONTACTS_LENGTH = 256 +DIRECTION_CHOICES = [ + (1, "Десктоп"), + (2, "Веб"), + (3, "Мобильная"), +] +DESCRIPTION_LENGTH = 3000 +LIST_PER_PAGE = 10 +NAME_LENGTH = 100 +STATUS_CHOICES = [ + (1, "Набор открыт"), + (2, "Набор закрыт"), +] diff --git a/src/backend/apps/project/migrations/0001_initial.py b/src/backend/apps/project/migrations/0001_initial.py new file mode 100644 index 0000000..2acda21 --- /dev/null +++ b/src/backend/apps/project/migrations/0001_initial.py @@ -0,0 +1,243 @@ +# Generated by Django 5.0.1 on 2024-02-05 12:04 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Level", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=100, verbose_name="Название"), + ), + ], + options={ + "verbose_name": "Уровень", + "verbose_name_plural": "Уровни", + }, + ), + migrations.CreateModel( + name="Skill", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=100, verbose_name="Название"), + ), + ], + options={ + "verbose_name": "Навык", + "verbose_name_plural": "Навыки", + }, + ), + migrations.CreateModel( + name="Specialization", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=100, verbose_name="Название"), + ), + ("quantity", models.PositiveSmallIntegerField()), + ], + options={ + "verbose_name": "Специальность", + "verbose_name_plural": "Специальности", + }, + ), + migrations.CreateModel( + name="Status", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=100, verbose_name="Название"), + ), + ], + options={ + "verbose_name": "Статус", + "verbose_name_plural": "Статусы", + }, + ), + migrations.CreateModel( + name="Specialist", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=100, verbose_name="Название"), + ), + ( + "specialization", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="specialists", + to="project.specialization", + verbose_name="Специализация", + ), + ), + ], + options={ + "verbose_name": "Специальность", + "verbose_name_plural": "Специальности", + }, + ), + migrations.CreateModel( + name="Project", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created", models.DateTimeField(auto_now_add=True)), + ("modified", models.DateTimeField(auto_now=True)), + ( + "name", + models.CharField( + max_length=100, verbose_name="Название проекта" + ), + ), + ( + "description", + models.TextField( + max_length=3000, verbose_name="Описание проекта" + ), + ), + ( + "purpose", + models.CharField( + max_length=100, verbose_name="Цель проекта" + ), + ), + ( + "started", + models.DateField( + blank=True, + null=True, + verbose_name="Дата начала проекта", + ), + ), + ( + "ended", + models.DateField( + blank=True, + null=True, + verbose_name="Дата окончания проекта", + ), + ), + ( + "busyness", + models.CharField( + choices=[ + ("full_time", "Полная занятость"), + ("part_time", "Частичная занятость"), + ], + max_length=20, + verbose_name="Занятость", + ), + ), + ( + "creator", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="projects", + to=settings.AUTH_USER_MODEL, + verbose_name="Организатор", + ), + ), + ( + "level", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="project.level", + verbose_name="Уровень", + ), + ), + ( + "skills", + models.ManyToManyField( + related_name="projects", + to="project.skill", + verbose_name="Навыки", + ), + ), + ( + "specialists", + models.ManyToManyField( + related_name="projects", + to="project.specialist", + verbose_name="Специалисты", + ), + ), + ( + "status", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="project.status", + verbose_name="Статус", + ), + ), + ], + options={ + "verbose_name": "Проект", + "verbose_name_plural": "Проекты", + "ordering": ("-created",), + }, + ), + ] diff --git a/src/backend/apps/project/migrations/0002_alter_specialist_options_alter_project_busyness.py b/src/backend/apps/project/migrations/0002_alter_specialist_options_alter_project_busyness.py new file mode 100644 index 0000000..dad2b47 --- /dev/null +++ b/src/backend/apps/project/migrations/0002_alter_specialist_options_alter_project_busyness.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.1 on 2024-02-07 14:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("project", "0001_initial"), + ] + + operations = [ + migrations.AlterModelOptions( + name="specialist", + options={ + "verbose_name": "Специалист", + "verbose_name_plural": "Специалисты", + }, + ), + migrations.AlterField( + model_name="project", + name="busyness", + field=models.IntegerField( + choices=[(1, "Полная занятость"), (2, "Частичная занятость")], + verbose_name="Занятость", + ), + ), + ] diff --git a/src/backend/apps/project/migrations/0003_project_recruitment_status_alter_project_status.py b/src/backend/apps/project/migrations/0003_project_recruitment_status_alter_project_status.py new file mode 100644 index 0000000..b5317b0 --- /dev/null +++ b/src/backend/apps/project/migrations/0003_project_recruitment_status_alter_project_status.py @@ -0,0 +1,31 @@ +# Generated by Django 5.0.1 on 2024-02-07 15:28 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("project", "0002_alter_specialist_options_alter_project_busyness"), + ] + + operations = [ + migrations.AddField( + model_name="project", + name="recruitment_status", + field=models.IntegerField( + choices=[(1, "Набор открыт"), (2, "Набор закрыт")], + default=1, + verbose_name="Статус набора участников", + ), + ), + migrations.AlterField( + model_name="project", + name="status", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="project.status", + verbose_name="Статус проекта", + ), + ), + ] diff --git a/src/backend/apps/project/migrations/0004_alter_project_busyness.py b/src/backend/apps/project/migrations/0004_alter_project_busyness.py new file mode 100644 index 0000000..d29ed9a --- /dev/null +++ b/src/backend/apps/project/migrations/0004_alter_project_busyness.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0.1 on 2024-02-07 15:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("project", "0003_project_recruitment_status_alter_project_status"), + ] + + operations = [ + migrations.AlterField( + model_name="project", + name="busyness", + field=models.IntegerField( + verbose_name="Занятость в часах в неделю" + ), + ), + ] diff --git a/src/backend/apps/project/migrations/0005_alter_specialization_options_and_more.py b/src/backend/apps/project/migrations/0005_alter_specialization_options_and_more.py new file mode 100644 index 0000000..06711df --- /dev/null +++ b/src/backend/apps/project/migrations/0005_alter_specialization_options_and_more.py @@ -0,0 +1,60 @@ +# Generated by Django 5.0.1 on 2024-02-12 13:23 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("project", "0004_alter_project_busyness"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterModelOptions( + name="specialization", + options={ + "verbose_name": "Специализация", + "verbose_name_plural": "Специализации", + }, + ), + migrations.RemoveField( + model_name="specialization", + name="quantity", + ), + migrations.AddField( + model_name="project", + name="contacts", + field=models.TextField( + default="", max_length=256, verbose_name="Контакты для связи" + ), + preserve_default=False, + ), + migrations.AddField( + model_name="project", + name="direction", + field=models.IntegerField( + choices=[(1, "Десктоп"), (2, "Веб"), (3, "Мобильная")], + default=1, + verbose_name="Направление разработки", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="project", + name="participants", + field=models.ManyToManyField( + related_name="project_participants", + to=settings.AUTH_USER_MODEL, + verbose_name="Команда проекта", + ), + ), + migrations.AlterField( + model_name="project", + name="busyness", + field=models.IntegerField( + choices=[(1, "10"), (2, "20"), (3, "30"), (4, "40")], + verbose_name="Занятость в часах в неделю", + ), + ), + ] diff --git a/src/backend/apps/project/migrations/__init__.py b/src/backend/apps/project/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/backend/apps/project/models.py b/src/backend/apps/project/models.py new file mode 100644 index 0000000..2d8a9ba --- /dev/null +++ b/src/backend/apps/project/models.py @@ -0,0 +1,165 @@ +from django.contrib.auth import get_user_model +from django.db import models + +from apps.general.models import CreatedModifiedFields + +from .constants import ( + BUSYNESS_CHOICES, + CONTACTS_LENGTH, + DESCRIPTION_LENGTH, + DIRECTION_CHOICES, + NAME_LENGTH, + STATUS_CHOICES, +) + +User = get_user_model() + + +class Specialization(models.Model): + """ + Модель представляющая специализацию, подразделяется на специальности. + """ + + name = models.CharField("Название", max_length=NAME_LENGTH) + + class Meta: + verbose_name = "Специализация" + verbose_name_plural = "Специализации" + + def __str__(self) -> str: + return self.name + + +class Specialist(models.Model): + """ + Модель представляющая специальность(специалиста), входящую в специализацию. + """ + + specialization = models.ForeignKey( + Specialization, + on_delete=models.CASCADE, + related_name="specialists", + verbose_name="Специализация", + ) + name = models.CharField("Название", max_length=NAME_LENGTH) + + class Meta: + verbose_name = "Специалист" + verbose_name_plural = "Специалисты" + + def __str__(self) -> str: + return self.name + + +class Level(models.Model): + """ + Модель представляющая уровень участников. + """ + + name = models.CharField("Название", max_length=NAME_LENGTH) + + class Meta: + verbose_name = "Уровень" + verbose_name_plural = "Уровни" + + def __str__(self) -> str: + return self.name + + +class Status(models.Model): + """ + Модель представляющая статус проекта. + """ + + name = models.CharField("Название", max_length=NAME_LENGTH) + + class Meta: + verbose_name = "Статус" + verbose_name_plural = "Статусы" + + def __str__(self) -> str: + return self.name + + +class Skill(models.Model): + """ + Модель представляющая необходимые для проекта навыки. + """ + + name = models.CharField("Название", max_length=NAME_LENGTH) + + class Meta: + verbose_name = "Навык" + verbose_name_plural = "Навыки" + + def __str__(self) -> str: + return self.name + + +class Project(CreatedModifiedFields): + """ + Модель представляющая проект. + """ + + name = models.CharField("Название проекта", max_length=NAME_LENGTH) + description = models.TextField( + "Описание проекта", max_length=DESCRIPTION_LENGTH + ) + purpose = models.CharField("Цель проекта", max_length=NAME_LENGTH) + creator = models.ForeignKey( + User, + on_delete=models.CASCADE, + related_name="projects", + verbose_name="Организатор", + ) + started = models.DateField("Дата начала проекта", null=True, blank=True) + ended = models.DateField("Дата окончания проекта", null=True, blank=True) + specialists = models.ManyToManyField( + Specialist, + related_name="projects", + verbose_name="Специалисты", + ) + level = models.ForeignKey( + Level, + on_delete=models.CASCADE, + verbose_name="Уровень", + ) + skills = models.ManyToManyField( + Skill, + related_name="projects", + verbose_name="Навыки", + ) + busyness = models.IntegerField( + choices=BUSYNESS_CHOICES, + verbose_name="Занятость в часах в неделю", + ) + recruitment_status = models.IntegerField( + choices=STATUS_CHOICES, + verbose_name="Статус набора участников", + default=1, + ) + status = models.ForeignKey( + Status, + on_delete=models.CASCADE, + verbose_name="Статус проекта", + ) + contacts = models.TextField( + "Контакты для связи", max_length=CONTACTS_LENGTH + ) + direction = models.IntegerField( + choices=DIRECTION_CHOICES, + verbose_name="Направление разработки", + ) + participants = models.ManyToManyField( + User, + related_name="project_participants", + verbose_name="Команда проекта", + ) + + class Meta: + verbose_name = "Проект" + verbose_name_plural = "Проекты" + ordering = ("-created",) + + def __str__(self) -> str: + return self.name diff --git a/src/backend/config/settings/base.py b/src/backend/config/settings/base.py index de345bb..aea6d49 100644 --- a/src/backend/config/settings/base.py +++ b/src/backend/config/settings/base.py @@ -29,8 +29,9 @@ "rest_framework", ] -LOCAL_APPS = [ +LOCAL_APPS: list = [ "apps.general", + "apps.project", ] INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS