From 6262afea17578703201b934849d1271856610e52 Mon Sep 17 00:00:00 2001 From: rizwanisready Date: Fri, 31 May 2024 17:16:30 +0530 Subject: [PATCH] event view count --- .../0010_alter_appversion_app_type.py | 20 ++ manage_events/admin.py | 11 +- manage_events/api/urls.py | 6 + manage_events/api/views.py | 41 ++++ manage_events/migrations/0009_eventview.py | 75 ++++++ manage_events/models.py | 14 ++ manage_events/report.py | 213 ++++++++++++++++++ manage_events/urls.py | 5 + manage_events/views.py | 25 ++ requirements.txt | 3 + 10 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 accounts/migrations/0010_alter_appversion_app_type.py create mode 100644 manage_events/migrations/0009_eventview.py create mode 100644 manage_events/report.py diff --git a/accounts/migrations/0010_alter_appversion_app_type.py b/accounts/migrations/0010_alter_appversion_app_type.py new file mode 100644 index 0000000..7997524 --- /dev/null +++ b/accounts/migrations/0010_alter_appversion_app_type.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.2 on 2024-05-31 11:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("accounts", "0009_appversion_app_type"), + ] + + operations = [ + migrations.AlterField( + model_name="appversion", + name="app_type", + field=models.CharField( + choices=[("android", "android"), ("ios", "ios")], max_length=10 + ), + ), + ] diff --git a/manage_events/admin.py b/manage_events/admin.py index ac72d3d..ec4bfc8 100644 --- a/manage_events/admin.py +++ b/manage_events/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import EventCategory, Venue, EventMaster, Event, EventPrincipalInteraction +from .models import EventCategory, EventView, Venue, EventMaster, Event, EventPrincipalInteraction # Register your models here. @@ -91,6 +91,15 @@ class EventPrincipalInteractionAdmin(admin.ModelAdmin): ) # Adjust these field lookups according to your models. +class EventViewAdmin(admin.ModelAdmin): + list_display = ('id', 'event', 'principal', 'view_date', 'location') + search_fields = ('event__title', 'principal__email', 'location') + list_filter = ('view_date', 'location', 'event__title', 'principal__email') + ordering = ('-view_date',) + readonly_fields = ('id',) + + +admin.site.register(EventView, EventViewAdmin) admin.site.register(EventPrincipalInteraction, EventPrincipalInteractionAdmin) admin.site.register(Event, EventAdmin) admin.site.register(EventCategory, EventCategoryAdmin) diff --git a/manage_events/api/urls.py b/manage_events/api/urls.py index 4e632f9..f9c3e7d 100644 --- a/manage_events/api/urls.py +++ b/manage_events/api/urls.py @@ -111,4 +111,10 @@ urlpatterns = [ name="principal-events", ), path("tags/", views.TagListView.as_view(), name="tag-list"), + # For counting event views + path( + "event//view/", + views.CaptureEventViewAPIView.as_view(), + name="capture_event_view", + ), ] diff --git a/manage_events/api/views.py b/manage_events/api/views.py index 889f0ba..08f3c6d 100644 --- a/manage_events/api/views.py +++ b/manage_events/api/views.py @@ -34,6 +34,7 @@ from manage_events.models import ( EventCategory, EventPrincipalInteraction, EventReview, + EventView, Favorites, PrincipalPreference, Venue, @@ -123,6 +124,20 @@ class CreateVenueApi(APIView): ) +# # Prepare the email +# subject = f"Your Event Report for {start_date.month} {start_date.year}." +# body = f"Please find attached the event report for {start_date.month} {start_date.month}." +# email_service = EmailService( +# subject="Good Times - Report", +# to=[user.email], +# from_email=settings.EMAIL_HOST_USER, +# ) +# email_service.attach(filename, buffer.getvalue(), "application/pdf") + +# # Send the email +# email_service.send() + + class VenueDeleteAPIView(APIView): authentication_classes = [JWTAuthentication] permission_classes = [IsAuthenticated] @@ -849,3 +864,29 @@ class TagListView(generics.ListAPIView): data=serializer.data, status=status.HTTP_200_OK, ) + + +class CaptureEventViewAPIView(APIView): + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + + def post(self, request, pk): + try: + event = Event.objects.get(pk=pk) + user = request.user + location_parts = [user.city, user.state, user.country] + location = " ".join(part for part in location_parts if part) + + EventView.objects.create(event=event, principal=user, location=location) + + return ApiResponse.success( + message=constants.SUCCESS, + data="Event view recorded successfully.", + status=status.HTTP_200_OK, + ) + except Event.DoesNotExist: + return ApiResponse.error( + message=constants.FAILURE, + errors="Event not found.", + status=status.HTTP_400_BAD_REQUEST, + ) diff --git a/manage_events/migrations/0009_eventview.py b/manage_events/migrations/0009_eventview.py new file mode 100644 index 0000000..ee512af --- /dev/null +++ b/manage_events/migrations/0009_eventview.py @@ -0,0 +1,75 @@ +# Generated by Django 5.0.2 on 2024-05-31 11:31 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_events", "0008_alter_eventprincipalinteraction_event_and_more"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="EventView", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("active", models.BooleanField(default=True)), + ("deleted", models.BooleanField(default=False)), + ("created_on", models.DateTimeField(auto_now_add=True)), + ("modified_on", models.DateTimeField(auto_now=True)), + ("view_date", models.DateTimeField(auto_now_add=True)), + ("location", models.CharField(blank=True, max_length=255, null=True)), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_created", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "event", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="views", + to="manage_events.event", + ), + ), + ( + "modified_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)s_modified", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "principal", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="event_views", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/manage_events/models.py b/manage_events/models.py index 4f876cb..1b4c55e 100644 --- a/manage_events/models.py +++ b/manage_events/models.py @@ -173,3 +173,17 @@ class EventReview(BaseModel): def __str__(self): return f"Review by {self.principal} on {self.event}" + + +class EventView(BaseModel): + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="views") + principal = models.ForeignKey( + IAmPrincipal, on_delete=models.CASCADE, related_name="event_views" + ) + view_date = models.DateTimeField(auto_now_add=True) + location = models.CharField( + max_length=255, blank=True, null=True + ) # Or use a more complex field for location data + + def __str__(self): + return f"{self.principal.email} viewed {self.event.title} from {self.location}" diff --git a/manage_events/report.py b/manage_events/report.py new file mode 100644 index 0000000..f435fe5 --- /dev/null +++ b/manage_events/report.py @@ -0,0 +1,213 @@ +from django.core.mail import EmailMessage +from django.db.models import Count, Q +from django.utils import timezone +from datetime import timedelta +from reportlab.pdfgen import canvas +from reportlab.lib.pagesizes import letter +from reportlab.graphics.shapes import Drawing +from reportlab.graphics.charts.piecharts import Pie +from io import BytesIO +from django.conf import settings +from reportlab.graphics import renderPDF +from django.contrib.auth import get_user_model +from goodtimes.services import EmailService +from manage_events.models import Event, EventInteractionType + + +User = get_user_model() + + +def generate_filename(email, date): + # Extract the username from the email address + username = email.split("@")[0] + # Get the full month name from the date + month_name = date.strftime("%B") + # Create the filename + filename = f"{username}_{month_name}_report.pdf" + return filename + + +def get_previous_month_date_range(): + today = timezone.now() + first_day_of_current_month = today.replace(day=1) + last_day_of_previous_month = first_day_of_current_month - timedelta(days=1) + first_day_of_previous_month = last_day_of_previous_month.replace(day=1) + return first_day_of_previous_month, last_day_of_previous_month + + +def generate_event_report(user_id): + # Calculate the start and end dates for the previous month + start_date, end_date = get_previous_month_date_range() + + # Get the user (manager) + user = User.objects.get(id=user_id) + + # Filter events created by the user in the previous month + events = Event.objects.filter( + created_by=user, created_on__gte=start_date, created_on__lte=end_date + ).annotate( + favorites_count=Count( + "favorites", filter=Q(favorites__active=True, favorites__deleted=False) + ), + interested_count=Count( + "interaction_event", + filter=Q(interaction_event__status=EventInteractionType.INTERESTED), + ), + going_count=Count( + "interaction_event", + filter=Q(interaction_event__status=EventInteractionType.GOING), + ), + reviews_count=Count( + "reviews", filter=Q(reviews__active=True, reviews__deleted=False) + ), + ) + + # Generate the report + report_data = [] + for event in events: + report_data.append( + { + "event_name": event.title, + "favorites_count": event.favorites_count, + "interested_count": event.interested_count, + "going_count": event.going_count, + "reviews_count": event.reviews_count, + } + ) + + return report_data + + +def generate_event_report_pdf(user, report_data): + start_date, _ = get_previous_month_date_range() + filename = generate_filename(user.email, start_date) + + buffer = BytesIO() + pdf = canvas.Canvas(buffer, pagesize=letter) + width, height = letter + + # Add a title and user information + pdf.setFont("Helvetica-Bold", 16) + pdf.drawString(100, 750, "Event Report - April 2024") + + user_name = user.email.split("@")[0] # Use part of email before @ as username + pdf.setFont("Helvetica", 12) + pdf.drawString(100, 730, f"For Event Manager: {user_name}") + + # Add a table header + pdf.setFont("Helvetica-Bold", 10) + pdf.drawString(50, 700, "Event Name") + pdf.drawString(250, 700, "Favorites") + pdf.drawString(350, 700, "Interested") + pdf.drawString(450, 700, "Going") + pdf.drawString(550, 700, "Reviews") + + # Loop through data and add table rows + y_pos = 680 + for event in report_data: + pdf.drawString(50, y_pos, event["event_name"]) + pdf.drawString(250, y_pos, str(event["favorites_count"])) + pdf.drawString(350, y_pos, str(event["interested_count"])) + pdf.drawString(450, y_pos, str(event["going_count"])) + pdf.drawString(550, y_pos, str(event["reviews_count"])) + y_pos -= 15 # Adjust position for next row + + # Draw the pie chart + if report_data: + pie_data = [ + sum(event[key] for event in report_data) + for key in ("favorites_count", "interested_count", "going_count") + ] + pie_labels = ["Favorites", "Interested", "Going"] + + drawing = Drawing(width, height) + pie = Pie() + pie.x = 150 + pie.y = 200 + pie.width = 300 + pie.height = 150 + pie.data = pie_data + pie.labels = pie_labels + pie.slices.strokeWidth = 0.5 + + drawing.add(pie) + renderPDF.draw(drawing, pdf, 150, 200) + + # Close the PDF object and write the buffer content to the PDF file + pdf.save() + buffer.seek(0) + pdf_data = buffer.read() + buffer.close() + return pdf_data, filename + + +def generate_event_report_pdf_two(user, report_data): + start_date, _ = get_previous_month_date_range() + filename = generate_filename(user.email, start_date) + + buffer = BytesIO() + pdf = canvas.Canvas(buffer, pagesize=letter) + width, height = letter + + # Add a title and user information + pdf.setFont("Helvetica-Bold", 16) + pdf.drawString(100, 750, "Event Report - April 2024") + + user_name = user.email.split("@")[0] # Use part of email before @ as username + pdf.setFont("Helvetica", 12) + pdf.drawString(100, 730, f"For Event Manager: {user_name}") + + # Event loop with row handling + y_pos = 650 # Starting position for charts (adjust as needed) + chart_width = 250 # Width of each pie chart + chart_height = 150 # Height of each pie chart + chart_spacing = 50 # Spacing between charts in a row + + for i, event in enumerate(report_data): + # Add event name as a header + pdf.setFont("Helvetica-Bold", 12) + pdf.drawString(50, y_pos + 20, event["event_name"]) + + # Check if this is the first event in a row + if i % 2 == 0: + x_pos = 75 # Starting position for charts in the first column + else: + x_pos = ( + width - chart_width - 75 + ) # Starting position for charts in the second column + + # Draw pie charts for the event + for key, value in [ + ("favorites_count", "Favorites"), + ("interested_count", "Interested"), + ("going_count", "Going"), + ]: + pie_data = [value] + pie_labels = [value] + + drawing = Drawing(chart_width, chart_height) + pie = Pie() + pie.x = 0 + pie.y = 0 + pie.width = chart_width + pie.height = chart_height + pie.data = pie_data + pie.labels = pie_labels + pie.slices.strokeWidth = 0.5 + + drawing.add(pie) + renderPDF.draw(drawing, pdf, x_pos, y_pos) + + x_pos += chart_width + chart_spacing # Adjust x position for the next chart + + y_pos -= ( + 100 # Adjust y position for the next event (adjust based on chart heights) + ) + + # Close the PDF object and write the buffer content to the PDF file + pdf.save() + buffer.seek(0) + pdf_data = buffer.read() + buffer.close() + + return pdf_data, filename diff --git a/manage_events/urls.py b/manage_events/urls.py index 543d40b..6ce1536 100644 --- a/manage_events/urls.py +++ b/manage_events/urls.py @@ -89,4 +89,9 @@ urlpatterns = [ views.VenueDeleteView.as_view(), name="venue_delete", ), + path( + "generate-event-report//", + views.GenerateEventReportView.as_view(), + name="generate_event_report", + ), ] diff --git a/manage_events/views.py b/manage_events/views.py index 5952aca..05c53e5 100644 --- a/manage_events/views.py +++ b/manage_events/views.py @@ -13,6 +13,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.urls import reverse_lazy from django.contrib import messages from goodtimes import constants +from django.contrib.auth import get_user_model # Create your views here. @@ -477,3 +478,27 @@ class VenueDeleteView(LoginRequiredMixin, generic.View): messages.success(request, self.error_message) return redirect(self.success_url) + + +User = get_user_model() +from .report import generate_event_report, generate_event_report_pdf +from django.http import HttpResponse + + +class GenerateEventReportView(generic.View): + + def get(self, request, user_id): + # Generate the event report + report_data = generate_event_report(user_id) + + # Get the user + user = get_object_or_404(User, id=user_id) + + # Generate the PDF + pdf_data, filename = generate_event_report_pdf(user, report_data) + + # Create the HttpResponse object with the PDF data + response = HttpResponse(pdf_data, content_type="application/pdf") + response["Content-Disposition"] = f'attachment; filename="{filename}"' + + return response diff --git a/requirements.txt b/requirements.txt index 08ccda6..094748b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ certifi==2024.2.2 cffi==1.16.0 channels==4.0.0 channels-redis==4.2.0 +chardet==5.2.0 charset-normalizer==3.3.2 colorama==0.4.6 colorlog==6.8.2 @@ -17,6 +18,7 @@ daphne==4.1.0 defusedxml==0.7.1 Django==5.0.2 django-allauth==0.61.1 +django-channels==0.7.0 django-cors-headers==4.3.1 django-debug-toolbar==4.3.0 django-environ==0.11.2 @@ -56,6 +58,7 @@ python3-openid==3.2.0 pytz==2024.1 PyYAML==6.0.1 redis==5.0.2 +reportlab==4.2.0 requests==2.31.0 requests-oauthlib==1.3.1 service-identity==24.1.0