Skip to content

Commit 18d8988

Browse files
authored
Merge pull request #525 from PROCOLLAB-github/feature/project-mospolytech-data
Feature/project mospolytech data
2 parents 3c0ff1b + 4668888 commit 18d8988

7 files changed

Lines changed: 81 additions & 45 deletions

File tree

vacancy/admin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class VacancySkillToObjectInline(SkillToObjectInline):
1515
class VacancyAdmin(admin.ModelAdmin):
1616
list_display = [
1717
"role",
18+
"specialization",
1819
"description",
1920
"project",
2021
"is_active",
@@ -24,7 +25,7 @@ class VacancyAdmin(admin.ModelAdmin):
2425
inlines = [
2526
VacancySkillToObjectInline,
2627
]
27-
readonly_fields = ('datetime_closed',)
28+
readonly_fields = ("datetime_closed",)
2829
list_display_links = ["role"]
2930

3031
change_list_template = "vacancies/vacancies_change_list.html"

vacancy/filters.py

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -47,50 +47,52 @@ def __init__(self, *args, **kwargs):
4747
self.data = dict(self.data)
4848
self.data["is_active"] = True
4949

50-
def filter_by_experience(self, queryset: QuerySet[Vacancy], name, value: list[str]) -> QuerySet[Vacancy]:
51-
return (
52-
queryset
53-
.filter(Q(required_experience__in=value) | Q(required_experience=None))
54-
.order_by(F("required_experience").asc(nulls_last=True))
50+
def filter_by_experience(
51+
self, queryset: QuerySet[Vacancy], name, value: list[str]
52+
) -> QuerySet[Vacancy]:
53+
return queryset.filter(
54+
Q(required_experience__in=value) | Q(required_experience=None)
55+
).order_by(F("required_experience").asc(nulls_last=True))
56+
57+
def filter_by_schedule(
58+
self, queryset: QuerySet[Vacancy], name, value: list[str]
59+
) -> QuerySet[Vacancy]:
60+
return queryset.filter(
61+
Q(work_schedule__in=value) | Q(work_schedule=None)
62+
).order_by(F("work_schedule").asc(nulls_last=True))
63+
64+
def filter_by_format(
65+
self, queryset: QuerySet[Vacancy], name, value: list[str]
66+
) -> QuerySet[Vacancy]:
67+
return queryset.filter(Q(work_format__in=value) | Q(work_format=None)).order_by(
68+
F("work_format").asc(nulls_last=True)
5569
)
5670

57-
def filter_by_schedule(self, queryset: QuerySet[Vacancy], name, value: list[str]) -> QuerySet[Vacancy]:
58-
return (
59-
queryset
60-
.filter(Q(work_schedule__in=value) | Q(work_schedule=None))
61-
.order_by(F("work_schedule").asc(nulls_last=True))
62-
)
63-
64-
def filter_by_format(self, queryset: QuerySet[Vacancy], name, value: list[str]) -> QuerySet[Vacancy]:
65-
return (
66-
queryset
67-
.filter(Q(work_format__in=value) | Q(work_format=None))
68-
.order_by(F("work_format").asc(nulls_last=True))
69-
)
70-
71-
def filter_by_salary_min(self, queryset: QuerySet[Vacancy], name, value: list[str]) -> QuerySet[Vacancy]:
71+
def filter_by_salary_min(
72+
self, queryset: QuerySet[Vacancy], name, value: list[str]
73+
) -> QuerySet[Vacancy]:
7274
try:
7375
min_salary = int(value[0])
74-
return (
75-
queryset
76-
.filter(Q(salary__gte=min_salary) | Q(salary=None))
77-
.order_by(F("salary").asc(nulls_last=True))
76+
return queryset.filter(Q(salary__gte=min_salary) | Q(salary=None)).order_by(
77+
F("salary").asc(nulls_last=True)
7878
)
7979
except ValueError:
8080
return queryset
8181

82-
def filter_by_salary_max(self, queryset: QuerySet[Vacancy], name, value: list[str]) -> QuerySet[Vacancy]:
82+
def filter_by_salary_max(
83+
self, queryset: QuerySet[Vacancy], name, value: list[str]
84+
) -> QuerySet[Vacancy]:
8385
try:
8486
max_salary = int(value[0])
85-
return (
86-
queryset
87-
.filter(Q(salary__lte=max_salary) | Q(salary=None))
88-
.order_by(F("salary").asc(nulls_last=True))
87+
return queryset.filter(Q(salary__lte=max_salary) | Q(salary=None)).order_by(
88+
F("salary").asc(nulls_last=True)
8989
)
9090
except ValueError:
9191
return queryset
9292

93-
def filter_by_role(self, queryset: QuerySet[Vacancy], name, value: list[str]) -> QuerySet[Vacancy]:
93+
def filter_by_role(
94+
self, queryset: QuerySet[Vacancy], name, value: list[str]
95+
) -> QuerySet[Vacancy]:
9496
if not value:
9597
return queryset
9698
return queryset.filter(role__icontains=value[0])
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 4.2.11 on 2025-07-10 06:51
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("vacancy", "0008_vacancy_salary_alter_vacancy_required_experience_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="vacancy",
15+
name="specialization",
16+
field=models.CharField(
17+
blank=True, max_length=128, null=True, verbose_name="Специализация"
18+
),
19+
),
20+
]

vacancy/models.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
from django.contrib.contenttypes.fields import GenericRelation
2-
from django.db import models
32
from django.core.validators import MinValueValidator
3+
from django.db import models
44
from django.utils import timezone
5+
from django_stubs_ext.db.models import TypedModelMeta
56

67
from files.models import UserFile
78
from projects.models import Project
8-
from vacancy.constants import WorkExperience, WorkSchedule, WorkFormat
9+
from vacancy.constants import WorkExperience, WorkFormat, WorkSchedule
910
from vacancy.managers import VacancyManager, VacancyResponseManager
10-
from django_stubs_ext.db.models import TypedModelMeta
1111

1212

1313
class Vacancy(models.Model):
@@ -28,6 +28,9 @@ class Vacancy(models.Model):
2828
"""
2929

3030
role = models.CharField(max_length=256, null=False)
31+
specialization = models.CharField(
32+
max_length=128, null=True, blank=True, verbose_name="Специализация"
33+
)
3134
required_skills = GenericRelation(
3235
"core.SkillToObject",
3336
related_query_name="vacancies",

vacancy/serializers.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from projects.models import Project
1111
from projects.validators import validate_project
1212
from users.serializers import UserDetailSerializer
13-
from vacancy.constants import WorkExperience, WorkSchedule, WorkFormat
13+
from vacancy.constants import WorkExperience, WorkFormat, WorkSchedule
1414
from vacancy.models import Vacancy, VacancyResponse
1515

1616
User = get_user_model()
@@ -51,8 +51,12 @@ def validate_work_format(self, value):
5151

5252
def to_representation(self, instance):
5353
representation = super().to_representation(instance)
54-
representation["required_experience"] = WorkExperience.to_display(instance.required_experience)
55-
representation["work_schedule"] = WorkSchedule.to_display(instance.work_schedule)
54+
representation["required_experience"] = WorkExperience.to_display(
55+
instance.required_experience
56+
)
57+
representation["work_schedule"] = WorkSchedule.to_display(
58+
instance.work_schedule
59+
)
5660
representation["work_format"] = WorkFormat.to_display(instance.work_format)
5761
return representation
5862

@@ -112,6 +116,7 @@ class Meta:
112116
fields = [
113117
"id",
114118
"role",
119+
"specialization",
115120
"required_skills",
116121
"required_skills_ids",
117122
"description",
@@ -139,6 +144,7 @@ class Meta:
139144
fields = [
140145
"id",
141146
"role",
147+
"specialization",
142148
"required_skills",
143149
"description",
144150
"is_active",
@@ -192,7 +198,6 @@ class ProjectVacancyCreateListSerializer(
192198
AbstractVacancyEnumFields,
193199
RequiredSkillsWriteSerializerMixin[Vacancy],
194200
):
195-
196201
def create(self, validated_data):
197202
project = validated_data["project"]
198203
if project.leader != self.context["request"].user:
@@ -233,6 +238,7 @@ class Meta:
233238
fields = [
234239
"id",
235240
"role",
241+
"specialization",
236242
"required_skills",
237243
"required_skills_ids",
238244
"description",

vacancy/tasks.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import datetime
22

3-
from mailing.typing import EmailDataToPrepare, ContextDataDict, MailDataDict
4-
from mailing.utils import send_mass_mail, prepare_mail_data
3+
from mailing.typing import ContextDataDict, EmailDataToPrepare, MailDataDict
4+
from mailing.utils import prepare_mail_data, send_mass_mail
55
from procollab.celery import app
66
from vacancy.mapping import (
77
CeleryEmailParams,
8+
EmailParamsType,
9+
MessageTypeEnum,
810
create_text_for_email,
9-
message_type_to_button_text,
1011
get_link,
11-
MessageTypeEnum,
12+
message_type_to_button_text,
1213
message_type_to_title,
13-
EmailParamsType,
1414
)
1515
from vacancy.models import Vacancy
1616

vacancy/views.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ def put(self, request, *args, **kwargs):
7070
return self.update(request, *args, **kwargs)
7171

7272

73-
class VacancyResponseList(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView):
73+
class VacancyResponseList(
74+
mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView
75+
):
7476
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
7577
serializer_class = VacancyResponseListSerializer
7678

@@ -145,7 +147,9 @@ def post(self, request, pk):
145147
role_add_as: str = vacancy.role
146148

147149
# check if this person already has a collaborator role in this project
148-
if Collaborator.objects.filter(project=project_add_in, user=user_to_add).exists():
150+
if Collaborator.objects.filter(
151+
project=project_add_in, user=user_to_add
152+
).exists():
149153
return Response(
150154
"You already work for this project, you can't accept a vacancy here",
151155
status=status.HTTP_400_BAD_REQUEST,

0 commit comments

Comments
 (0)