commit e6dc52ca4d87002df47ee18fa47f9bd3777f5fc3 Author: cesar98 Date: Tue Sep 30 23:09:47 2025 +0300 Part 1: Booking API with Swagger and custom endpoints diff --git a/booking_system/__init__.py b/booking_system/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/booking_system/__pycache__/__init__.cpython-311.pyc b/booking_system/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..8053daf Binary files /dev/null and b/booking_system/__pycache__/__init__.cpython-311.pyc differ diff --git a/booking_system/__pycache__/settings.cpython-311.pyc b/booking_system/__pycache__/settings.cpython-311.pyc new file mode 100644 index 0000000..d4b01e9 Binary files /dev/null and b/booking_system/__pycache__/settings.cpython-311.pyc differ diff --git a/booking_system/__pycache__/urls.cpython-311.pyc b/booking_system/__pycache__/urls.cpython-311.pyc new file mode 100644 index 0000000..162be03 Binary files /dev/null and b/booking_system/__pycache__/urls.cpython-311.pyc differ diff --git a/booking_system/__pycache__/wsgi.cpython-311.pyc b/booking_system/__pycache__/wsgi.cpython-311.pyc new file mode 100644 index 0000000..f370ece Binary files /dev/null and b/booking_system/__pycache__/wsgi.cpython-311.pyc differ diff --git a/booking_system/asgi.py b/booking_system/asgi.py new file mode 100644 index 0000000..1074db8 --- /dev/null +++ b/booking_system/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for booking_system project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'booking_system.settings') + +application = get_asgi_application() diff --git a/booking_system/settings.py b/booking_system/settings.py new file mode 100644 index 0000000..2bad364 --- /dev/null +++ b/booking_system/settings.py @@ -0,0 +1,180 @@ +""" +Django settings for booking_system project. + +Generated by 'django-admin startproject' using Django 5.2.6. + +For more information on this file, see +https://docs.djangoproject.com/en/5.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.2/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-i9ut%*0nx@(z7ta7)5uly(3xxjp(5d%172$rk5b0on6ch9f1_r' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + # third-party + 'rest_framework', + 'drf_yasg', + + # our app + 'bookings', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "bookings.middleware.RequestLoggingMiddleware", +] + +ROOT_URLCONF = 'booking_system.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + "django.template.context_processors.debug", + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'booking_system.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.2/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# Django REST Framework default +REST_FRAMEWORK = { + "DEFAULT_RENDERER_CLASSES": ( + "rest_framework.renderers.JSONRenderer", + ), + "DEFAULT_PARSER_CLASSES": ( + "rest_framework.parsers.JSONParser", + ), +} + +# ------------------------- +# Logging config: write to api.log +# ------------------------- +LOG_DIR = BASE_DIR / "logs" +LOG_DIR.mkdir(exist_ok=True) +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "standard": { + "format": "[%(asctime)s] %(levelname)s %(name)s: %(message)s" + }, + }, + "handlers": { + "file": { + "level": "INFO", + "class": "logging.FileHandler", + "filename": str(LOG_DIR / "api.log"), + "formatter": "standard", + }, + "console": { + "class": "logging.StreamHandler", + "formatter": "standard", + }, + }, + "loggers": { + # root logger + "": { + "handlers": ["file", "console"], + "level": "INFO", + "propagate": True, + }, + "django.request": { + "handlers": ["file", "console"], + "level": "ERROR", + "propagate": False, + }, + }, +} \ No newline at end of file diff --git a/booking_system/urls.py b/booking_system/urls.py new file mode 100644 index 0000000..5fe6c52 --- /dev/null +++ b/booking_system/urls.py @@ -0,0 +1,41 @@ +""" +URL configuration for booking_system project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +# booking_api/urls.py +from django.contrib import admin +from django.urls import path, include +from rest_framework import permissions +from drf_yasg.views import get_schema_view +from drf_yasg import openapi + +schema_view = get_schema_view( + openapi.Info( + title="Booking API", + default_version="v1", + description="Demo booking API with Venues and Bookings", + contact=openapi.Contact(email="dev@example.com"), + ), + public=True, + permission_classes=(permissions.AllowAny,), +) + +urlpatterns = [ + path("admin/", admin.site.urls), + path("api/", include("bookings.urls")), + # Swagger UI: + path("swagger/", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"), + path("swagger.json", schema_view.without_ui(cache_timeout=0), name="schema-json"), +] diff --git a/booking_system/wsgi.py b/booking_system/wsgi.py new file mode 100644 index 0000000..7911a9a --- /dev/null +++ b/booking_system/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for booking_system project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'booking_system.settings') + +application = get_wsgi_application() diff --git a/bookings/__init__.py b/bookings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bookings/__pycache__/__init__.cpython-311.pyc b/bookings/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..302c5a2 Binary files /dev/null and b/bookings/__pycache__/__init__.cpython-311.pyc differ diff --git a/bookings/__pycache__/admin.cpython-311.pyc b/bookings/__pycache__/admin.cpython-311.pyc new file mode 100644 index 0000000..46137f7 Binary files /dev/null and b/bookings/__pycache__/admin.cpython-311.pyc differ diff --git a/bookings/__pycache__/apps.cpython-311.pyc b/bookings/__pycache__/apps.cpython-311.pyc new file mode 100644 index 0000000..a72b07b Binary files /dev/null and b/bookings/__pycache__/apps.cpython-311.pyc differ diff --git a/bookings/__pycache__/middleware.cpython-311.pyc b/bookings/__pycache__/middleware.cpython-311.pyc new file mode 100644 index 0000000..41d862d Binary files /dev/null and b/bookings/__pycache__/middleware.cpython-311.pyc differ diff --git a/bookings/__pycache__/models.cpython-311.pyc b/bookings/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000..a6378e9 Binary files /dev/null and b/bookings/__pycache__/models.cpython-311.pyc differ diff --git a/bookings/__pycache__/serializers.cpython-311.pyc b/bookings/__pycache__/serializers.cpython-311.pyc new file mode 100644 index 0000000..8c4ef30 Binary files /dev/null and b/bookings/__pycache__/serializers.cpython-311.pyc differ diff --git a/bookings/__pycache__/urls.cpython-311.pyc b/bookings/__pycache__/urls.cpython-311.pyc new file mode 100644 index 0000000..7c2ae97 Binary files /dev/null and b/bookings/__pycache__/urls.cpython-311.pyc differ diff --git a/bookings/__pycache__/views.cpython-311.pyc b/bookings/__pycache__/views.cpython-311.pyc new file mode 100644 index 0000000..8853b1d Binary files /dev/null and b/bookings/__pycache__/views.cpython-311.pyc differ diff --git a/bookings/admin.py b/bookings/admin.py new file mode 100644 index 0000000..bf7bda3 --- /dev/null +++ b/bookings/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin +from .models import Venue, Booking + +@admin.register(Venue) +class VenueAdmin(admin.ModelAdmin): + list_display = ("id", "name", "capacity", "address") + +@admin.register(Booking) +class BookingAdmin(admin.ModelAdmin): + list_display = ("id", "venue", "customer_name", "start_time", "end_time", "status") + list_filter = ("status", "venue") diff --git a/bookings/apps.py b/bookings/apps.py new file mode 100644 index 0000000..2b3d34c --- /dev/null +++ b/bookings/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class BookingsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'bookings' diff --git a/bookings/middleware.py b/bookings/middleware.py new file mode 100644 index 0000000..eba757c --- /dev/null +++ b/bookings/middleware.py @@ -0,0 +1,41 @@ +# bookings/middleware.py +import logging +import time +import json + +logger = logging.getLogger(__name__) + +class RequestLoggingMiddleware: + """ + Logs request start, method, path, body (if small), response status and duration. + Place this middleware after common Django middleware as configured in settings.py. + """ + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + start = time.time() + try: + # basic request info + method = request.method + path = request.get_full_path() + # try to capture small JSON bodies safely + body = None + try: + if method in ("POST", "PUT", "PATCH") and request.body: + raw = request.body.decode("utf-8")[:2000] + body = raw + except Exception: + body = "" + + logger.info(f"Request start: {method} {path} body={body}") + + response = self.get_response(request) + + duration = time.time() - start + logger.info(f"Request finished: {method} {path} status={response.status_code} time={duration:.3f}s") + return response + except Exception as ex: + # log exception details + logger.exception(f"Unhandled exception during request: {ex}") + raise diff --git a/bookings/migrations/0001_initial.py b/bookings/migrations/0001_initial.py new file mode 100644 index 0000000..2f8d9d4 --- /dev/null +++ b/bookings/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 5.2.6 on 2025-09-30 18:07 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Venue', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('address', models.CharField(blank=True, max_length=300)), + ('capacity', models.PositiveIntegerField(default=0)), + ], + ), + migrations.CreateModel( + name='Booking', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('customer_name', models.CharField(max_length=200)), + ('customer_email', models.EmailField(max_length=254)), + ('start_time', models.DateTimeField()), + ('end_time', models.DateTimeField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('status', models.CharField(choices=[('CONFIRMED', 'Confirmed'), ('CANCELLED', 'Cancelled')], default='CONFIRMED', max_length=20)), + ('venue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bookings', to='bookings.venue')), + ], + ), + ] diff --git a/bookings/migrations/__init__.py b/bookings/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bookings/migrations/__pycache__/0001_initial.cpython-311.pyc b/bookings/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000..3db58d4 Binary files /dev/null and b/bookings/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/bookings/migrations/__pycache__/__init__.cpython-311.pyc b/bookings/migrations/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..13356ff Binary files /dev/null and b/bookings/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/bookings/models.py b/bookings/models.py new file mode 100644 index 0000000..6fb3359 --- /dev/null +++ b/bookings/models.py @@ -0,0 +1,32 @@ +from django.db import models + +# Create your models here.# bookings/models.py + +class Venue(models.Model): + name = models.CharField(max_length=100) + city = models.CharField(max_length=50) + capacity = models.IntegerField() + description = models.TextField(blank=True, null=True) + image = models.ImageField(upload_to="venues/", blank=True, null=True) + + def __str__(self): + return f"{self.name} ({self.city})" + + +class Booking(models.Model): + venue = models.ForeignKey(Venue, related_name="bookings", on_delete=models.CASCADE) + customer_name = models.CharField(max_length=200) + customer_email = models.EmailField() + start_time = models.DateTimeField() + end_time = models.DateTimeField() + created_at = models.DateTimeField(auto_now_add=True) + + STATUS_CHOICES = [ + ("CONFIRMED", "Confirmed"), + ("CANCELLED", "Cancelled"), + ] + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="CONFIRMED") + + def __str__(self): + return f"Booking {self.id} at {self.venue.name} for {self.customer_name}" + diff --git a/bookings/serializers.py b/bookings/serializers.py new file mode 100644 index 0000000..715fb0c --- /dev/null +++ b/bookings/serializers.py @@ -0,0 +1,15 @@ +# bookings/serializers.py +from rest_framework import serializers +from .models import Venue, Booking + +class VenueSerializer(serializers.ModelSerializer): + class Meta: + model = Venue + fields = "__all__" +class BookingSerializer(serializers.ModelSerializer): + venue = serializers.PrimaryKeyRelatedField(queryset=Venue.objects.all()) + + class Meta: + model = Booking + fields = ["id", "venue", "customer_name", "customer_email", "start_time", "end_time", "status", "created_at"] + read_only_fields = ["created_at", "status"] diff --git a/bookings/tests.py b/bookings/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/bookings/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/bookings/urls.py b/bookings/urls.py new file mode 100644 index 0000000..a16e193 --- /dev/null +++ b/bookings/urls.py @@ -0,0 +1,9 @@ +# bookings/urls.py +from rest_framework import routers +from .views import VenueViewSet, BookingViewSet + +router = routers.DefaultRouter() +router.register(r"venues", VenueViewSet, basename="venues") +router.register(r"bookings", BookingViewSet, basename="bookings") + +urlpatterns = router.urls diff --git a/bookings/views.py b/bookings/views.py new file mode 100644 index 0000000..b48f9f4 --- /dev/null +++ b/bookings/views.py @@ -0,0 +1,111 @@ +from django.shortcuts import render + +# Create your views here. +# bookings/views.py +from rest_framework import viewsets, filters, status +from rest_framework.decorators import action +from rest_framework.response import Response +from .models import Venue, Booking +from .serializers import VenueSerializer, BookingSerializer +from django.shortcuts import get_object_or_404 +from django.utils import timezone + +from rest_framework.decorators import action +from rest_framework import viewsets, status +from rest_framework.response import Response +from .models import Venue, Booking +from .serializers import VenueSerializer, BookingSerializer + +class VenueViewSet(viewsets.ModelViewSet): + queryset = Venue.objects.all() + serializer_class = VenueSerializer + + # Custom endpoint: /venues/findByCity?city=Paris + @action(detail=False, methods=["get"]) + def findByCity(self, request): + city = request.query_params.get("city") + venues = Venue.objects.filter(city__iexact=city) if city else Venue.objects.all() + serializer = self.get_serializer(venues, many=True) + return Response(serializer.data) + + # Custom endpoint: /venues/{id}/availability + @action(detail=True, methods=["get"]) + def availability(self, request, pk=None): + venue = self.get_object() + bookings = Booking.objects.filter(venue=venue, status="active") + return Response({ + "venue": venue.name, + "city": venue.city, + "capacity": venue.capacity, + "active_bookings": bookings.count(), + "available": venue.capacity - bookings.count() + }) + + # Custom endpoint: /venues/{id}/uploadImage + @action(detail=True, methods=["post"]) + def uploadImage(self, request, pk=None): + venue = self.get_object() + file = request.FILES.get("file") + if not file: + return Response({"error": "No file uploaded"}, status=400) + venue.image = file + venue.save() + return Response({"status": "Image uploaded", "venue": venue.name}) + + # Custom endpoint: /venues/{id}/bookings + @action(detail=True, methods=["get"]) + def bookings(self, request, pk=None): + venue = self.get_object() + bookings = Booking.objects.filter(venue=venue) + serializer = BookingSerializer(bookings, many=True) + return Response(serializer.data) + + + +class BookingViewSet(viewsets.ModelViewSet): + queryset = Booking.objects.all().order_by("-created_at") + serializer_class = BookingSerializer + filter_backends = [filters.SearchFilter] + search_fields = ["customer_name", "customer_email"] + + def get_queryset(self): + qs = super().get_queryset() + venue_id = self.request.query_params.get("venue") + if venue_id: + qs = qs.filter(venue_id=venue_id) + return qs + + # Example custom action: cancel booking (POST /api/bookings/{id}/cancel/) + @action(detail=True, methods=["post"]) + def cancel(self, request, pk=None): + booking = self.get_object() + booking.status = "cancelled" + booking.save() + return Response( + {"status": "Booking cancelled", "id": booking.id}, + status=status.HTTP_200_OK + ) + # Custom endpoint: /bookings/findByStatus?status=active + @action(detail=False, methods=["get"]) + def findByStatus(self, request): + status_param = request.query_params.get("status", None) + if status_param: + bookings = Booking.objects.filter(status=status_param) + else: + bookings = Booking.objects.all() + serializer = self.get_serializer(bookings, many=True) + return Response(serializer.data) + + # Custom endpoint: /bookings/{id}/uploadDocument + @action(detail=True, methods=["post"]) + def uploadDocument(self, request, pk=None): + booking = self.get_object() + file = request.FILES.get("file") + if not file: + return Response({"error": "No file uploaded"}, status=400) + + # In a real app, save file to booking record or storage + return Response( + {"status": "File uploaded", "filename": file.name, "booking_id": booking.id}, + status=200 + ) \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000..92543c7 Binary files /dev/null and b/db.sqlite3 differ diff --git a/logs/api.log b/logs/api.log new file mode 100644 index 0000000..41f9397 --- /dev/null +++ b/logs/api.log @@ -0,0 +1,33 @@ +[2025-09-30 21:08:56,616] INFO django.utils.autoreload: Watching for file changes with StatReloader +[2025-09-30 21:09:36,669] INFO bookings.middleware: Request start: GET /api/ body=None +[2025-09-30 21:09:36,693] INFO bookings.middleware: Request finished: GET /api/ status=200 time=0.024s +[2025-09-30 21:10:00,790] INFO bookings.middleware: Request start: GET /swagger/ body=None +[2025-09-30 21:10:00,883] INFO bookings.middleware: Request finished: GET /swagger/ status=200 time=0.093s +[2025-09-30 21:10:02,120] INFO bookings.middleware: Request start: GET /swagger/?format=openapi body=None +[2025-09-30 21:10:02,144] INFO bookings.middleware: Request finished: GET /swagger/?format=openapi status=200 time=0.024s +[2025-09-30 21:11:08,908] INFO bookings.middleware: Request start: GET /swagger.json body=None +[2025-09-30 21:11:08,936] INFO bookings.middleware: Request finished: GET /swagger.json status=200 time=0.027s +[2025-09-30 22:20:11,279] INFO django.utils.autoreload: C:\Users\Cesar\Desktop\Apighaymah\Apighaymah\booking_system\bookings\views.py changed, reloading. +[2025-09-30 22:20:12,596] INFO django.utils.autoreload: Watching for file changes with StatReloader +[2025-09-30 22:20:16,338] INFO bookings.middleware: Request start: GET /swagger/ body=None +[2025-09-30 22:20:16,365] INFO bookings.middleware: Request finished: GET /swagger/ status=200 time=0.028s +[2025-09-30 22:20:17,138] INFO bookings.middleware: Request start: GET /swagger/?format=openapi body=None +[2025-09-30 22:20:17,230] INFO bookings.middleware: Request finished: GET /swagger/?format=openapi status=200 time=0.092s +[2025-09-30 22:21:44,602] INFO django.utils.autoreload: C:\Users\Cesar\Desktop\Apighaymah\Apighaymah\booking_system\bookings\views.py changed, reloading. +[2025-09-30 22:21:45,825] INFO django.utils.autoreload: Watching for file changes with StatReloader +[2025-09-30 22:21:46,318] INFO bookings.middleware: Request start: GET /swagger/ body=None +[2025-09-30 22:21:46,345] INFO bookings.middleware: Request finished: GET /swagger/ status=200 time=0.027s +[2025-09-30 22:21:47,206] INFO bookings.middleware: Request start: GET /swagger/?format=openapi body=None +[2025-09-30 22:21:47,299] INFO bookings.middleware: Request finished: GET /swagger/?format=openapi status=200 time=0.093s +[2025-09-30 22:22:30,864] INFO django.utils.autoreload: C:\Users\Cesar\Desktop\Apighaymah\Apighaymah\booking_system\bookings\views.py changed, reloading. +[2025-09-30 22:22:31,796] INFO django.utils.autoreload: Watching for file changes with StatReloader +[2025-09-30 22:22:34,154] INFO bookings.middleware: Request start: GET /swagger/ body=None +[2025-09-30 22:22:34,183] INFO bookings.middleware: Request finished: GET /swagger/ status=200 time=0.029s +[2025-09-30 22:22:34,674] INFO bookings.middleware: Request start: GET /swagger/?format=openapi body=None +[2025-09-30 22:22:34,710] INFO bookings.middleware: Request finished: GET /swagger/?format=openapi status=200 time=0.036s +[2025-09-30 22:48:48,940] INFO django.utils.autoreload: C:\Users\Cesar\Desktop\Apighaymah\Apighaymah\booking_system\bookings\models.py changed, reloading. +[2025-09-30 22:48:50,022] INFO django.utils.autoreload: Watching for file changes with StatReloader +[2025-09-30 22:49:30,550] INFO django.utils.autoreload: C:\Users\Cesar\Desktop\Apighaymah\Apighaymah\booking_system\bookings\serializers.py changed, reloading. +[2025-09-30 22:49:31,697] INFO django.utils.autoreload: Watching for file changes with StatReloader +[2025-09-30 22:50:34,260] INFO django.utils.autoreload: C:\Users\Cesar\Desktop\Apighaymah\Apighaymah\booking_system\bookings\views.py changed, reloading. +[2025-09-30 22:50:35,299] INFO django.utils.autoreload: Watching for file changes with StatReloader diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..a330967 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'booking_system.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main()