diff --git a/partner_programs/admin.py b/partner_programs/admin.py index 3e11346c..4a568238 100644 --- a/partner_programs/admin.py +++ b/partner_programs/admin.py @@ -47,6 +47,7 @@ class PartnerProgramUserProfileAdmin(admin.ModelAdmin): list_filter = ("partner_program",) raw_id_fields = ( "user", + "project", "partner_program", ) date_hierarchy = "datetime_created" diff --git a/partner_programs/migrations/0003_partnerprogramuserprofile_project_and_more.py b/partner_programs/migrations/0003_partnerprogramuserprofile_project_and_more.py new file mode 100644 index 00000000..b09d6a0a --- /dev/null +++ b/partner_programs/migrations/0003_partnerprogramuserprofile_project_and_more.py @@ -0,0 +1,46 @@ +# Generated by Django 4.2.3 on 2023-07-18 20:25 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("projects", "0019_alter_project_options_project_hidden_score"), + ("partner_programs", "0002_alter_partnerprogram_options_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="partnerprogramuserprofile", + name="project", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="partner_program_profiles", + to="projects.project", + ), + ), + migrations.AlterField( + model_name="partnerprogramuserprofile", + name="partner_program", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="partner_program_profiles", + to="partner_programs.partnerprogram", + ), + ), + migrations.AlterField( + model_name="partnerprogramuserprofile", + name="user", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="partner_program_profiles", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/partner_programs/models.py b/partner_programs/models.py index 319689e8..b43df963 100644 --- a/partner_programs/models.py +++ b/partner_programs/models.py @@ -2,6 +2,7 @@ from django.db import models from partner_programs.constants import get_default_data_schema +from projects.models import Project User = get_user_model() @@ -107,14 +108,22 @@ class PartnerProgramUserProfile(models.Model): """ user = models.ForeignKey( - User, on_delete=models.CASCADE, related_name="partner_program_profile" + User, + on_delete=models.SET_NULL, + related_name="partner_program_profiles", + null=True, + ) + project = models.ForeignKey( + Project, + on_delete=models.SET_NULL, + related_name="partner_program_profiles", + null=True, ) partner_program = models.ForeignKey( PartnerProgram, on_delete=models.CASCADE, - related_name="partner_program_user_profile", + related_name="partner_program_profiles", ) - # TODO: add amount of projects of this Program that user created partner_program_data = models.JSONField() datetime_created = models.DateTimeField(auto_now_add=True) datetime_updated = models.DateTimeField(auto_now=True) @@ -128,6 +137,4 @@ class Meta: ) def __str__(self): - return ( - f"PartnerProgramUserProfile<{self.pk}> - {self.user} {self.partner_program}" - ) + return f"PartnerProgramUserProfile<{self.pk}> - {self.user} {self.project} {self.partner_program}" diff --git a/partner_programs/serializers.py b/partner_programs/serializers.py index 99918044..873e2a47 100644 --- a/partner_programs/serializers.py +++ b/partner_programs/serializers.py @@ -126,3 +126,13 @@ class PartnerProgramUserSerializer(serializers.Serializer): class PartnerProgramDataSchemaSerializer(serializers.Serializer): data_schema = serializers.JSONField(required=True) + + +class UserProgramsSerializer(serializers.ModelSerializer): + class Meta: + model = PartnerProgram + fields = [ + "id", + "name", + "tag", + ] diff --git a/projects/helpers.py b/projects/helpers.py index 8933bc59..28d39a84 100644 --- a/projects/helpers.py +++ b/projects/helpers.py @@ -2,6 +2,7 @@ from django.contrib.auth import get_user_model +from partner_programs.models import PartnerProgram, PartnerProgramUserProfile from projects.constants import RECOMMENDATIONS_COUNT from projects.models import Project, ProjectLink, Achievement @@ -84,3 +85,16 @@ def update_links(links, pk): for link in links ] ) + + +def update_partner_program( + partner_program_id: int, user: "User", instance: "Project" +) -> None: + if partner_program_id: + partner_program = PartnerProgram.objects.get(pk=partner_program_id) + partner_program_profile = PartnerProgramUserProfile.objects.get( + user=user, + partner_program=partner_program, + ) + partner_program_profile.project = instance + partner_program_profile.save() diff --git a/projects/views.py b/projects/views.py index b039ed96..6f5b3118 100644 --- a/projects/views.py +++ b/projects/views.py @@ -9,9 +9,14 @@ from core.permissions import IsStaffOrReadOnly from core.serializers import SetLikedSerializer from core.services import add_view, set_like +from partner_programs.models import PartnerProgram, PartnerProgramUserProfile from projects.filters import ProjectFilter from projects.constants import VERBOSE_STEPS -from projects.helpers import get_recommended_users, check_related_fields_update +from projects.helpers import ( + get_recommended_users, + check_related_fields_update, + update_partner_program, +) from projects.models import Project, Achievement, ProjectNews from projects.pagination import ProjectNewsPagination from projects.permissions import ( @@ -51,6 +56,21 @@ def create(self, request, *args, **kwargs): serializer.validated_data["leader"] = request.user self.perform_create(serializer) + + try: + partner_program_id = request.data.get("partner_program_id") + update_partner_program(partner_program_id, request.user, serializer.instance) + except PartnerProgram.DoesNotExist: + return Response( + {"detail": "Partner program with this id does not exist"}, + status=status.HTTP_400_BAD_REQUEST, + ) + except PartnerProgramUserProfile.DoesNotExist: + return Response( + {"detail": "User is not a member of this partner program"}, + status=status.HTTP_400_BAD_REQUEST, + ) + headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) @@ -101,6 +121,20 @@ def retrieve(self, request, *args, **kwargs): return Response(serializer.data) def put(self, request, pk, **kwargs): + # fixme: add partner_program_id to docs + try: + partner_program_id = request.data.get("partner_program_id") + update_partner_program(partner_program_id, request.user, self.get_object()) + except PartnerProgram.DoesNotExist: + return Response( + {"detail": "Partner program with this id does not exist"}, + status=status.HTTP_400_BAD_REQUEST, + ) + except PartnerProgramUserProfile.DoesNotExist: + return Response( + {"detail": "User is not a member of this partner program"}, + status=status.HTTP_400_BAD_REQUEST, + ) check_related_fields_update(request.data, pk) return super(ProjectDetail, self).put(request, pk) diff --git a/users/urls.py b/users/urls.py index 98f866b6..a2d23257 100644 --- a/users/urls.py +++ b/users/urls.py @@ -18,6 +18,7 @@ RegisteredEventsList, SetUserOnboardingStage, ResendVerifyEmail, + CurrentUserPrograms, ) app_name = "users" @@ -35,6 +36,7 @@ path("users//set_onboarding_stage/", SetUserOnboardingStage.as_view()), path("users/reset-password/", EmailResetPassword.as_view()), path("users/current/", CurrentUser.as_view()), + path("users/current/programs/", CurrentUserPrograms.as_view()), path("users/current/events/", RegisteredEventsList.as_view()), path("users/achievements/", AchievementList.as_view()), path("users/achievements//", AchievementDetail.as_view()), diff --git a/users/views.py b/users/views.py index 67b59618..0a728071 100644 --- a/users/views.py +++ b/users/views.py @@ -15,6 +15,7 @@ ListCreateAPIView, RetrieveUpdateDestroyAPIView, UpdateAPIView, + RetrieveAPIView, ) from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.request import Request @@ -25,6 +26,8 @@ from core.permissions import IsOwnerOrReadOnly from events.models import Event from events.serializers import EventsListSerializer +from partner_programs.models import PartnerProgram +from partner_programs.serializers import UserProgramsSerializer from projects.serializers import ProjectListSerializer from users.helpers import ( reset_email, @@ -126,6 +129,21 @@ def patch(self, request, pk): return super().patch(request, pk) +class CurrentUserPrograms(RetrieveAPIView): + queryset = PartnerProgram.objects.all() + permission_classes = [IsAuthenticated] + serializer_class = UserProgramsSerializer + + def get(self, request, *args, **kwargs): + user = User.objects.get(id=request.user.id) + # fixme: mb hide finished programs + programs = [ + profile.partner_program for profile in user.partner_program_profiles.all() + ] + serializer = self.get_serializer(programs, many=True) + return Response(serializer.data, status=status.HTTP_200_OK) + + class CurrentUser(GenericAPIView): queryset = User.objects.get_users_for_detail_view() permission_classes = [IsAuthenticated]