From f457c78ef567f7da1e60eac303c26ddc79ac4004 Mon Sep 17 00:00:00 2001 From: NickKipshidze Date: Fri, 8 Mar 2024 19:03:08 +0400 Subject: [PATCH 1/8] Add last_payment & start_payment fields --- ...t_last_payment_enrollment_start_payment.py | 27 +++++++++++++++++++ accounts/models.py | 10 +++++++ accounts/serializers.py | 2 +- 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py diff --git a/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py b/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py new file mode 100644 index 0000000..6b10554 --- /dev/null +++ b/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.2 on 2024-03-08 14:44 + +import datetime +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0009_kidsprofile_delete_discorduser'), + ] + + operations = [ + migrations.AddField( + model_name='enrollment', + name='last_payment', + field=models.DateTimeField(default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='enrollment', + name='start_payment', + field=models.DateTimeField(default=datetime.datetime(2024, 3, 8, 14, 44, 52, 652696, tzinfo=datetime.timezone.utc)), + preserve_default=False, + ), + ] diff --git a/accounts/models.py b/accounts/models.py index fd7a235..a7ef9a5 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -84,6 +84,16 @@ class Enrollment(models.Model): status = models.CharField( max_length=16 ) + + start_payment = models.DateTimeField( + null=False, + blank=False + ) + + last_payment = models.DateTimeField( + null=False, + blank=False + ) payze_subscription_id = models.CharField( max_length=255, null=True, blank=True diff --git a/accounts/serializers.py b/accounts/serializers.py index c076332..6361660 100644 --- a/accounts/serializers.py +++ b/accounts/serializers.py @@ -33,7 +33,7 @@ class Meta: model = models.Enrollment fields = [ "id", "enrollment_date", "enrollment_cancel_date", "user", - "service_id", "program_id", "mentor_id", "status", + "service_id", "program_id", "mentor_id", "status", "start_payment", "last_payment", "payments", "program", "service" ] From c2b57353ee5c6b86f5ce052476c767008283b900 Mon Sep 17 00:00:00 2001 From: NickKipshidze Date: Fri, 8 Mar 2024 19:03:23 +0400 Subject: [PATCH 2/8] Add QueryEnrolment endpoint --- accounts/urls.py | 1 + accounts/views.py | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/accounts/urls.py b/accounts/urls.py index bf1cd76..a2991f6 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -10,6 +10,7 @@ path("auth/connect-to-discord", views.Discord.as_view(), name="connect-to-discord"), path("enroll", views.NewEnroll.as_view(), name="enrollment"), path("enrollments", views.MyEnrolls.as_view(), name="myenrollments"), + path("enrollments/query", views.QueryEnrollments.as_view(), name="myenrollments"), path('enrollments//check-payze-subscription-status', views.CheckPayzeSubscriptionStatusView.as_view(), name='check-payze-status'), path("enrollments/", views.UpdateEnroll.as_view()), path("kids/newprofile", views.NewKidsProfile.as_view()), diff --git a/accounts/views.py b/accounts/views.py index fdc1f58..e49f3cd 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -6,9 +6,10 @@ from rest_framework.views import APIView from rest_framework import status from django.shortcuts import get_object_or_404 +from django.utils.dateparse import parse_date from drf_spectacular.utils import extend_schema from . import serializers, models -from datetime import datetime, timezone, timedelta +from datetime import datetime, timezone, timedelta, time import requests from django.conf import settings import logging @@ -526,4 +527,31 @@ def delete(self, request, **kwargs): profile.delete() return Response(status=status.HTTP_204_NO_CONTENT) except models.KidsProfile.DoesNotExist: - return Response({"error": "Profile not found"}, status=status.HTTP_404_NOT_FOUND) \ No newline at end of file + return Response({"error": "Profile not found"}, status=status.HTTP_404_NOT_FOUND) + +class QueryEnrollments(APIView): + authentication_classes = [TokenAuthentication] + permission_classes = [IsAuthenticated] + + @extend_schema(responses=serializers.EnrollmentSerializer) + def get(self, request, **kwargs): + user = models.BitCampUser.objects.get(id=request.user.id) + + if user.is_superuser: + date_str = request.GET.get("date", None) + if date_str: + date_obj = parse_date(date_str) + if date_obj is None: + return Response({"error": "Invalid date format"}, status=status.HTTP_400_BAD_REQUEST) + + datetime_obj = datetime.combine(date_obj, time.min) + + users_after_date = models.Enrollment.objects.filter(last_payment__lt=datetime_obj) + print(datetime_obj) + + serializer = serializers.EnrollmentSerializer(users_after_date, many=True) + return Response(serializer.data) + else: + return Response({"error": "Date parameter is required"}, status=status.HTTP_400_BAD_REQUEST) + else: + return Response({"error": "Unauthorized"}, status=status.HTTP_401_UNAUTHORIZED) \ No newline at end of file From 1e2b884475b1ab297a171949324519c74350c27f Mon Sep 17 00:00:00 2001 From: NickKipshidze Date: Mon, 11 Mar 2024 15:47:50 +0400 Subject: [PATCH 3/8] Add example environment variables --- .env.example | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.env.example b/.env.example index bb2f85c..d92180d 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,8 @@ -PAYZE_API_KEY="{Key:Secret}" \ No newline at end of file +PAYZE_API_KEY="{Key:Secret}" +SECRET_KEY={Secret} +DEBUG=1 +DB_NAME={Database} +DB_USER={User} +DB_PASS={Password} +DB_HOST=localhost +DB_PORT=5432 \ No newline at end of file From 171f476e2e1e96e6ce6a87deb2565f85ebc8c4fe Mon Sep 17 00:00:00 2001 From: NickKipshidze Date: Mon, 11 Mar 2024 15:59:25 +0400 Subject: [PATCH 4/8] Add update last payment date views/urls --- accounts/urls.py | 6 ++++-- accounts/views.py | 30 ++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/accounts/urls.py b/accounts/urls.py index a2991f6..aebbd68 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -10,10 +10,12 @@ path("auth/connect-to-discord", views.Discord.as_view(), name="connect-to-discord"), path("enroll", views.NewEnroll.as_view(), name="enrollment"), path("enrollments", views.MyEnrolls.as_view(), name="myenrollments"), - path("enrollments/query", views.QueryEnrollments.as_view(), name="myenrollments"), path('enrollments//check-payze-subscription-status', views.CheckPayzeSubscriptionStatusView.as_view(), name='check-payze-status'), path("enrollments/", views.UpdateEnroll.as_view()), path("kids/newprofile", views.NewKidsProfile.as_view()), path("kids/getprofiles", views.GetKidsProfile.as_view()), - path("kids/deleteprofile", views.DeleteKidsProfile.as_view()) + path("kids/deleteprofile", views.DeleteKidsProfile.as_view()), + + path("enrollments/query", views.QueryEnrollments.as_view(), name="myenrollments"), + path("enrollments/update-last-payment", views.UpdateLastPayment.as_view(), name="update-last-payment") ] diff --git a/accounts/views.py b/accounts/views.py index d99530a..686febd 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -532,7 +532,34 @@ def delete(self, request, **kwargs): return Response(status=status.HTTP_204_NO_CONTENT) except models.KidsProfile.DoesNotExist: return Response({"error": "Profile not found"}, status=status.HTTP_404_NOT_FOUND) - + +class UpdateLastPayment(APIView): + authentication_classes = [TokenAuthentication] + permission_classes = [IsAuthenticated] + + @extend_schema(responses=serializers.EnrollmentSerializer) + def put(self, request, **kwargs): + user = models.BitCampUser.objects.get(id=request.user.id) + + if user.is_superuser: + enrolment = models.Enrollment.objects.get(id=request.data["id"]) + serializer = serializers.EnrollmentSerializer( + enrolment, + data=request.data, + partial=True + ) + + if serializer.is_valid(): + serializer.save() + return Response( + {"detail": f"Enrolment updated successfully"}, + status=status.HTTP_200_OK + ) + + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + else: + return Response({"error": "Unauthorized"}, status=status.HTTP_401_UNAUTHORIZED) + class QueryEnrollments(APIView): authentication_classes = [TokenAuthentication] permission_classes = [IsAuthenticated] @@ -551,7 +578,6 @@ def get(self, request, **kwargs): datetime_obj = datetime.combine(date_obj, time.min) users_after_date = models.Enrollment.objects.filter(last_payment__lt=datetime_obj) - print(datetime_obj) serializer = serializers.EnrollmentSerializer(users_after_date, many=True) return Response(serializer.data) From a4bb943ef6a99bddd81e65cf968906219cf35886 Mon Sep 17 00:00:00 2001 From: NickKipshidze Date: Mon, 25 Mar 2024 16:34:52 +0400 Subject: [PATCH 5/8] Make better migrations --- ...t_last_payment_enrollment_start_payment.py | 4 +-- ...t_last_payment_enrollment_start_payment.py | 26 +++++++++++++++++++ accounts/models.py | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 accounts/migrations/0011_enrollment_last_payment_enrollment_start_payment.py diff --git a/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py b/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py index 6b10554..2ceb329 100644 --- a/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py +++ b/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py @@ -1,7 +1,6 @@ # Generated by Django 5.0.2 on 2024-03-08 14:44 import datetime -import django.utils.timezone from django.db import migrations, models @@ -15,8 +14,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='enrollment', name='last_payment', - field=models.DateTimeField(default=django.utils.timezone.now), - preserve_default=False, + field=models.DateTimeField(null=True), ), migrations.AddField( model_name='enrollment', diff --git a/accounts/migrations/0011_enrollment_last_payment_enrollment_start_payment.py b/accounts/migrations/0011_enrollment_last_payment_enrollment_start_payment.py new file mode 100644 index 0000000..6441233 --- /dev/null +++ b/accounts/migrations/0011_enrollment_last_payment_enrollment_start_payment.py @@ -0,0 +1,26 @@ +# Generated by ChatGPT 3.5 on 2024-03-25 14:34 + +from django.db import migrations +from django.db.models import Max + +def populate_last_payment_date(apps, schema_editor): + Enrollment = apps.get_model('accounts', 'Enrollment') + Payment = apps.get_model('accounts', 'Payment') + + for enrollment in Enrollment.objects.all(): + latest_payment = Payment.objects.filter(enrollment=enrollment).aggregate(max_date=Max('created_at')) + last_payment_date = latest_payment.get('max_date') + + if last_payment_date: + enrollment.last_payment = last_payment_date + enrollment.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0010_enrollment_last_payment_enrollment_start_payment'), + ] + + operations = [ + migrations.RunPython(populate_last_payment_date), + ] diff --git a/accounts/models.py b/accounts/models.py index a7ef9a5..1623499 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -91,7 +91,7 @@ class Enrollment(models.Model): ) last_payment = models.DateTimeField( - null=False, + null=True, blank=False ) From 5d07f1100cfa327565ead77b97188ed013c21ac4 Mon Sep 17 00:00:00 2001 From: NickKipshidze Date: Mon, 25 Mar 2024 18:03:47 +0400 Subject: [PATCH 6/8] Add enrollment data endpoint --- accounts/urls.py | 3 ++- accounts/views.py | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/accounts/urls.py b/accounts/urls.py index aebbd68..375c13f 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -17,5 +17,6 @@ path("kids/deleteprofile", views.DeleteKidsProfile.as_view()), path("enrollments/query", views.QueryEnrollments.as_view(), name="myenrollments"), - path("enrollments/update-last-payment", views.UpdateLastPayment.as_view(), name="update-last-payment") + path("enrollments/update-last-payment", views.UpdateLastPayment.as_view(), name="update-last-payment"), + path("enrollments/get-enrollment-data", views.GetEnrollmentData.as_view()) ] diff --git a/accounts/views.py b/accounts/views.py index 686febd..ca670cb 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -13,6 +13,7 @@ from . import serializers, models from datetime import datetime, timezone, timedelta, time from content import models as content_models +from content import serializers as content_serializers from datetime import datetime, timezone, timedelta import requests from django.conf import settings @@ -654,3 +655,27 @@ def ManualTransaction(request, enrollment_id): return HttpResponseRedirect(reverse("admin:accounts_enrollment_change", args=[enrollment_id]), status=status.HTTP_201_CREATED) else: return HttpResponseRedirect(reverse("admin:accounts_enrollment_change", args=[enrollment_id]), status=status.HTTP_400_BAD_REQUEST) + +class GetEnrollmentData(APIView): + authentication_classes = [TokenAuthentication] + permission_classes = [IsAuthenticated] + + @extend_schema(responses=serializers.EnrollmentSerializer) + def get(self, request, **kwargs): + user = models.BitCampUser.objects.get(id=request.user.id) + + if user.is_superuser: + id = request.GET.get("id", None) + if id: + enrolment = models.Enrollment.objects.get(id=id) + user = enrolment.user + + enrolment_data = serializers.EnrollmentSerializer(enrolment) + user_data = serializers.BitCampUserSerializer(user).data + + return Response({ + "enrolment": enrolment_data.data, + "user": user_data + }) + else: + return Response({"error": "Unauthorized"}, status=status.HTTP_401_UNAUTHORIZED) From f18080610ab686cbb17d361c5bd5ddc3e4bc11d7 Mon Sep 17 00:00:00 2001 From: NickKipshidze Date: Tue, 26 Mar 2024 15:49:13 +0400 Subject: [PATCH 7/8] Change of plans! Remove everything! --- ...t_last_payment_enrollment_start_payment.py | 25 ----------------- ...t_last_payment_enrollment_start_payment.py | 26 ------------------ accounts/models.py | 10 ------- accounts/urls.py | 1 - accounts/views.py | 27 ------------------- 5 files changed, 89 deletions(-) delete mode 100644 accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py delete mode 100644 accounts/migrations/0011_enrollment_last_payment_enrollment_start_payment.py diff --git a/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py b/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py deleted file mode 100644 index 2ceb329..0000000 --- a/accounts/migrations/0010_enrollment_last_payment_enrollment_start_payment.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 5.0.2 on 2024-03-08 14:44 - -import datetime -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0009_kidsprofile_delete_discorduser'), - ] - - operations = [ - migrations.AddField( - model_name='enrollment', - name='last_payment', - field=models.DateTimeField(null=True), - ), - migrations.AddField( - model_name='enrollment', - name='start_payment', - field=models.DateTimeField(default=datetime.datetime(2024, 3, 8, 14, 44, 52, 652696, tzinfo=datetime.timezone.utc)), - preserve_default=False, - ), - ] diff --git a/accounts/migrations/0011_enrollment_last_payment_enrollment_start_payment.py b/accounts/migrations/0011_enrollment_last_payment_enrollment_start_payment.py deleted file mode 100644 index 6441233..0000000 --- a/accounts/migrations/0011_enrollment_last_payment_enrollment_start_payment.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by ChatGPT 3.5 on 2024-03-25 14:34 - -from django.db import migrations -from django.db.models import Max - -def populate_last_payment_date(apps, schema_editor): - Enrollment = apps.get_model('accounts', 'Enrollment') - Payment = apps.get_model('accounts', 'Payment') - - for enrollment in Enrollment.objects.all(): - latest_payment = Payment.objects.filter(enrollment=enrollment).aggregate(max_date=Max('created_at')) - last_payment_date = latest_payment.get('max_date') - - if last_payment_date: - enrollment.last_payment = last_payment_date - enrollment.save() - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0010_enrollment_last_payment_enrollment_start_payment'), - ] - - operations = [ - migrations.RunPython(populate_last_payment_date), - ] diff --git a/accounts/models.py b/accounts/models.py index 1623499..fd7a235 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -84,16 +84,6 @@ class Enrollment(models.Model): status = models.CharField( max_length=16 ) - - start_payment = models.DateTimeField( - null=False, - blank=False - ) - - last_payment = models.DateTimeField( - null=True, - blank=False - ) payze_subscription_id = models.CharField( max_length=255, null=True, blank=True diff --git a/accounts/urls.py b/accounts/urls.py index 375c13f..2793cb5 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -17,6 +17,5 @@ path("kids/deleteprofile", views.DeleteKidsProfile.as_view()), path("enrollments/query", views.QueryEnrollments.as_view(), name="myenrollments"), - path("enrollments/update-last-payment", views.UpdateLastPayment.as_view(), name="update-last-payment"), path("enrollments/get-enrollment-data", views.GetEnrollmentData.as_view()) ] diff --git a/accounts/views.py b/accounts/views.py index ca670cb..315955b 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -534,33 +534,6 @@ def delete(self, request, **kwargs): except models.KidsProfile.DoesNotExist: return Response({"error": "Profile not found"}, status=status.HTTP_404_NOT_FOUND) -class UpdateLastPayment(APIView): - authentication_classes = [TokenAuthentication] - permission_classes = [IsAuthenticated] - - @extend_schema(responses=serializers.EnrollmentSerializer) - def put(self, request, **kwargs): - user = models.BitCampUser.objects.get(id=request.user.id) - - if user.is_superuser: - enrolment = models.Enrollment.objects.get(id=request.data["id"]) - serializer = serializers.EnrollmentSerializer( - enrolment, - data=request.data, - partial=True - ) - - if serializer.is_valid(): - serializer.save() - return Response( - {"detail": f"Enrolment updated successfully"}, - status=status.HTTP_200_OK - ) - - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - else: - return Response({"error": "Unauthorized"}, status=status.HTTP_401_UNAUTHORIZED) - class QueryEnrollments(APIView): authentication_classes = [TokenAuthentication] permission_classes = [IsAuthenticated] From e5df9b026d75a155c97114333c96cb122eca80e0 Mon Sep 17 00:00:00 2001 From: NickKipshidze Date: Tue, 26 Mar 2024 15:49:36 +0400 Subject: [PATCH 8/8] Change psycopg2 dependency --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4712e03..5251bf9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ django djangorestframework django-cors-headers drf-spectacular -psycopg2 +psycopg2-binary discord.py gunicorn requests