event view count
This commit is contained in:
20
accounts/migrations/0010_alter_appversion_app_type.py
Normal file
20
accounts/migrations/0010_alter_appversion_app_type.py
Normal file
@@ -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
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -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)
|
||||
|
||||
@@ -111,4 +111,10 @@ urlpatterns = [
|
||||
name="principal-events",
|
||||
),
|
||||
path("tags/", views.TagListView.as_view(), name="tag-list"),
|
||||
# For counting event views
|
||||
path(
|
||||
"event/<int:pk>/view/",
|
||||
views.CaptureEventViewAPIView.as_view(),
|
||||
name="capture_event_view",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
75
manage_events/migrations/0009_eventview.py
Normal file
75
manage_events/migrations/0009_eventview.py
Normal file
@@ -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,
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -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}"
|
||||
|
||||
213
manage_events/report.py
Normal file
213
manage_events/report.py
Normal file
@@ -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
|
||||
@@ -89,4 +89,9 @@ urlpatterns = [
|
||||
views.VenueDeleteView.as_view(),
|
||||
name="venue_delete",
|
||||
),
|
||||
path(
|
||||
"generate-event-report/<int:user_id>/",
|
||||
views.GenerateEventReportView.as_view(),
|
||||
name="generate_event_report",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user