Skip to content

Commit f5a9bf9

Browse files
authored
Merge pull request #169 from PROCOLLAB-github/feature/partner-programs
Привязка проектов к партнерским программам
2 parents 3e34174 + af8d24b commit f5a9bf9

8 files changed

Lines changed: 139 additions & 7 deletions

File tree

partner_programs/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class PartnerProgramUserProfileAdmin(admin.ModelAdmin):
4747
list_filter = ("partner_program",)
4848
raw_id_fields = (
4949
"user",
50+
"project",
5051
"partner_program",
5152
)
5253
date_hierarchy = "datetime_created"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Generated by Django 4.2.3 on 2023-07-18 20:25
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12+
("projects", "0019_alter_project_options_project_hidden_score"),
13+
("partner_programs", "0002_alter_partnerprogram_options_and_more"),
14+
]
15+
16+
operations = [
17+
migrations.AddField(
18+
model_name="partnerprogramuserprofile",
19+
name="project",
20+
field=models.ForeignKey(
21+
null=True,
22+
on_delete=django.db.models.deletion.SET_NULL,
23+
related_name="partner_program_profiles",
24+
to="projects.project",
25+
),
26+
),
27+
migrations.AlterField(
28+
model_name="partnerprogramuserprofile",
29+
name="partner_program",
30+
field=models.ForeignKey(
31+
on_delete=django.db.models.deletion.CASCADE,
32+
related_name="partner_program_profiles",
33+
to="partner_programs.partnerprogram",
34+
),
35+
),
36+
migrations.AlterField(
37+
model_name="partnerprogramuserprofile",
38+
name="user",
39+
field=models.ForeignKey(
40+
null=True,
41+
on_delete=django.db.models.deletion.SET_NULL,
42+
related_name="partner_program_profiles",
43+
to=settings.AUTH_USER_MODEL,
44+
),
45+
),
46+
]

partner_programs/models.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from django.db import models
33

44
from partner_programs.constants import get_default_data_schema
5+
from projects.models import Project
56

67
User = get_user_model()
78

@@ -107,14 +108,22 @@ class PartnerProgramUserProfile(models.Model):
107108
"""
108109

109110
user = models.ForeignKey(
110-
User, on_delete=models.CASCADE, related_name="partner_program_profile"
111+
User,
112+
on_delete=models.SET_NULL,
113+
related_name="partner_program_profiles",
114+
null=True,
115+
)
116+
project = models.ForeignKey(
117+
Project,
118+
on_delete=models.SET_NULL,
119+
related_name="partner_program_profiles",
120+
null=True,
111121
)
112122
partner_program = models.ForeignKey(
113123
PartnerProgram,
114124
on_delete=models.CASCADE,
115-
related_name="partner_program_user_profile",
125+
related_name="partner_program_profiles",
116126
)
117-
# TODO: add amount of projects of this Program that user created
118127
partner_program_data = models.JSONField()
119128
datetime_created = models.DateTimeField(auto_now_add=True)
120129
datetime_updated = models.DateTimeField(auto_now=True)
@@ -128,6 +137,4 @@ class Meta:
128137
)
129138

130139
def __str__(self):
131-
return (
132-
f"PartnerProgramUserProfile<{self.pk}> - {self.user} {self.partner_program}"
133-
)
140+
return f"PartnerProgramUserProfile<{self.pk}> - {self.user} {self.project} {self.partner_program}"

partner_programs/serializers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,13 @@ class PartnerProgramUserSerializer(serializers.Serializer):
126126

127127
class PartnerProgramDataSchemaSerializer(serializers.Serializer):
128128
data_schema = serializers.JSONField(required=True)
129+
130+
131+
class UserProgramsSerializer(serializers.ModelSerializer):
132+
class Meta:
133+
model = PartnerProgram
134+
fields = [
135+
"id",
136+
"name",
137+
"tag",
138+
]

projects/helpers.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from django.contrib.auth import get_user_model
44

5+
from partner_programs.models import PartnerProgram, PartnerProgramUserProfile
56
from projects.constants import RECOMMENDATIONS_COUNT
67
from projects.models import Project, ProjectLink, Achievement
78

@@ -84,3 +85,16 @@ def update_links(links, pk):
8485
for link in links
8586
]
8687
)
88+
89+
90+
def update_partner_program(
91+
partner_program_id: int, user: "User", instance: "Project"
92+
) -> None:
93+
if partner_program_id:
94+
partner_program = PartnerProgram.objects.get(pk=partner_program_id)
95+
partner_program_profile = PartnerProgramUserProfile.objects.get(
96+
user=user,
97+
partner_program=partner_program,
98+
)
99+
partner_program_profile.project = instance
100+
partner_program_profile.save()

projects/views.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99
from core.permissions import IsStaffOrReadOnly
1010
from core.serializers import SetLikedSerializer
1111
from core.services import add_view, set_like
12+
from partner_programs.models import PartnerProgram, PartnerProgramUserProfile
1213
from projects.filters import ProjectFilter
1314
from projects.constants import VERBOSE_STEPS
14-
from projects.helpers import get_recommended_users, check_related_fields_update
15+
from projects.helpers import (
16+
get_recommended_users,
17+
check_related_fields_update,
18+
update_partner_program,
19+
)
1520
from projects.models import Project, Achievement, ProjectNews
1621
from projects.pagination import ProjectNewsPagination
1722
from projects.permissions import (
@@ -51,6 +56,21 @@ def create(self, request, *args, **kwargs):
5156
serializer.validated_data["leader"] = request.user
5257

5358
self.perform_create(serializer)
59+
60+
try:
61+
partner_program_id = request.data.get("partner_program_id")
62+
update_partner_program(partner_program_id, request.user, serializer.instance)
63+
except PartnerProgram.DoesNotExist:
64+
return Response(
65+
{"detail": "Partner program with this id does not exist"},
66+
status=status.HTTP_400_BAD_REQUEST,
67+
)
68+
except PartnerProgramUserProfile.DoesNotExist:
69+
return Response(
70+
{"detail": "User is not a member of this partner program"},
71+
status=status.HTTP_400_BAD_REQUEST,
72+
)
73+
5474
headers = self.get_success_headers(serializer.data)
5575
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
5676

@@ -101,6 +121,20 @@ def retrieve(self, request, *args, **kwargs):
101121
return Response(serializer.data)
102122

103123
def put(self, request, pk, **kwargs):
124+
# fixme: add partner_program_id to docs
125+
try:
126+
partner_program_id = request.data.get("partner_program_id")
127+
update_partner_program(partner_program_id, request.user, self.get_object())
128+
except PartnerProgram.DoesNotExist:
129+
return Response(
130+
{"detail": "Partner program with this id does not exist"},
131+
status=status.HTTP_400_BAD_REQUEST,
132+
)
133+
except PartnerProgramUserProfile.DoesNotExist:
134+
return Response(
135+
{"detail": "User is not a member of this partner program"},
136+
status=status.HTTP_400_BAD_REQUEST,
137+
)
104138
check_related_fields_update(request.data, pk)
105139
return super(ProjectDetail, self).put(request, pk)
106140

users/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
RegisteredEventsList,
1919
SetUserOnboardingStage,
2020
ResendVerifyEmail,
21+
CurrentUserPrograms,
2122
)
2223

2324
app_name = "users"
@@ -35,6 +36,7 @@
3536
path("users/<int:pk>/set_onboarding_stage/", SetUserOnboardingStage.as_view()),
3637
path("users/reset-password/", EmailResetPassword.as_view()),
3738
path("users/current/", CurrentUser.as_view()),
39+
path("users/current/programs/", CurrentUserPrograms.as_view()),
3840
path("users/current/events/", RegisteredEventsList.as_view()),
3941
path("users/achievements/", AchievementList.as_view()),
4042
path("users/achievements/<int:pk>/", AchievementDetail.as_view()),

users/views.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
ListCreateAPIView,
1616
RetrieveUpdateDestroyAPIView,
1717
UpdateAPIView,
18+
RetrieveAPIView,
1819
)
1920
from rest_framework.permissions import AllowAny, IsAuthenticated
2021
from rest_framework.request import Request
@@ -25,6 +26,8 @@
2526
from core.permissions import IsOwnerOrReadOnly
2627
from events.models import Event
2728
from events.serializers import EventsListSerializer
29+
from partner_programs.models import PartnerProgram
30+
from partner_programs.serializers import UserProgramsSerializer
2831
from projects.serializers import ProjectListSerializer
2932
from users.helpers import (
3033
reset_email,
@@ -126,6 +129,21 @@ def patch(self, request, pk):
126129
return super().patch(request, pk)
127130

128131

132+
class CurrentUserPrograms(RetrieveAPIView):
133+
queryset = PartnerProgram.objects.all()
134+
permission_classes = [IsAuthenticated]
135+
serializer_class = UserProgramsSerializer
136+
137+
def get(self, request, *args, **kwargs):
138+
user = User.objects.get(id=request.user.id)
139+
# fixme: mb hide finished programs
140+
programs = [
141+
profile.partner_program for profile in user.partner_program_profiles.all()
142+
]
143+
serializer = self.get_serializer(programs, many=True)
144+
return Response(serializer.data, status=status.HTTP_200_OK)
145+
146+
129147
class CurrentUser(GenericAPIView):
130148
queryset = User.objects.get_users_for_detail_view()
131149
permission_classes = [IsAuthenticated]

0 commit comments

Comments
 (0)