39435-vm/core/views.py
2026-04-02 16:08:07 +00:00

298 lines
9.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import calendar
from collections import defaultdict
from datetime import datetime, time, timedelta
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils import timezone
from .forms import EventForm
from .models import Event
PROJECT_NAME = 'Roadshow Calendar'
DEFAULT_META_DESCRIPTION = 'Track where your business will be each day with a polished public calendar and secure staff-only event management.'
def _parse_month(month_value: str | None):
today = timezone.localdate()
if month_value:
try:
parsed = datetime.strptime(month_value, '%Y-%m').date()
return parsed.replace(day=1)
except ValueError:
pass
return today.replace(day=1)
def _next_month(month_start):
return (month_start.replace(day=28) + timedelta(days=4)).replace(day=1)
def _previous_month(month_start):
return (month_start - timedelta(days=1)).replace(day=1)
def _build_month_context(month_value=None):
month_start = _parse_month(month_value)
next_month = _next_month(month_start)
tz = timezone.get_current_timezone()
range_start = timezone.make_aware(datetime.combine(month_start, time.min), tz)
range_end = timezone.make_aware(datetime.combine(next_month, time.min), tz)
events = list(
Event.objects.filter(is_published=True, start__lt=range_end, end__gte=range_start).order_by('start', 'name')
)
event_map = defaultdict(list)
for event in events:
start_local = timezone.localtime(event.start)
end_local = timezone.localtime(event.end)
current_day = start_local.date()
final_day = end_local.date()
while current_day <= final_day:
event_map[current_day.isoformat()].append(
{
'name': event.name,
'location': event.location,
'time': f"{start_local:%I:%M %p} {end_local:%I:%M %p}" if start_local.date() == end_local.date() else f"{start_local:%b %d, %I:%M %p} {end_local:%b %d, %I:%M %p}",
'summary': event.summary,
'event_url': event.event_url,
'detail_url': reverse('event_detail', kwargs={'slug': event.slug}),
}
)
current_day += timedelta(days=1)
month_calendar = calendar.Calendar(firstweekday=6)
today = timezone.localdate()
calendar_weeks = []
for week in month_calendar.monthdatescalendar(month_start.year, month_start.month):
week_days = []
for day in week:
iso = day.isoformat()
day_events = event_map.get(iso, [])
week_days.append(
{
'date': day,
'iso': iso,
'day': day.day,
'in_month': day.month == month_start.month,
'is_today': day == today,
'event_count': len(day_events),
'has_events': bool(day_events),
}
)
calendar_weeks.append(week_days)
return {
'calendar_weeks': calendar_weeks,
'calendar_events': dict(event_map),
'month_label': month_start.strftime('%B %Y'),
'month_value': month_start.strftime('%Y-%m'),
'prev_month': _previous_month(month_start).strftime('%Y-%m'),
'next_month': next_month.strftime('%Y-%m'),
'today_value': today.strftime('%Y-%m'),
}
def _base_context(**extra):
context = {
'project_name': PROJECT_NAME,
'meta_description': DEFAULT_META_DESCRIPTION,
}
context.update(extra)
return context
def _embed_base_url(request):
return request.build_absolute_uri(reverse('calendar_embed'))
def _show_embed_header(request):
return request.GET.get('header', '1') != '0'
@login_required(login_url='login')
def event_dashboard(request):
if not request.user.is_staff:
raise PermissionDenied
events = Event.objects.all().order_by('start', 'name')
return render(
request,
'core/event_dashboard.html',
_base_context(
page_title='Manage events',
events=events,
dashboard_count=events.count(),
embed_base_url=_embed_base_url(request),
default_embed_month=timezone.localdate().replace(day=1).strftime('%Y-%m'),
),
)
@login_required(login_url='login')
def event_create(request):
if not request.user.is_staff:
raise PermissionDenied
if request.method == 'POST':
form = EventForm(request.POST)
if form.is_valid():
event = form.save()
messages.success(request, 'Event saved and ready for the public calendar.')
return redirect('event_dashboard_detail', slug=event.slug)
else:
form = EventForm()
return render(
request,
'core/event_form.html',
_base_context(
page_title='Add event',
form=form,
form_mode='create',
form_title='Add a new stop to the calendar',
form_intro='Once saved, published events immediately power the landing page, public calendar, and embeddable widget.',
submit_label='Save event',
),
)
@login_required(login_url='login')
def event_edit(request, slug):
if not request.user.is_staff:
raise PermissionDenied
event = get_object_or_404(Event, slug=slug)
if request.method == 'POST':
form = EventForm(request.POST, instance=event)
if form.is_valid():
event = form.save()
messages.success(request, 'Event updated successfully.')
return redirect('event_dashboard_detail', slug=event.slug)
else:
form = EventForm(instance=event)
return render(
request,
'core/event_form.html',
_base_context(
page_title=f'Edit {event.name}',
form=form,
event=event,
form_mode='edit',
form_title='Update this calendar stop',
form_intro='Change dates, copy, publication status, or the event link without leaving the custom dashboard.',
submit_label='Save changes',
),
)
@login_required(login_url='login')
def event_delete(request, slug):
if not request.user.is_staff:
raise PermissionDenied
event = get_object_or_404(Event, slug=slug)
if request.method == 'POST':
event_name = event.name
event.delete()
messages.success(request, f'{event_name} was deleted.')
return redirect('event_dashboard')
return render(
request,
'core/event_confirm_delete.html',
_base_context(
page_title=f'Delete {event.name}',
event=event,
),
)
@login_required(login_url='login')
def event_dashboard_detail(request, slug):
if not request.user.is_staff:
raise PermissionDenied
event = get_object_or_404(Event, slug=slug)
return render(
request,
'core/event_dashboard_detail.html',
_base_context(
page_title=event.name,
event=event,
),
)
def home(request):
month_context = _build_month_context(request.GET.get('month'))
upcoming_events = list(Event.objects.filter(is_published=True, end__gte=timezone.now()).order_by('start', 'name')[:6])
embed_url = _embed_base_url(request)
iframe_snippet = f'<iframe src="{embed_url}" title="Where to find us calendar" width="100%" height="760" style="border:0;border-radius:24px;overflow:hidden;"></iframe>'
return render(
request,
'core/index.html',
_base_context(
page_title=PROJECT_NAME,
hero_events=upcoming_events[:3],
upcoming_events=upcoming_events,
embed_url=embed_url,
iframe_snippet=iframe_snippet,
**month_context,
),
)
def calendar_page(request):
month_context = _build_month_context(request.GET.get('month'))
return render(
request,
'core/calendar_page.html',
_base_context(
page_title='Public calendar',
**month_context,
),
)
def calendar_embed(request):
month_context = _build_month_context(request.GET.get('month'))
return render(
request,
'core/calendar_embed.html',
_base_context(
page_title='Embeddable calendar',
embedded=True,
show_embed_header=_show_embed_header(request),
**month_context,
),
)
def event_list(request):
events = Event.objects.filter(is_published=True).order_by('start', 'name')
return render(
request,
'core/event_list.html',
_base_context(
page_title='Upcoming events',
events=events,
),
)
def event_detail(request, slug):
event = get_object_or_404(Event, slug=slug, is_published=True)
return render(
request,
'core/event_detail.html',
_base_context(
page_title=event.name,
event=event,
),
)