From 10171170f47524ee5883009327773853a13ff589 Mon Sep 17 00:00:00 2001 From: rizwanisready Date: Fri, 31 May 2024 19:58:50 +0530 Subject: [PATCH 1/3] reports pdf --- manage_events/report.py | 177 ++++++++++++++++++++++++++++++++-------- manage_events/views.py | 4 +- 2 files changed, 147 insertions(+), 34 deletions(-) diff --git a/manage_events/report.py b/manage_events/report.py index f435fe5..94d237c 100644 --- a/manage_events/report.py +++ b/manage_events/report.py @@ -8,10 +8,11 @@ from reportlab.graphics.shapes import Drawing from reportlab.graphics.charts.piecharts import Pie from io import BytesIO from django.conf import settings +from collections import defaultdict 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 +from manage_events.models import Event, EventInteractionType, EventView User = get_user_model() @@ -36,44 +37,35 @@ def get_previous_month_date_range(): 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) - ), + 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)), + views_count=Count("views", filter=Q(views__active=True, views__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, - } - ) + views = EventView.objects.filter(event=event) + locations = defaultdict(int) + for view in views: + locations[view.location] += 1 + + report_data.append({ + "event_name": event.title, + "event_type": event.category.title, + "event_date": str(event.start_date), + "favorites_count": event.favorites_count, + "interested_count": event.interested_count, + "going_count": event.going_count, + "reviews_count": event.reviews_count, + "views_count": event.views_count, + "locations": dict(locations), + }) return report_data @@ -211,3 +203,124 @@ def generate_event_report_pdf_two(user, report_data): buffer.close() return pdf_data, filename + + +def generate_event_report_pdf_three(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 + + # Header Section + title = "Good Times Ltd. Monthly Report" + report_for_month = f"Report for the month of - {start_date.strftime('%B %Y')}" + organiser_name = f"Name of the Organiser - {user.get_full_name()}" + contact_name = f"Contact Name - {user.get_full_name()}" + + pdf.setFont("Helvetica-Bold", 20) + title_width = pdf.stringWidth(title, "Helvetica-Bold", 20) + pdf.drawString((width - title_width) / 2, 750, title) + + pdf.setFont("Helvetica", 16) + report_for_month_width = pdf.stringWidth(report_for_month, "Helvetica", 16) + pdf.drawString((width - report_for_month_width) / 2, 720, report_for_month) + + organiser_name_width = pdf.stringWidth(organiser_name, "Helvetica", 16) + pdf.drawString((width - organiser_name_width) / 2, 690, organiser_name) + + contact_name_width = pdf.stringWidth(contact_name, "Helvetica", 16) + pdf.drawString((width - contact_name_width) / 2, 660, contact_name) + pdf.showPage() + # Summary Section + pdf.setFont("Helvetica-Bold", 12) + pdf.drawString( + 100, + 640, + f"Number of Events added in {start_date.strftime('%B %Y')} - {len(report_data)}", + ) + + y_pos = 620 + pdf.setFont("Helvetica-Bold", 10) + pdf.drawString(50, y_pos, "Event Name") + pdf.drawString(200, y_pos, "Event Type") + pdf.drawString(350, y_pos, "Date") + y_pos -= 20 + + pdf.setFont("Helvetica", 10) + for event in report_data: + pdf.drawString(50, y_pos, event["event_name"]) + pdf.drawString(200, y_pos, event["event_type"]) + pdf.drawString(350, y_pos, event["event_date"]) + y_pos -= 20 + + # Start a new page for Traffic Details Section + pdf.showPage() + + pdf.setFont("Helvetica-Bold", 12) + pdf.drawString( + 50, height - 50, "Traffic Details for profile - Event Organisers London Ltd." + ) + + # Detailed Review of Each Event + for event in report_data: + pdf.showPage() + pdf.setFont("Helvetica-Bold", 12) + pdf.drawString(50, height - 50, f"Event Name - {event['event_name']}") + pdf.setFont("Helvetica", 10) + pdf.drawString(50, height - 70, f"Event Type - {event['event_type']}") + pdf.drawString(50, height - 90, f"Event Date - {event['event_date']}") + + y_pos = height - 120 + pdf.drawString(50, y_pos, f"Number of Views - {event['views_count']}") + pdf.drawString(200, y_pos, f"Favorites Count - {event['favorites_count']}") + pdf.drawString(350, y_pos, f"Interested in Going - {event['interested_count']}") + pdf.drawString(500, y_pos, f"Going - {event['going_count']}") + pdf.drawString(650, y_pos, f"Reviews - {event['reviews_count']}") + y_pos -= 40 + + pdf.setFont("Helvetica-Bold", 10) + pdf.drawString(50, y_pos, "View Details:") + y_pos -= 20 + pdf.setFont("Helvetica", 10) + pdf.drawString(100, y_pos, "User Email") + pdf.drawString(300, y_pos, "Location") + pdf.drawString(500, y_pos, "View Date") + y_pos -= 20 + for view in event["locations"]: + pdf.drawString(100, y_pos, view["user__email"]) + pdf.drawString(300, y_pos, view["location"]) + pdf.drawString(500, y_pos, view["view_date"].strftime("%d %B %Y %H:%M")) + y_pos -= 20 + + # Draw pie chart on a new page if there's not enough space + if y_pos < 150: + pdf.showPage() + y_pos = height - 150 + + pie_data = [ + event["views_count"], + event["favorites_count"], + event["interested_count"], + event["going_count"], + ] + pie_labels = ["Views", "Favorites", "Interested", "Going"] + + drawing = Drawing(200, 100) + pie = Pie() + pie.x = 50 + pie.y = y_pos - 100 + pie.data = pie_data + pie.labels = pie_labels + pie.width = 100 + pie.height = 100 + drawing.add(pie) + renderPDF.draw(drawing, pdf, 50, y_pos - 100) + y_pos -= 150 # Adjust for pie chart height + + pdf.save() + buffer.seek(0) + pdf_data = buffer.read() + buffer.close() + return pdf_data, filename diff --git a/manage_events/views.py b/manage_events/views.py index 05c53e5..89c3336 100644 --- a/manage_events/views.py +++ b/manage_events/views.py @@ -481,7 +481,7 @@ class VenueDeleteView(LoginRequiredMixin, generic.View): User = get_user_model() -from .report import generate_event_report, generate_event_report_pdf +from .report import generate_event_report, generate_event_report_pdf_three from django.http import HttpResponse @@ -495,7 +495,7 @@ class GenerateEventReportView(generic.View): user = get_object_or_404(User, id=user_id) # Generate the PDF - pdf_data, filename = generate_event_report_pdf(user, report_data) + pdf_data, filename = generate_event_report_pdf_three(user, report_data) # Create the HttpResponse object with the PDF data response = HttpResponse(pdf_data, content_type="application/pdf") From 5ddccc0860ff7270817bdb03ea4e5907bc49ad64 Mon Sep 17 00:00:00 2001 From: rizwanisready Date: Mon, 3 Jun 2024 16:49:19 +0530 Subject: [PATCH 2/3] report --- manage_events/admin.py | 31 +- manage_events/api/urls.py | 6 + manage_events/api/views.py | 28 ++ ...nt_social_media_shares_count_eventshare.py | 78 ++++ manage_events/models.py | 14 + manage_events/report.py | 423 ++++++++---------- 6 files changed, 344 insertions(+), 236 deletions(-) create mode 100644 manage_events/migrations/0010_event_social_media_shares_count_eventshare.py diff --git a/manage_events/admin.py b/manage_events/admin.py index ec4bfc8..ff0a938 100644 --- a/manage_events/admin.py +++ b/manage_events/admin.py @@ -1,5 +1,13 @@ from django.contrib import admin -from .models import EventCategory, EventView, Venue, EventMaster, Event, EventPrincipalInteraction +from .models import ( + EventCategory, + EventShare, + EventView, + Venue, + EventMaster, + Event, + EventPrincipalInteraction, +) # Register your models here. @@ -78,7 +86,7 @@ class EventAdmin(admin.ModelAdmin): }, ), ) - filter_horizontal = () # Use this if there are many-to-many fields + filter_horizontal = () # if there are many-to-many fields raw_id_fields = ("venue", "category", "event_master") @@ -88,17 +96,24 @@ class EventPrincipalInteractionAdmin(admin.ModelAdmin): search_fields = ( "principal__name", "event__title", - ) # 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',) + list_display = ("id", "event", "principal", "view_date", "location") + search_fields = ("event__title", "principal__email", "location") + list_filter = ("id", "view_date", "location", "event__title", "principal__email") + ordering = ("-view_date",) + readonly_fields = ("id",) +class EventShareAdmin(admin.ModelAdmin): + list_display = ("id", "event", "principal", "created_on") + search_fields = ("event__title", "principal__username") + list_filter = ("id", "event", "principal", "created_on") + + +admin.site.register(EventShare, EventShareAdmin) admin.site.register(EventView, EventViewAdmin) admin.site.register(EventPrincipalInteraction, EventPrincipalInteractionAdmin) admin.site.register(Event, EventAdmin) diff --git a/manage_events/api/urls.py b/manage_events/api/urls.py index f9c3e7d..5b63eb7 100644 --- a/manage_events/api/urls.py +++ b/manage_events/api/urls.py @@ -117,4 +117,10 @@ urlpatterns = [ views.CaptureEventViewAPIView.as_view(), name="capture_event_view", ), + # For counting event shares + path( + "event//share/", + views.EventShareView.as_view(), + name="capture_event_share", + ), ] diff --git a/manage_events/api/views.py b/manage_events/api/views.py index 08f3c6d..9bc35ce 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, + EventShare, EventView, Favorites, PrincipalPreference, @@ -890,3 +891,30 @@ class CaptureEventViewAPIView(APIView): errors="Event not found.", status=status.HTTP_400_BAD_REQUEST, ) + + +class EventShareView(APIView): + authentication_classes = [JWTAuthentication] + permission_classes = [IsAuthenticated] + + def post(self, request, pk): + try: + event = Event.objects.get(id=pk) + except Event.DoesNotExist: + return ApiResponse.error( + message=constants.FAILURE, + errors="Event not found.", + status=status.HTTP_400_BAD_REQUEST, + ) + + # Incrementing the social media shares count + event.increment_shares() + + user = request.user # Assuming the user is authenticated + EventShare.objects.create(principal=user, event=event) + + return ApiResponse.success( + message=constants.SUCCESS, + data="Event shared successfully.", + status=status.HTTP_200_OK, + ) diff --git a/manage_events/migrations/0010_event_social_media_shares_count_eventshare.py b/manage_events/migrations/0010_event_social_media_shares_count_eventshare.py new file mode 100644 index 0000000..4c6c7a4 --- /dev/null +++ b/manage_events/migrations/0010_event_social_media_shares_count_eventshare.py @@ -0,0 +1,78 @@ +# Generated by Django 5.0.2 on 2024-06-01 15:06 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("manage_events", "0009_eventview"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name="event", + name="social_media_shares_count", + field=models.IntegerField(default=0), + ), + migrations.CreateModel( + name="EventShare", + 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)), + ( + "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="social_media_shares", + 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_shares", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/manage_events/models.py b/manage_events/models.py index 1b4c55e..a961ab5 100644 --- a/manage_events/models.py +++ b/manage_events/models.py @@ -84,6 +84,11 @@ class Event(BaseModel): tags = TaggableManager(blank=True) age_group = models.CharField(max_length=100, blank=True, null=True) draft = models.BooleanField(default=False) + social_media_shares_count = models.IntegerField(default=0) + + def increment_shares(self): + self.social_media_shares_count += 1 + self.save() def __str__(self): return self.title @@ -187,3 +192,12 @@ class EventView(BaseModel): def __str__(self): return f"{self.principal.email} viewed {self.event.title} from {self.location}" + + +class EventShare(BaseModel): + event = models.ForeignKey( + Event, on_delete=models.CASCADE, related_name="social_media_shares" + ) + principal = models.ForeignKey( + IAmPrincipal, on_delete=models.CASCADE, related_name="event_shares" + ) diff --git a/manage_events/report.py b/manage_events/report.py index 94d237c..be32646 100644 --- a/manage_events/report.py +++ b/manage_events/report.py @@ -5,14 +5,24 @@ from datetime import timedelta from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import letter from reportlab.graphics.shapes import Drawing +from reportlab.lib import colors +from reportlab.lib.styles import getSampleStyleSheet from reportlab.graphics.charts.piecharts import Pie +from reportlab.platypus import ( + SimpleDocTemplate, + Table, + TableStyle, + Paragraph, + Spacer, + PageBreak, +) from io import BytesIO from django.conf import settings from collections import defaultdict 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, EventView +from manage_events.models import Event, EventInteractionType, EventShare, EventView User = get_user_model() @@ -40,13 +50,26 @@ def generate_event_report(user_id): start_date, end_date = get_previous_month_date_range() user = User.objects.get(id=user_id) - 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)), + events = Event.objects.filter( + created_by=user, start_date__gte=start_date, start_date__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) + ), views_count=Count("views", filter=Q(views__active=True, views__deleted=False)), ) + print("events: ", events) report_data = [] for event in events: @@ -55,154 +78,35 @@ def generate_event_report(user_id): for view in views: locations[view.location] += 1 - report_data.append({ - "event_name": event.title, - "event_type": event.category.title, - "event_date": str(event.start_date), - "favorites_count": event.favorites_count, - "interested_count": event.interested_count, - "going_count": event.going_count, - "reviews_count": event.reviews_count, - "views_count": event.views_count, - "locations": dict(locations), - }) - - 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) + shares = ( + EventShare.objects.filter(event=event) + .values("principal") + .annotate(share_count=Count("principal")) ) + shares_data = { + User.objects.get(id=share["principal"]).get_full_name(): share[ + "share_count" + ] + for share in shares + } - # 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 + report_data.append( + { + "event_name": event.title, + "event_type": event.category.title, + "event_date": str(event.start_date), + "favorites_count": event.favorites_count, + "interested_count": event.interested_count, + "going_count": event.going_count, + "reviews_count": event.reviews_count, + "views_count": event.views_count, + "locations": dict(locations), + "social_media_shares": event.social_media_shares_count, + "shares_data": shares_data, + } + ) + print("report_data: ", report_data) + return report_data def generate_event_report_pdf_three(user, report_data): @@ -210,116 +114,179 @@ def generate_event_report_pdf_three(user, report_data): filename = generate_filename(user.email, start_date) buffer = BytesIO() - pdf = canvas.Canvas(buffer, pagesize=letter) + # pdf = canvas.Canvas(buffer, pagesize=letter) + pdf = SimpleDocTemplate(buffer, pagesize=letter) width, height = letter + elements = [] + + styles = getSampleStyleSheet() # Header Section - title = "Good Times Ltd. Monthly Report" - report_for_month = f"Report for the month of - {start_date.strftime('%B %Y')}" - organiser_name = f"Name of the Organiser - {user.get_full_name()}" - contact_name = f"Contact Name - {user.get_full_name()}" + title = Paragraph("Good Times Ltd. Monthly Report", styles["Title"]) + report_for_month = Paragraph( + f"Report for the month of - {start_date.strftime('%B %Y')}", styles["Title"] + ) + organiser_name = Paragraph( + f"Name of the Organiser - {user.get_full_name()}", styles["Title"] + ) + contact_name = Paragraph(f"Contact Name - {user.get_full_name()}", styles["Title"]) - pdf.setFont("Helvetica-Bold", 20) - title_width = pdf.stringWidth(title, "Helvetica-Bold", 20) - pdf.drawString((width - title_width) / 2, 750, title) + elements.extend( + [ + title, + Spacer(1, 12), + report_for_month, + Spacer(1, 12), + organiser_name, + Spacer(1, 12), + contact_name, + PageBreak(), + ] + ) - pdf.setFont("Helvetica", 16) - report_for_month_width = pdf.stringWidth(report_for_month, "Helvetica", 16) - pdf.drawString((width - report_for_month_width) / 2, 720, report_for_month) - - organiser_name_width = pdf.stringWidth(organiser_name, "Helvetica", 16) - pdf.drawString((width - organiser_name_width) / 2, 690, organiser_name) - - contact_name_width = pdf.stringWidth(contact_name, "Helvetica", 16) - pdf.drawString((width - contact_name_width) / 2, 660, contact_name) - pdf.showPage() # Summary Section - pdf.setFont("Helvetica-Bold", 12) - pdf.drawString( - 100, - 640, - f"Number of Events added in {start_date.strftime('%B %Y')} - {len(report_data)}", + summary_text = ( + f"Number of Events added in {start_date.strftime('%B %Y')} - {len(report_data)}" ) + elements.append(Paragraph(summary_text, styles["Normal"])) + elements.append(Spacer(1, 12)) - y_pos = 620 - pdf.setFont("Helvetica-Bold", 10) - pdf.drawString(50, y_pos, "Event Name") - pdf.drawString(200, y_pos, "Event Type") - pdf.drawString(350, y_pos, "Date") - y_pos -= 20 + data = [["Sr No.", "Name of the Event", "Event Type", "Date"]] + for idx, event in enumerate(report_data, start=1): + data.append( + [ + idx, + event["event_name"], + event["event_type"], + event["event_date"], + ] + ) - pdf.setFont("Helvetica", 10) + table = Table(data, colWidths=[50, 200, 150, 100]) + style = TableStyle( + [ + ("BACKGROUND", (0, 0), (-1, 0), colors.grey), + ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke), + ("ALIGN", (0, 0), (-1, -1), "CENTER"), + ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), + ("BOTTOMPADDING", (0, 0), (-1, 0), 12), + ("BACKGROUND", (0, 1), (-1, -1), colors.beige), + ("GRID", (0, 0), (-1, -1), 1, colors.black), + ] + ) + table.setStyle(style) + + elements.append(table) + elements.append(PageBreak()) + + # Traffic Details Section + traffic_header = "Traffic Details for profile - Event Organisers London Ltd." + elements.append(Paragraph(traffic_header, styles["Heading2"])) + elements.append(Spacer(1, 12)) + + views_count = sum(event["views_count"] for event in report_data) + favorites_count = sum(event["favorites_count"] for event in report_data) + social_media_shares = sum(event["social_media_shares"] for event in report_data) + + elements.append( + Paragraph(f"Number of Event Views - {views_count}", styles["Normal"]) + ) + elements.append(Spacer(1, 12)) + elements.append( + Paragraph(f"Number of Event Favorites - {favorites_count}", styles["Normal"]) + ) + elements.append(Spacer(1, 12)) + elements.append( + Paragraph(f"Social Media Shares - {social_media_shares}", styles["Normal"]) + ) + # elements.append(PageBreak()) + elements.append(Spacer(1, 60)) + + # Top 5 Locations and Top 5 Viewed Events + all_locations = defaultdict(int) for event in report_data: - pdf.drawString(50, y_pos, event["event_name"]) - pdf.drawString(200, y_pos, event["event_type"]) - pdf.drawString(350, y_pos, event["event_date"]) - y_pos -= 20 + for location, count in event["locations"].items(): + all_locations[location] += count - # Start a new page for Traffic Details Section - pdf.showPage() + top_locations = sorted(all_locations.items(), key=lambda x: x[1], reverse=True)[:5] + top_events = sorted(report_data, key=lambda x: x["views_count"], reverse=True)[:5] - pdf.setFont("Helvetica-Bold", 12) - pdf.drawString( - 50, height - 50, "Traffic Details for profile - Event Organisers London Ltd." + data = [ + ["Top 5 Locations Viewed From", "Top 5 Viewed Events"], + ] + + for i in range(5): + location = top_locations[i][0] if i < len(top_locations) else "" + event = top_events[i]["event_name"] if i < len(top_events) else "" + data.append([location, event]) + + table = Table(data, colWidths=[200, 200]) + style = TableStyle( + [ + ("BACKGROUND", (0, 0), (-1, 0), colors.grey), + ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke), + ("ALIGN", (0, 0), (-1, -1), "CENTER"), + ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"), + ("BOTTOMPADDING", (0, 0), (-1, 0), 12), + ("BACKGROUND", (0, 1), (-1, -1), colors.beige), + ("GRID", (0, 0), (-1, -1), 1, colors.black), + ] ) + table.setStyle(style) + + elements.append(table) + elements.append(PageBreak()) # Detailed Review of Each Event for event in report_data: - pdf.showPage() - pdf.setFont("Helvetica-Bold", 12) - pdf.drawString(50, height - 50, f"Event Name - {event['event_name']}") - pdf.setFont("Helvetica", 10) - pdf.drawString(50, height - 70, f"Event Type - {event['event_type']}") - pdf.drawString(50, height - 90, f"Event Date - {event['event_date']}") + elements.append(Paragraph(f"Event Name - {event['event_name']}", styles['Heading2'])) + elements.append(Spacer(1, 12)) + elements.append(Paragraph(f"Event Type - {event['event_type']}", styles['Normal'])) + elements.append(Paragraph(f"Event Date - {event['event_date']}", styles['Normal'])) - y_pos = height - 120 - pdf.drawString(50, y_pos, f"Number of Views - {event['views_count']}") - pdf.drawString(200, y_pos, f"Favorites Count - {event['favorites_count']}") - pdf.drawString(350, y_pos, f"Interested in Going - {event['interested_count']}") - pdf.drawString(500, y_pos, f"Going - {event['going_count']}") - pdf.drawString(650, y_pos, f"Reviews - {event['reviews_count']}") - y_pos -= 40 + views = f"Number of Views - {event['views_count']}" + favorites = f"Favorites Count - {event['favorites_count']}" + interested = f"Interested in Going - {event['interested_count']}" + going = f"Going - {event['going_count']}" + reviews = f"Reviews - {event['reviews_count']}" + shares = f"Social Media Shares - {event['social_media_shares']}" - pdf.setFont("Helvetica-Bold", 10) - pdf.drawString(50, y_pos, "View Details:") - y_pos -= 20 - pdf.setFont("Helvetica", 10) - pdf.drawString(100, y_pos, "User Email") - pdf.drawString(300, y_pos, "Location") - pdf.drawString(500, y_pos, "View Date") - y_pos -= 20 - for view in event["locations"]: - pdf.drawString(100, y_pos, view["user__email"]) - pdf.drawString(300, y_pos, view["location"]) - pdf.drawString(500, y_pos, view["view_date"].strftime("%d %B %Y %H:%M")) - y_pos -= 20 + elements.append(Paragraph(views, styles['Normal'])) + elements.append(Paragraph(shares, styles['Normal'])) + elements.append(Paragraph(favorites, styles['Normal'])) + elements.append(Paragraph(interested, styles['Normal'])) + elements.append(Paragraph(going, styles['Normal'])) + elements.append(Paragraph(reviews, styles['Normal'])) + elements.append(Spacer(1, 12)) - # Draw pie chart on a new page if there's not enough space - if y_pos < 150: - pdf.showPage() - y_pos = height - 150 + location_details = [] + for location, count in event["locations"].items(): + location_details.append(f"{location}: {count}") + + elements.append(Paragraph("Event viewed from:", styles['Normal'])) + elements.append(Paragraph(", ".join(location_details), styles['Normal'])) + # elements.append(PageBreak()) + elements.append(Spacer(1, 60)) pie_data = [ event["views_count"], + event["social_media_shares"], event["favorites_count"], event["interested_count"], event["going_count"], ] - pie_labels = ["Views", "Favorites", "Interested", "Going"] + pie_labels = ["Views", "Shares", "Favorites", "Interested", "Going"] drawing = Drawing(200, 100) pie = Pie() - pie.x = 50 - pie.y = y_pos - 100 pie.data = pie_data pie.labels = pie_labels pie.width = 100 pie.height = 100 drawing.add(pie) - renderPDF.draw(drawing, pdf, 50, y_pos - 100) - y_pos -= 150 # Adjust for pie chart height - - pdf.save() + elements.append(drawing) + elements.append(PageBreak()) + pdf.build(elements) buffer.seek(0) pdf_data = buffer.read() buffer.close() From d09ff0f9735d9b54050dcea92f457b4d30999f02 Mon Sep 17 00:00:00 2001 From: rizwanisready Date: Mon, 3 Jun 2024 19:30:33 +0530 Subject: [PATCH 3/3] report and share api --- .../management/commands/manager_report.py | 33 ++++++++++++ manage_events/report.py | 50 ++++++++++++------- manage_events/views.py | 2 +- requirements.txt | 2 +- 4 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 manage_events/management/commands/manager_report.py diff --git a/manage_events/management/commands/manager_report.py b/manage_events/management/commands/manager_report.py new file mode 100644 index 0000000..8da35e6 --- /dev/null +++ b/manage_events/management/commands/manager_report.py @@ -0,0 +1,33 @@ +from django.core.management.base import BaseCommand +from django.core.mail import EmailMessage +from django.conf import settings +from manage_events.report import ( + get_previous_month_date_range, + event_managers, + generate_event_report, + generate_event_report_pdf_three, +) + + +class Command(BaseCommand): + help = "Send monthly event reports to event managers" + + def handle(self, *args, **kwargs): + start_date, end_date = get_previous_month_date_range() + users = event_managers() + + for user in users: + report_data = generate_event_report(user.id) + if report_data: + pdf_data, filename = generate_event_report_pdf_three(user, report_data) + self.send_email_with_attachment(user.email, pdf_data, filename) + + def send_email_with_attachment(self, email, pdf_data, filename): + email_message = EmailMessage( + subject="Monthly Event Report", + body="Please find the attached report for the last month.", + to=[email], + from_email=settings.EMAIL_HOST_USER, + ) + email_message.attach(filename, pdf_data, "application/pdf") + email_message.send() diff --git a/manage_events/report.py b/manage_events/report.py index be32646..19619f5 100644 --- a/manage_events/report.py +++ b/manage_events/report.py @@ -6,7 +6,7 @@ from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import letter from reportlab.graphics.shapes import Drawing from reportlab.lib import colors -from reportlab.lib.styles import getSampleStyleSheet +from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.graphics.charts.piecharts import Pie from reportlab.platypus import ( SimpleDocTemplate, @@ -21,6 +21,7 @@ from django.conf import settings from collections import defaultdict from reportlab.graphics import renderPDF from django.contrib.auth import get_user_model +from accounts.models import IAmPrincipalType from goodtimes.services import EmailService from manage_events.models import Event, EventInteractionType, EventShare, EventView @@ -37,6 +38,10 @@ def generate_filename(email, date): filename = f"{username}_{month_name}_report.pdf" return filename +def event_managers(): + principal_type = IAmPrincipalType.objects.filter(name="event_manager").first() + return User.objects.filter(principal_type=principal_type, is_active=True) + def get_previous_month_date_range(): today = timezone.now() @@ -69,7 +74,7 @@ def generate_event_report(user_id): ), views_count=Count("views", filter=Q(views__active=True, views__deleted=False)), ) - print("events: ", events) + # print("events: ", events) report_data = [] for event in events: @@ -105,7 +110,7 @@ def generate_event_report(user_id): "shares_data": shares_data, } ) - print("report_data: ", report_data) + # print("report_data: ", report_data) return report_data @@ -121,6 +126,14 @@ def generate_event_report_pdf_three(user, report_data): styles = getSampleStyleSheet() + custom_style = ParagraphStyle( + name="Custom", + parent=styles["Normal"], + fontName="Helvetica", + fontSize=14, + leading=18, + spaceAfter=12, + ) # Header Section title = Paragraph("Good Times Ltd. Monthly Report", styles["Title"]) report_for_month = Paragraph( @@ -148,8 +161,8 @@ def generate_event_report_pdf_three(user, report_data): summary_text = ( f"Number of Events added in {start_date.strftime('%B %Y')} - {len(report_data)}" ) - elements.append(Paragraph(summary_text, styles["Normal"])) - elements.append(Spacer(1, 12)) + elements.append(Paragraph(summary_text, styles["Title"])) + elements.append(Spacer(1, 24)) data = [["Sr No.", "Name of the Event", "Event Type", "Date"]] for idx, event in enumerate(report_data, start=1): @@ -204,12 +217,15 @@ def generate_event_report_pdf_three(user, report_data): # Top 5 Locations and Top 5 Viewed Events all_locations = defaultdict(int) + print("all_locations: ", all_locations) for event in report_data: for location, count in event["locations"].items(): all_locations[location] += count top_locations = sorted(all_locations.items(), key=lambda x: x[1], reverse=True)[:5] + print("top_locations: ", top_locations) top_events = sorted(report_data, key=lambda x: x["views_count"], reverse=True)[:5] + print("top_events: ", top_events) data = [ ["Top 5 Locations Viewed From", "Top 5 Viewed Events"], @@ -239,10 +255,10 @@ def generate_event_report_pdf_three(user, report_data): # Detailed Review of Each Event for event in report_data: - elements.append(Paragraph(f"Event Name - {event['event_name']}", styles['Heading2'])) + elements.append(Paragraph(f"Event Name - {event['event_name']}", styles['Heading1'])) elements.append(Spacer(1, 12)) - elements.append(Paragraph(f"Event Type - {event['event_type']}", styles['Normal'])) - elements.append(Paragraph(f"Event Date - {event['event_date']}", styles['Normal'])) + elements.append(Paragraph(f"Event Type - {event['event_type']}", custom_style)) + elements.append(Paragraph(f"Event Date - {event['event_date']}", custom_style)) views = f"Number of Views - {event['views_count']}" favorites = f"Favorites Count - {event['favorites_count']}" @@ -251,22 +267,22 @@ def generate_event_report_pdf_three(user, report_data): reviews = f"Reviews - {event['reviews_count']}" shares = f"Social Media Shares - {event['social_media_shares']}" - elements.append(Paragraph(views, styles['Normal'])) - elements.append(Paragraph(shares, styles['Normal'])) - elements.append(Paragraph(favorites, styles['Normal'])) - elements.append(Paragraph(interested, styles['Normal'])) - elements.append(Paragraph(going, styles['Normal'])) - elements.append(Paragraph(reviews, styles['Normal'])) + elements.append(Paragraph(views, custom_style)) + elements.append(Paragraph(shares, custom_style)) + elements.append(Paragraph(favorites, custom_style)) + elements.append(Paragraph(interested, custom_style)) + elements.append(Paragraph(going, custom_style)) + elements.append(Paragraph(reviews, custom_style)) elements.append(Spacer(1, 12)) location_details = [] for location, count in event["locations"].items(): location_details.append(f"{location}: {count}") - elements.append(Paragraph("Event viewed from:", styles['Normal'])) - elements.append(Paragraph(", ".join(location_details), styles['Normal'])) + elements.append(Paragraph("Event viewed from:", custom_style)) + elements.append(Paragraph(", ".join(location_details), custom_style)) # elements.append(PageBreak()) - elements.append(Spacer(1, 60)) + elements.append(Spacer(1, 48)) pie_data = [ event["views_count"], diff --git a/manage_events/views.py b/manage_events/views.py index 89c3336..0bfcc9e 100644 --- a/manage_events/views.py +++ b/manage_events/views.py @@ -500,5 +500,5 @@ class GenerateEventReportView(generic.View): # 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 094748b..e80e07b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -68,7 +68,7 @@ sqlparse==0.4.4 stripe==8.2.0 tqdm==4.66.2 Twisted==23.10.0 -twisted-iocpsupport==1.0.4 +# twisted-iocpsupport==1.0.4 txaio==23.1.1 typing_extensions==4.9.0 tzdata==2024.1