CI Python Linter

from django.shortcuts import render, get_object_or_404, redirect from tutor.models import Tutor, TimeSlot from personaluser.models import Profile from booking.models import Booking from django.contrib.auth.decorators import login_required from datetime import datetime, timedelta from django.http import JsonResponse from django.contrib import messages from django.core.mail import EmailMultiAlternatives from django.template.loader import render_to_string import stripe from django.conf import settings from django.db import transaction, models from django.db.models import Count from decimal import Decimal from django.views.decorators.csrf import csrf_protect # Set the Stripe API key from settings stripe.api_key = settings.STRIPE_SECRET_KEY def get_available_dates(tutor): """ Generate a list of available dates for the given tutor based on their day availability for the next 30 days. Today's date is excluded, and users must book at least 24 hours in advance. Args: tutor (Tutor): The tutor for whom to check availability. Returns: List[str]: A list of available dates in 'dd-mm-yyyy' format. """ today = datetime.today() available_dates = [] # Get a set of available weekday indices from tutor's day availability available_weekdays = { day.name: day.order for day in tutor.day_availability.all() } # Get the next 30 days for i in range(30): current_day = today + timedelta(days=i) # Skip today entirely if current_day.date() == today.date(): continue # Check if the current day's weekday is in set of available weekdays if current_day.strftime('%A') in available_weekdays: # Check the total time slots for this day total_time_slots = tutor.time_availability.count() # Count how many unique time slots are booked for this day booked_time_slots_count = Booking.objects.filter( session_date=current_day.date(), tutor=tutor ).aggregate( total_booked=Count('session_time') )['total_booked'] or 0 # If the number of booked time slots is equal to or greater than # the total time slots, it's fully booked if booked_time_slots_count >= total_time_slots: continue # Skip adding this date as it's fully booked # If there are available time slots, add date to available dates available_dates.append(current_day.strftime('%d-%m-%Y')) # Filter out dates that are less than 24 hours from now available_dates = [ date for date in available_dates if ( datetime.strptime(date, '%d-%m-%Y') - today ).days >= 1 ] return available_dates @login_required def get_available_time_slots(request): """ Retrieves available time slots for a specific tutor and session date. Args: request (HttpRequest): The HTTP request object. Returns: JsonResponse: A JSON response containing available time slots or an error message. """ if request.method == "GET": tutor_id = request.GET.get('tutor_id') session_date_str = request.GET.get('session_date') if not tutor_id or not session_date_str: return JsonResponse({'error': 'Invalid parameters'}, status=400) tutor = get_object_or_404(Tutor, id=tutor_id) try: session_date = datetime.strptime( session_date_str, '%d-%m-%Y' ).date() booked_time_slots = Booking.objects.filter( session_date=session_date, tutor=tutor).values_list('session_time', flat=True) available_time_slots = tutor.time_availability.exclude( id__in=booked_time_slots) data = [{'id': ts.id, 'name': str(ts)} for ts in available_time_slots] # Prepare data for response return JsonResponse(data, safe=False) except ValueError: return JsonResponse({'error': 'Invalid date format'}, status=400) @login_required def booking_create(request): """ Handles booking creation and Stripe payment integration. Args: request (HttpRequest): The HTTP request object. Returns: HttpResponse: Renders the booking creation page or redirects to the success page upon successful booking. """ profile = get_object_or_404(Profile, personal_details=request.user) tutor_id = request.session.get('tutor_id') if not tutor_id: return render(request, 'error.html', { 'message': 'No tutor selected. Please select a tutor first.' }) tutor = get_object_or_404(Tutor, id=tutor_id) session_date = None if request.method == 'POST': session_date_str = request.POST.get('session_date') # Get session date selected_time_slots = request.POST.getlist('time_slots') stripe_pid = request.POST.get('stripe_pid') # Stripe Payment Intent ID if not session_date_str: return render(request, 'error.html', { 'message': 'Session date is required.' }) try: session_date = datetime.strptime( session_date_str, '%d-%m-%Y' ).date() except ValueError: return render(request, 'error.html', { 'message': 'Invalid session date format.' }) if not selected_time_slots: return render(request, 'error.html', { 'message': 'Please select at least one time slot.' }) # Calculate total price total_price = len(selected_time_slots) * float(tutor.price) try: # Confirm the payment status using Stripe API payment_intent = stripe.PaymentIntent.retrieve(stripe_pid) if payment_intent['status'] != 'succeeded': messages.error(request, "Payment failed. Please try again.") return render(request, 'error.html', { 'message': 'Payment was not successful. \ Booking cannot be confirmed.' }) # Create a provisional booking only if payment is successful booking = Booking( user=profile, tutor=tutor, session_date=session_date, total_price=total_price, stripe_pid=stripe_pid, # Save the Stripe Payment Intent ID payment_status='paid' # Set the payment status to paid ) booking.user_fullname = f"{profile.personal_firstname} \ {profile.personal_lastname}" booking.user_email = profile.personal_details.email booking.tutor_fullname = f"{tutor.tutor_firstname} \ {tutor.tutor_lastname}" booking.tutor_email = tutor.tutor_email booking.save() # Save booking first for time_slot_id in selected_time_slots: time_slot = get_object_or_404(TimeSlot, id=time_slot_id) # Associate time slot with the booking booking.session_time.add(time_slot) messages.success( request, "Your booking has been successfully booked!" ) # Recalculate availability after saving booking available_dates = get_available_dates(tutor) fully_booked_dates = [] # Clear previous fully booked dates today = datetime.today() # Check again for fully booked dates based on all bookings after # new booking is saved. for i in range(1, 30): # Start from 1 to skip today current_day = today + timedelta(days=i) total_time_slots = tutor.time_availability.count() booked_time_slots_count = Booking.objects.filter( session_date=current_day.date(), tutor=tutor ).aggregate( total_booked=Count('session_time') )['total_booked'] or 0 if booked_time_slots_count >= total_time_slots: fully_booked_dates.append(current_day.strftime('%d-%m-%Y')) send_booking_confirmation_email(profile, booking) return redirect('booking_success', booking_id=booking.id) except Exception as e: print(f"Error saving booking: {e}") return render(request, 'error.html', { 'message': 'An error occurred while processing your booking.' }) available_dates = get_available_dates(tutor) available_time_slots = tutor.time_availability.all() if session_date: booked_time_slots = Booking.objects.filter( session_date=session_date, tutor=tutor ).values_list('session_time', flat=True) available_time_slots = available_time_slots.exclude( id__in=booked_time_slots ) today = datetime.today() fully_booked_dates = [] for i in range(1, 30): # Start from 1 to skip today current_day = today + timedelta(days=i) total_time_slots = tutor.time_availability.count() booked_time_slots_count = Booking.objects.filter( session_date=current_day.date(), tutor=tutor ).aggregate(total_booked=Count('session_time'))['total_booked'] or 0 if booked_time_slots_count >= total_time_slots: fully_booked_dates.append(current_day.strftime('%d-%m-%Y')) return render(request, 'booking/booking_create.html', { 'tutor': tutor, 'profile': profile, 'available_dates': available_dates, 'fully_booked_dates': fully_booked_dates, 'available_time_slots': available_time_slots, 'stripe_public_key': settings.STRIPE_PUBLIC_KEY, }) @csrf_protect def create_payment_intent(request): """ Creates a Stripe Payment Intent based on the total price and selected tutor. Args: request (HttpRequest): The HTTP request object. Returns: JsonResponse: A JSON response containing the client secret of the created Payment Intent or an error message. """ if request.method == "POST": total_price = request.POST.get('total_price') tutor_id = request.POST.get('tutor') # Tutor ID sent from the frontend # Get the profile of the currently logged-in user user_profile = request.user.profile try: # Fetch the tutor based on ID from the request tutor = Tutor.objects.get(id=tutor_id) except Tutor.DoesNotExist: return JsonResponse({'error': 'Tutor not found'}, status=400) try: # Access tutor's first name and last name from the Tutor model tutor_first_name = tutor.tutor_firstname tutor_last_name = tutor.tutor_lastname tutor_email = tutor.tutor_email # Create a Payment Intent with Stripe payment_intent = stripe.PaymentIntent.create( amount=int(float(total_price) * 100), # Convert to cents currency='eur', metadata={ 'user': user_profile.personal_details.username, 'tutor_firstname': tutor_first_name, 'tutor_lastname': tutor_last_name, 'tutor_email': tutor_email, } ) return JsonResponse( {'client_secret': payment_intent['client_secret']} ) except Exception as e: print(f"Error creating Payment Intent: {e}") return JsonResponse({'error': str(e)}, status=400) @login_required def booking_success(request, booking_id): """ Displays booking confirmation details. Args: request (HttpRequest): The HTTP request object. booking_id (int): The ID of the completed booking. Returns: HttpResponse: Renders the success page with booking details or an error message. """ # Retrieve the booking by ID. booking = get_object_or_404(Booking, id=booking_id) # Ensure the booking belongs to the logged-in user. if booking.user != request.user.profile: return render(request, 'error.html', {'message': 'Booking not found.'}) # Check payment status and set appropriate message. if booking.payment_status == 'paid': messages.success( request, "Your payment was successful! Details of your \ booking are below." ) elif booking.payment_status == 'failed': messages.error( request, "Payment failed. Please retry or contact support." ) return render( request, 'booking/booking_success.html', {'booking': booking} ) def send_booking_confirmation_email(user_profile, booking): """ Sends a confirmation email to both user and tutor after successful booking. Args: user_profile (Profile): The profile of the user who made the booking. booking (Booking): The Booking instance containing details of the reservation. """ subject_template = 'emails/booking_confirmation_email_subject.txt' body_template = 'emails/booking_confirmation_email_body.html' # Render subject and body from templates. subject = render_to_string(subject_template, {'booking': booking}).strip() context = { 'user': user_profile, 'booking': booking, } # Render HTML content from body template. html_content = render_to_string(body_template, context) # Create email message for user. user_email = user_profile.personal_details.email email_to_user = EmailMultiAlternatives( subject=subject, body='', from_email='arsenalpure95@gmail.com', to=[user_email] ) email_to_user.attach_alternative(html_content, "text/html") # Send email to user. email_to_user.send() # Create email message for tutor. tutor_email = booking.tutor.tutor_email email_to_tutor = EmailMultiAlternatives( subject=subject, body='', from_email='arsenalpure95@gmail.com', to=[tutor_email] ) email_to_tutor.attach_alternative(html_content, "text/html") # Send email to tutor. email_to_tutor.send()

Settings:


Results:

All clear, no errors found