Merge pull request #82 from WDI-Ideas/development

Development
This commit is contained in:
BOBBY VISHWAKARMA
2024-08-09 11:49:33 +05:30
committed by GitHub
13 changed files with 260 additions and 23 deletions

View File

@@ -10,7 +10,8 @@ from accounts.models import (
IAmPrincipalType,
# IAmPrincipalKYCDetails,
)
from manage_events.models import EventPrincipalInteraction, PrincipalPreference
from manage_events.models import EventInteractionType, EventPrincipalInteraction, FreeUsageFeatureLimit, PrincipalPreference
from manage_referrals.models import (
ReferralCode,
ReferralRecord,
@@ -267,6 +268,86 @@ class ProfileSerializer(serializers.ModelSerializer):
data["profile_photo"] = self.get_image_url(instance, "profile_photo", request)
return data
class ProfileExtendedDataSerializer(serializers.ModelSerializer):
invite_count = serializers.SerializerMethodField(read_only=True)
principal_type_name = serializers.SerializerMethodField(read_only=True)
has_active_subscription = serializers.SerializerMethodField(read_only=True)
preference = serializers.SerializerMethodField(read_only=True)
register_complete = serializers.BooleanField(read_only=True)
is_active = serializers.BooleanField(read_only=True)
going_events_count = serializers.SerializerMethodField(read_only=True)
interested_events_count = serializers.SerializerMethodField(read_only=True)
feature_limit = serializers.SerializerMethodField(read_only=True)
class Meta:
model = IAmPrincipal
fields = [
"principal_type_name",
"invite_count",
"register_complete",
"has_active_subscription",
"preference",
"is_active",
"going_events_count",
"interested_events_count",
"feature_limit"
]
def get_going_events_count(self, obj):
return EventPrincipalInteraction.objects.filter(
principal=obj, status=EventInteractionType.GOING
).count()
def get_interested_events_count(self, obj):
return EventPrincipalInteraction.objects.filter(
principal=obj, status=EventInteractionType.INTERESTED
).count()
def get_invite_count(self, obj):
if obj:
return ReferralRecord.get_invite_count(obj)
return 0
def get_principal_type_name(self, obj):
return obj.principal_type.name if obj.principal_type else None
def get_preference(self, obj):
return PrincipalPreference.objects.filter(principal=obj).exists()
def get_has_active_subscription(self, obj):
subscription_status = {
"has_active_subscription": False,
"in_grace_period": False,
"grace_period_end_date": None,
}
today = timezone.now().date()
# Attempt to find the active subscription with the furthest grace_period_end_date
latest_subscription = PrincipalSubscription.get_principal_subscription(obj)
print(f"subscrition record {latest_subscription}")
if latest_subscription:
# Check if we're within the grace period
if today <= latest_subscription.grace_period_end_date:
subscription_status["has_active_subscription"] = (
today <= latest_subscription.end_date
)
subscription_status["in_grace_period"] = (
latest_subscription.end_date
< today
<= latest_subscription.grace_period_end_date
)
subscription_status["grace_period_end_date"] = (
latest_subscription.grace_period_end_date
)
return subscription_status
def get_feature_limit(self, obj):
from manage_events.api.serializers import FreeUsageFeatureLimitSerializer
obj = FreeUsageFeatureLimit.objects.first()
return FreeUsageFeatureLimitSerializer().to_representation(obj)
# class PrincipalKYCDetailsSerializer(serializers.ModelSerializer):
# aadhar_front_image = serializers.ImageField(required=False)

View File

@@ -17,7 +17,8 @@ urlpatterns = [
path('request-otp/', views.OtpRequestView.as_view(), name='send_otp'),
path('verify-otp/', views.OTPVerificationView.as_view(), name='send_otp'),
# path('profile/view/<str:principal_type>/', views.ProfileView.as_view(), name='profile_veiw'),
path('profile/extended-data/', views.ProfileExtendedView.as_view(), name='profile_veiw'),
path('profile/view/', views.ProfileView.as_view(), name='profile_veiw'),
path('profile/edit/', views.ProfileView.as_view(), name='profile_edit'),
path('profile/password-reset/', views.ProfilePasswordResetView.as_view(), name='password_reset'),

View File

@@ -44,6 +44,7 @@ from .serializers import (
EmailSerializer,
IAmPrincipalExtendedDataSerializer,
PlayerIDSerializer,
ProfileExtendedDataSerializer,
RegistrationPasswordSerializer,
RegistrationSerializer,
ReferralCodeSerializer,
@@ -538,6 +539,21 @@ class ProfileView(APIView):
)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
class ProfileExtendedView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
model = IAmPrincipal
serializer = ProfileExtendedDataSerializer
def get(self, request, *args, **kwargs):
serializer = self.serializer(instance=request.user)
success_response = {
"status": status.HTTP_200_OK,
"message": constants.SUCCESS,
"data": serializer.data,
}
return ApiResponse.success(**success_response)
class ProfilePasswordResetView(APIView):
authentication_classes = [JWTAuthentication]
@@ -1003,7 +1019,7 @@ class AccountTransferCheckView(APIView):
"errors": str(e),
}
return ApiResponse.error(**error_response)
serializer = self.serializer_class(obj)
print("serializer data", serializer)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
@@ -1032,6 +1048,4 @@ class AccountTransferCheckView(APIView):
obj.pwd_changed_post_transfer = True
obj.save()
serializer = self.serializer_class(obj)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)

View File

@@ -760,7 +760,10 @@ class CustomerDetailView(LoginRequiredMixin, generic.DetailView):
def get(self, request, *args, **kwargs):
principal_obj = IAmPrincipal.objects.get(pk=kwargs.get("pk"))
principal_preference = PrincipalPreference.objects.get(principal_id=principal_obj.id)
try:
principal_preference = PrincipalPreference.objects.get(principal_id=principal_obj.id)
except Exception as e:
principal_preference = None
principal_subscription = PrincipalSubscription.objects.filter(principal=principal_obj).order_by("-start_date").first()
return render(request, self.template_name, locals())

View File

@@ -4,6 +4,7 @@ from .models import (
EventShare,
EventView,
Favorites,
FreeUsageFeatureLimit,
Venue,
EventMaster,
Event,
@@ -131,3 +132,4 @@ admin.site.register(EventCategory, EventCategoryAdmin)
admin.site.register(Venue, VenueAdmin)
admin.site.register(EventMaster, EventMasterAdmin)
admin.site.register(AgeGroups)
admin.site.register(FreeUsageFeatureLimit)

View File

@@ -14,11 +14,18 @@ from manage_events.models import (
EventPrincipalInteraction,
EventReview,
Favorites,
FreeUsageFeatureLimit,
Venue,
PrincipalPreference,
)
class FreeUsageFeatureLimitSerializer(serializers.ModelSerializer):
class Meta:
model = FreeUsageFeatureLimit
fields = ['id', 'category_limit']
class EventImageSerializer(serializers.ModelSerializer):
class Meta:
model = EventImage

View File

@@ -4,6 +4,7 @@ from . import views
app_name = "manage_events_api"
urlpatterns = [
path('free/feature-limit/', views.FreeUsageFeatureLimitView.as_view(), name='feature-limit'),
path(
"add-event/",
views.CreateEventApi.as_view(),
@@ -134,6 +135,7 @@ urlpatterns = [
name="age_group_list"
),
# event list with filter
path(
"events/",
views.EventListView.as_view(),

View File

@@ -6,6 +6,7 @@ import googlemaps
from rest_framework import status, generics, mixins
from rest_framework.views import APIView
from django.conf import settings
from accounts import resource_action
from accounts.models import IAmPrincipalLocation
from goodtimes import constants
from django.db.models import Q, Count
@@ -27,6 +28,7 @@ from manage_events.api.serializers import (
EventCategorySerializer,
EventDetailSerializer,
EventReviewSerializer,
FreeUsageFeatureLimitSerializer,
IAmPrincipalLocationSerializer,
PrincipalPreferenceSerializer,
VenueSerializer,
@@ -43,14 +45,30 @@ from manage_events.models import (
EventShare,
EventView,
Favorites,
FreeUsageFeatureLimit,
PrincipalPreference,
Venue,
)
import requests
from manage_events.utils import haversine_one, update_principal_location
from manage_subscriptions.models import PrincipalSubscription
from .filters import EventFilter
class FreeUsageFeatureLimitView(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
model = FreeUsageFeatureLimit
serializer_class = FreeUsageFeatureLimitSerializer
def get(self, request):
obj = self.model.objects.first()
serializer = self.serializer_class(obj)
return ApiResponse.success(
status=status.HTTP_200_OK,
message=constants.SUCCESS,
data=serializer.data,
)
class CreateEventApi(APIView):
authentication_classes = [JWTAuthentication]
@@ -539,6 +557,27 @@ class PrincipalPreferenceView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, *args, **kwargs):
principal = request.user
# Check if the principal has a subscription
if not PrincipalSubscription.has_principal_subscription(principal):
# Get the preferred categories from the request data
preferred_categories = request.data.get("preferred_categories", [])
# Get the category limit for free usage
category_limit = FreeUsageFeatureLimit.get_category_limit()
# Check if the principal is an event user and has exceeded the category limit
if principal.principal_type.name == resource_action.PRINCIPAL_TYPE_EVENT_USER and len(preferred_categories) > category_limit:
# Create an error message indicating that a paid subscription is required
error_message = f"Upgrade to paid subscription to select more than {category_limit} categories."
return ApiResponse.error(
status=status.HTTP_400_BAD_REQUEST,
message=error_message,
errors=error_message,
)
serializer = PrincipalPreferenceSerializer(
data=request.data, context={"request": request}
)
@@ -1009,7 +1048,12 @@ class EventListView(generics.ListAPIView):
filterset_class = EventFilter
def get_queryset(self):
# Base queryset filtering active, non-draft, non-deleted events with end date in the future
"""
Returns a queryset of events filtered by the user's preferences and subscription status.
"""
principal = self.request.user
# Filter the base queryset to only include active, non-draft, non-deleted events with an end date in the future
queryset = Event.objects.filter(
active=True,
draft=False,
@@ -1017,11 +1061,14 @@ class EventListView(generics.ListAPIView):
end_date__gte=timezone.now().date()
)
if not self.request.query_params:
preferences = PrincipalPreference.objects.get(principal=self.request.user)
preferred_categories_ids = preferences.preferred_categories.values_list(
"id", flat=True
)
# If no filter is applied and the user does not have a subscription,
# only show events that match the user's preferred categories
if not self.request.query_params or not PrincipalSubscription.has_principal_subscription(principal):
# Get the user's preferred categories
preferences = PrincipalPreference.objects.get(principal=principal)
preferred_categories_ids = preferences.preferred_categories.values_list("id", flat=True)
# Filter the queryset to only include events in the user's preferred categories
queryset = queryset.filter(category__in=preferred_categories_ids).order_by("start_date")
return queryset

View File

@@ -0,0 +1,25 @@
# Generated by Django 5.0.2 on 2024-08-05 10:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('manage_events', '0015_agegroups'),
]
operations = [
migrations.CreateModel(
name='FreeUsageFeatureLimit',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('category_limit', models.PositiveIntegerField(default=3, help_text='The maximum number of categories that free app users can select.')),
],
options={
'verbose_name': 'Free Usage Feature Limit',
'verbose_name_plural': 'Free Usage Feature Limits',
'db_table': 'free_usage_feature_limit',
},
),
]

View File

@@ -1,12 +1,33 @@
from django.db import models
from django.core.exceptions import ValidationError
from accounts.models import BaseModel, IAmPrincipal
from django.db import transaction
from taggit.managers import TaggableManager
# from django.contrib.gis.db import models as gis_models
class FreeUsageFeatureLimit(models.Model):
category_limit = models.PositiveIntegerField(
default=3,
help_text="The maximum number of categories that free app users can select."
)
class Meta:
db_table = "free_usage_feature_limit"
verbose_name = "Free Usage Feature Limit"
verbose_name_plural = "Free Usage Feature Limits"
def __str__(self):
return f"Free usage limit: {self.category_limit} categories"
def save(self, *args, **kwargs):
if not self.pk and FreeUsageFeatureLimit.objects.exists():
raise ValidationError("There can only be one FreeUsageFeatureLimit instance.")
return super().save(*args, **kwargs)
@classmethod
def get_category_limit(cls):
return cls.objects.values_list('category_limit', flat=True).first()
# Create your models here.
class EventCategory(BaseModel):
title = models.CharField(max_length=255)
image = models.ImageField(upload_to="event_category", null=True, blank=True)

View File

@@ -17,6 +17,7 @@ from django.urls import reverse_lazy
from django.contrib import messages
from goodtimes import constants
from django.contrib.auth import get_user_model
from datetime import date
# Create your views here.
@@ -362,24 +363,26 @@ class EventDetailView(generic.DetailView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_name"] = self.page_name
event_id = self.object.id # Get the current event's ID
event = self.object # Get the current event's ID
# Separate count for interested and going
interested_count = EventPrincipalInteraction.objects.filter(
event_id=event_id, status="interested"
event=event, status="interested"
).count()
going_count = EventPrincipalInteraction.objects.filter(
event_id=event_id, status="going"
event=event, status="going"
).count()
context["interested_count"] = interested_count
context["going_count"] = going_count
today = date.today()
context["publish"] = not event.draft and event.active and event.end_date >= today
# Reviews for the event
context["reviews"] = self.object.reviews.all()
context["reviews"] = event.reviews.all()
# Images of the event
context["images"] = self.object.event_images.all()
context["images"] = event.event_images.all()
return context

View File

@@ -1,4 +1,5 @@
from datetime import timedelta, timezone
from datetime import timedelta
from django.utils import timezone
from django.db import models
from accounts.models import BaseModel, IAmPrincipal, IAmPrincipalType
from django.utils.translation import gettext_lazy as _
@@ -77,13 +78,41 @@ class PrincipalSubscription(BaseModel):
def __str__(self):
return f"{self.subscription} - {self.principal.first_name}"
def generate_order_id(email):
return f"order_{str(timezone.localtime().timestamp())}{str(email)}"
def generate_grace_period_end_date(date):
return date + timedelta(days=15)
@classmethod
def has_principal_subscription(cls, principal):
return cls.get_active_princial_subscription(principal).exists()
@classmethod
def get_active_princial_subscription(cls, principal):
return cls.objects.filter(
principal=principal,
is_paid=True,
cancelled=False,
deleted=False,
active=True,
status=SubscriptionStatus.ACTIVE,
grace_period_end_date__gt=timezone.now().date(),
)
# need to improve this
@classmethod
def get_principal_subscription(cls, principal):
return cls.objects.filter(
principal=principal,
is_paid=True,
cancelled=False,
deleted=False,
active=True,
status=SubscriptionStatus.ACTIVE,
).order_by("-grace_period_end_date").first()
class WebhookEvent(BaseModel):
event_id = models.CharField(max_length=255, unique=True, db_index=True)

View File

@@ -62,7 +62,7 @@
</div>
<div class="row">
<div class="col-md-8">
<div class="col-md-{{ publish|yesno:'8,12' }}">
<div class="card mb-3" style="border-radius: 20px; box-shadow: 0 4px 8px rgba(0,0,0,.1);">
<div class="card-body">
<h2 class="card-title">{{ event.brand.title }}</h2>
@@ -72,6 +72,7 @@
</div>
</div>
</div>
{% if publish %}
<div class="col-md-4">
<div class="card mb-3" style="border-radius: 20px; box-shadow: 0 4px 8px rgba(0,0,0,.1);">
<div class="card-body">
@@ -92,6 +93,7 @@
</div>
</div>
</div>
{% endif %}
</div>