import csv import io from django.shortcuts import render, redirect, get_object_or_404 from django.db.models import Count, Sum from django.utils import timezone from django.contrib import messages from .models import Property, Guest, Stay, Campaign from .mail import send_campaign_email def home(request): """Host Dashboard""" total_guests = Guest.objects.count() total_properties = Property.objects.count() total_stays = Stay.objects.count() recent_stays = Stay.objects.select_related('guest', 'property').order_by('-check_in')[:5] recent_guests = Guest.objects.order_by('-created_at')[:5] context = { 'total_guests': total_guests, 'total_properties': total_properties, 'total_stays': total_stays, 'recent_stays': recent_stays, 'recent_guests': recent_guests, 'project_name': 'Host Loyalty CRM' } return render(request, "core/index.html", context) def guest_list(request): """List of all guests""" guests = Guest.objects.annotate(stay_count=Count('stays')).order_by('-created_at') return render(request, "core/guest_list.html", {'guests': guests}) def import_guests(request): """Import guests from a CSV file""" if request.method == 'POST' and request.FILES.get('csv_file'): csv_file = request.FILES['csv_file'] if not csv_file.name.endswith('.csv'): messages.error(request, 'Please upload a CSV file.') return redirect('import_guests') try: decoded_file = csv_file.read().decode('utf-8') io_string = io.StringIO(decoded_file) reader = csv.DictReader(io_string) # Simple column mapping/validation required_cols = {'first_name', 'last_name', 'email'} if not required_cols.issubset(set(reader.fieldnames or [])): messages.error(request, f'CSV must contain columns: {", ".join(required_cols)}') return redirect('import_guests') created_count = 0 updated_count = 0 for row in reader: guest, created = Guest.objects.update_or_create( email=row['email'].strip().lower(), defaults={ 'first_name': row['first_name'].strip(), 'last_name': row['last_name'].strip(), 'phone': row.get('phone', '').strip(), } ) if created: created_count += 1 else: updated_count += 1 messages.success(request, f'Successfully imported {created_count} new guests and updated {updated_count}.') return redirect('guest_list') except Exception as e: messages.error(request, f'Error processing file: {str(e)}') return redirect('import_guests') return render(request, "core/import_guests.html") def campaign_list(request): """List of email campaigns""" campaigns = Campaign.objects.all().order_by('-created_at') return render(request, "core/campaign_list.html", {'campaigns': campaigns}) def send_campaign(request, pk): """Send a campaign to all guests""" campaign = get_object_or_404(Campaign, pk=pk) if campaign.status == 'sent': messages.warning(request, "This campaign has already been sent.") return redirect('campaign_list') guests = Guest.objects.all() if not guests: messages.error(request, "No guests found to send the campaign to.") return redirect('campaign_list') success_count, fail_count = send_campaign_email(campaign, guests) campaign.status = 'sent' campaign.sent_at = timezone.now() campaign.save() if success_count > 0: messages.success(request, f"Campaign sent successfully to {success_count} guests.") if fail_count > 0: messages.error(request, f"Failed to send to {fail_count} guests.") return redirect('campaign_list')