Skip to content

Commit d120787

Browse files
authored
Merge pull request #564 from PROCOLLAB-github/dev
Dev
2 parents d772ab1 + d90c3b8 commit d120787

42 files changed

Lines changed: 2187 additions & 274 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/serializers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
from .models import SkillToObject, SkillCategory, Skill
44

55

6+
class EmptySerializer(serializers.Serializer):
7+
pass
8+
9+
610
class SetLikedSerializer(serializers.Serializer):
711
is_liked = serializers.BooleanField()
812

docker-compose.dev-ci.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ services:
1313
- .env
1414
environment:
1515
HOST: 0.0.0.0
16-
expose:
17-
- 8000
16+
ports:
17+
- "127.0.0.1:8000:8000"
1818

1919
grafana:
2020
image: grafana/grafana:latest
@@ -37,13 +37,13 @@ services:
3737
- prom-data:/prometheus
3838
- ./prometheus:/etc/prometheus
3939

40-
nginx:
41-
restart: unless-stopped
42-
build: ./nginx
43-
depends_on:
44-
- web
45-
ports:
46-
- 8000:80
40+
#nginx:
41+
# restart: unless-stopped
42+
# build: ./nginx
43+
# depends_on:
44+
# - web
45+
# ports:
46+
# - 8000:80
4747

4848
loki:
4949
image: grafana/loki:2.9.0

feed/views.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
from django.contrib.contenttypes.models import ContentType
2-
from django.db.models import QuerySet, Q
2+
from django.db.models import Q, QuerySet
33
from rest_framework.generics import CreateAPIView
44
from rest_framework.response import Response
55
from rest_framework.views import APIView
66

7+
from core.serializers import EmptySerializer
78
from feed.pagination import FeedPagination
89
from feed.services import get_liked_news
9-
1010
from news.models import News
11-
from .serializers import NewsFeedListSerializer
12-
from projects.models import Project
1311
from partner_programs.models import PartnerProgramUserProfile
12+
from projects.models import Project
1413
from vacancy.models import Vacancy
1514

15+
from .serializers import NewsFeedListSerializer
16+
1617

1718
class NewSimpleFeed(APIView):
1819
serializator_class = NewsFeedListSerializer
@@ -29,11 +30,9 @@ def _get_filter_data(self) -> list[str]:
2930

3031
def _get_excluded_projects_ids(self) -> list[int]:
3132
"""IDs for exclude projects which in Partner Program."""
32-
excluded_projects = (
33-
PartnerProgramUserProfile.objects
34-
.values_list("project_id", flat=True)
35-
.exclude(project_id__isnull=True)
36-
)
33+
excluded_projects = PartnerProgramUserProfile.objects.values_list(
34+
"project_id", flat=True
35+
).exclude(project_id__isnull=True)
3736
return excluded_projects
3837

3938
def get_queryset(self) -> QuerySet[News]:
@@ -80,6 +79,8 @@ def get(self, *args, **kwargs):
8079

8180

8281
class DevScript(CreateAPIView):
82+
serializer_class = EmptySerializer
83+
8384
def create(self, request):
8485
content_type_project = ContentType.objects.filter(model="project").first()
8586
for project in Project.objects.filter(draft=False):

invites/serializers.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from django.apps import apps
12
from rest_framework import serializers
23

34
from invites.models import Invite
5+
from projects.models import Collaborator
46
from projects.serializers import ProjectListSerializer
57
from users.serializers import UserDetailSerializer
68

@@ -18,6 +20,48 @@ class Meta:
1820
"is_accepted",
1921
]
2022

23+
def validate(self, attrs):
24+
project = attrs["project"]
25+
user = attrs["user"]
26+
27+
if project.leader_id == user.id:
28+
raise serializers.ValidationError(
29+
{"user": "Пользователь уже является лидером проекта."}
30+
)
31+
32+
if Collaborator.objects.filter(project=project, user=user).exists():
33+
raise serializers.ValidationError(
34+
{"user": "Пользователь уже состоит в проекте."}
35+
)
36+
37+
if Invite.objects.filter(
38+
project=project, user=user, is_accepted__isnull=True
39+
).exists():
40+
raise serializers.ValidationError(
41+
{"user": "У пользователя уже есть активное приглашение в этот проект."}
42+
)
43+
44+
link = project.program_links.select_related("partner_program").first()
45+
if link:
46+
PartnerProgramUserProfile = apps.get_model(
47+
"partner_programs", "PartnerProgramUserProfile"
48+
)
49+
is_participant = PartnerProgramUserProfile.objects.filter(
50+
user_id=user.id,
51+
partner_program_id=link.partner_program_id,
52+
).exists()
53+
if not is_participant:
54+
raise serializers.ValidationError(
55+
{
56+
"user": (
57+
"Нельзя пригласить пользователя: проект относится к программе, "
58+
"а пользователь не является её участником."
59+
)
60+
}
61+
)
62+
63+
return attrs
64+
2165

2266
class InviteDetailSerializer(serializers.ModelSerializer[Invite]):
2367
user = UserDetailSerializer(many=False, read_only=True)

invites/tests.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ def setUp(self) -> None:
3636
"name": "Test",
3737
"description": "Test",
3838
"industry": Industry.objects.create(name="Test").id,
39-
"step": 1,
4039
"draft": False,
4140
}
4241

news/mapping.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class NewsMapping:
1414
@classmethod
1515
def get_name(cls, content_object) -> str:
1616
if isinstance(content_object, User):
17-
f"{content_object.first_name} {content_object.last_name}"
17+
return f"{content_object.first_name} {content_object.last_name}"
1818
if isinstance(content_object, Project) or isinstance(
1919
content_object, PartnerProgram
2020
):

partner_programs/admin.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import urllib.parse
33

44
import tablib
5+
from django import forms
56
from django.contrib import admin
67
from django.db.models import QuerySet
78
from django.http import HttpRequest, HttpResponse
@@ -37,7 +38,19 @@ class PartnerProgramFieldInline(admin.TabularInline):
3738

3839
@admin.register(PartnerProgram)
3940
class PartnerProgramAdmin(admin.ModelAdmin):
41+
class PartnerProgramAdminForm(forms.ModelForm):
42+
class Meta:
43+
model = PartnerProgram
44+
fields = "__all__"
45+
widgets = {
46+
"name": forms.TextInput(attrs={"size": 80}),
47+
"tag": forms.TextInput(attrs={"size": 80}),
48+
"description": forms.Textarea(attrs={"rows": 4, "cols": 82}),
49+
"city": forms.TextInput(attrs={"size": 80}),
50+
}
51+
4052
inlines = [PartnerProgramMaterialInline, PartnerProgramFieldInline]
53+
form = PartnerProgramAdminForm
4154
list_display = ("id", "name", "tag", "city", "datetime_created")
4255
list_display_links = (
4356
"id",
@@ -53,8 +66,39 @@ class PartnerProgramAdmin(admin.ModelAdmin):
5366
)
5467
list_filter = ("city",)
5568

56-
filter_horizontal = ("users", "managers")
69+
filter_horizontal = ("managers",)
5770
date_hierarchy = "datetime_started"
71+
readonly_fields = ("datetime_created", "datetime_updated")
72+
fieldsets = (
73+
(
74+
None,
75+
{
76+
"fields": (
77+
"name",
78+
"tag",
79+
"description",
80+
"city",
81+
"is_competitive",
82+
"projects_availability",
83+
"draft",
84+
(
85+
"datetime_started",
86+
"datetime_registration_ends",
87+
"datetime_finished",
88+
),
89+
(
90+
"image_address",
91+
"cover_image_address",
92+
"advertisement_image_address",
93+
),
94+
("presentation_address", "registration_link"),
95+
"data_schema",
96+
)
97+
},
98+
),
99+
("Менеджеры программы", {"fields": ("managers",)}),
100+
("Служебная информация", {"fields": ("datetime_created", "datetime_updated")}),
101+
)
58102

59103
def get_queryset(self, request: HttpRequest) -> QuerySet[PartnerProgram]:
60104
qs = super().get_queryset(request)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.24 on 2025-10-31 10:20
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('partner_programs', '0011_partnerprogram_is_competitive_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='partnerprogram',
15+
name='registration_link',
16+
field=models.URLField(blank=True, help_text='Адрес страницы регистрации (например, на Tilda)', null=True, verbose_name='Ссылка на регистрацию'),
17+
),
18+
]

partner_programs/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ class PartnerProgram(models.Model):
7373
blank=True,
7474
verbose_name="Ссылка на презентацию",
7575
)
76+
registration_link = models.URLField(
77+
null=True,
78+
blank=True,
79+
verbose_name="Ссылка на регистрацию",
80+
help_text="Адрес страницы регистрации (например, на Tilda)",
81+
)
7682
data_schema = models.JSONField(
7783
verbose_name="Схема данных в формате JSON",
7884
help_text="Ключи - имена полей, значения - тип поля ввода",

partner_programs/serializers.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class Meta:
4545
"name",
4646
"image_address",
4747
"short_description",
48+
"registration_link",
4849
"datetime_registration_ends",
4950
"datetime_started",
5051
"datetime_finished",
@@ -80,9 +81,7 @@ class PartnerProgramForMemberSerializer(PartnerProgramBaseSerializerMixin):
8081

8182
views_count = serializers.SerializerMethodField(method_name="count_views")
8283
links = serializers.SerializerMethodField(method_name="get_links")
83-
is_user_manager = serializers.SerializerMethodField(
84-
method_name="get_is_user_manager"
85-
)
84+
is_user_manager = serializers.SerializerMethodField(method_name="get_is_user_manager")
8685

8786
def count_views(self, program):
8887
return get_views_count(program)
@@ -112,6 +111,7 @@ class Meta:
112111
"image_address",
113112
"cover_image_address",
114113
"presentation_address",
114+
"registration_link",
115115
"views_count",
116116
"datetime_registration_ends",
117117
"is_user_manager",
@@ -133,6 +133,7 @@ class Meta:
133133
"cover_image_address",
134134
"advertisement_image_address",
135135
"presentation_address",
136+
"registration_link",
136137
"datetime_registration_ends",
137138
"is_user_manager",
138139
)

0 commit comments

Comments
 (0)