Skip to content

ppinklesh/room-bookings

Repository files navigation

Virtual Workspace Room Booking System

A production-ready RESTful API for managing workspace room bookings with JWT authentication, role-based access control, and comprehensive booking management.


📋 Table of Contents


📘 Overview

This system manages a virtual workspace with 15 rooms:

  • 8 Private Rooms (P01-P08) - For individual users
  • 4 Conference Rooms (C01-C04) - For teams of 3+ members
  • 3 Shared Desks (S01-S03) - For up to 4 users each

Key Features

✅ User registration and JWT authentication
✅ Role-based access control (Admin/User)
✅ Smart room allocation based on requirements
✅ Booking time slots: 9 AM - 6 PM (hourly)
✅ Double-booking prevention with database locking
✅ Booking history tracking
✅ Children handling (counted but don't occupy seats)
✅ Swagger/OpenAPI documentation
✅ Docker containerization
✅ Comprehensive test suite


🛠️ Technology Stack

Backend

  • Python 3.12+
  • Django 4.2.7 - Web framework
  • Django REST Framework 3.14.0 - API framework
  • djangorestframework-simplejwt 5.3.0 - JWT authentication
  • drf-spectacular - OpenAPI/Swagger documentation

Database

  • PostgreSQL 15 (Production)
  • SQLite (Development)

Deployment

  • Docker & Docker Compose
  • Gunicorn - WSGI server

🚀 Setup Instructions

Quick Start with Docker (Recommended)

Prerequisites

  • Docker
  • Docker Compose

Steps

  1. Navigate to project directory
cd /path/to/booking_system
  1. Start the application
docker-compose up --build

This automatically:

  • Starts PostgreSQL database
  • Runs migrations
  • Initializes 15 rooms
  • Starts Django server on port 8000
  1. Create admin user (in another terminal)
docker-compose exec web python manage.py createsuperuser
  1. Access the application

Local Development Setup

Prerequisites

  • Python 3.11+
  • pip
  • virtualenv

Steps

  1. Create virtual environment
python3 -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
  1. Install dependencies
pip install -r requirements.txt
  1. Configure environment

Create .env file in project root:

SECRET_KEY=your-secret-key-change-in-production
DEBUG=True
DB_ENGINE=sqlite
DB_NAME=db.sqlite3
  1. Create migrations directory (if needed)
mkdir -p bookings/migrations
touch bookings/migrations/__init__.py
  1. Run migrations
python manage.py makemigrations
python manage.py migrate
  1. Initialize 15 rooms
python manage.py init_rooms

Output:

Successfully created 15 rooms: 8 Private, 4 Conference, 3 Shared Desks
  1. Create admin user
python manage.py createsuperuser
  1. Start development server
python manage.py runserver
  1. Access the application

📚 API Documentation & Usage

Base URL

http://localhost:8000/api/v1/

Interactive Documentation


Authentication Flow

1. Register New User (No Auth Required)

Endpoint:

POST /api/v1/register/
Content-Type: application/json

Request:

{
  "username": "john_doe",
  "email": "john@example.com",
  "password": "SecurePass123",
  "password_confirm": "SecurePass123",
  "name": "John Doe",
  "age": 30,
  "gender": "M"
}

Response (201 Created):

{
  "message": "Registration successful!",
  "user": {
    "id": 1,
    "username": "john_doe",
    "email": "john@example.com",
    "profile": {
      "name": "John Doe",
      "age": 30,
      "gender": "M"
    }
  },
  "access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "refresh": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}

cURL Example:

curl -X POST http://localhost:8000/api/v1/register/ \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john_doe",
    "email": "john@example.com",
    "password": "SecurePass123",
    "password_confirm": "SecurePass123",
    "name": "John Doe",
    "age": 30,
    "gender": "M"
  }'

2. Login (Get JWT Token)

Endpoint:

POST /api/token/
Content-Type: application/json

Request:

{
  "username": "john_doe",
  "password": "SecurePass123"
}

Response:

{
  "access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "refresh": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}

cURL Example:

curl -X POST http://localhost:8000/api/token/ \
  -H "Content-Type: application/json" \
  -d '{"username": "john_doe", "password": "SecurePass123"}'

Note: Save the access token - you'll need it for all authenticated requests!

3. Refresh Token

Endpoint:

POST /api/token/refresh/

Request:

{
  "refresh": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}

Booking Operations

1. Create Booking

Endpoint:

POST /api/v1/bookings/
Authorization: Bearer <access_token>
Content-Type: application/json

Private Room Booking:

{
  "room_type": "PRIVATE",
  "date": "2025-10-15",
  "time_slot": "10:00:00",
  "user_id": 1
}

Conference Room Booking:

{
  "room_type": "CONFERENCE",
  "date": "2025-10-15",
  "time_slot": "14:00:00",
  "team_id": 1
}

Shared Desk Booking:

{
  "room_type": "SHARED_DESK",
  "date": "2025-10-15",
  "time_slot": "11:00:00",
  "user_ids": [1, 2, 3]
}

Response (201 Created):

{
  "id": 1,
  "booking_id": "BK20251015100000P01",
  "room": {
    "id": 1,
    "room_number": "P01",
    "room_type": "PRIVATE",
    "capacity": 1
  },
  "user": {
    "id": 1,
    "name": "John Doe",
    "age": 30,
    "gender": "M",
    "is_child": false
  },
  "team": null,
  "date": "2025-10-15",
  "time_slot": "10:00:00",
  "status": "ACTIVE",
  "created_at": "2025-10-15T10:00:00Z"
}

cURL Example:

curl -X POST http://localhost:8000/api/v1/bookings/ \
  -H "Authorization: Bearer eyJhbGc..." \
  -H "Content-Type: application/json" \
  -d '{
    "room_type": "PRIVATE",
    "date": "2025-10-15",
    "time_slot": "10:00:00",
    "user_id": 1
  }'

2. Get My Bookings (User's History)

Endpoint:

GET /api/v1/bookings/my-bookings/
Authorization: Bearer <access_token>

Response:

{
  "user_info": {
    "name": "John Doe",
    "username": "john_doe"
  },
  "total_bookings": 3,
  "active_bookings": 2,
  "cancelled_bookings": 1,
  "active": [
    {
      "id": 1,
      "booking_id": "BK20251015100000P01",
      "room": {
        "room_number": "P01",
        "room_type": "PRIVATE"
      },
      "date": "2025-10-15",
      "time_slot": "10:00:00",
      "status": "ACTIVE"
    },
    {
      "booking_id": "BK20251016140000C01",
      "room": {
        "room_number": "C01",
        "room_type": "CONFERENCE"
      },
      "date": "2025-10-16",
      "time_slot": "14:00:00",
      "status": "ACTIVE"
    }
  ],
  "cancelled": [
    {
      "booking_id": "BK20251012110000P02",
      "date": "2025-10-12",
      "status": "CANCELLED"
    }
  ]
}

cURL Example:

curl -H "Authorization: Bearer eyJhbGc..." \
  http://localhost:8000/api/v1/bookings/my-bookings/

3. Get All Bookings (Admin sees all, User sees own)

Endpoint:

GET /api/v1/bookings/
Authorization: Bearer <access_token>

Response (Paginated):

{
  "count": 10,
  "next": "http://localhost:8000/api/v1/bookings/?page=2",
  "previous": null,
  "results": [
    {
      "id": 1,
      "booking_id": "BK20251015100000P01",
      "room": {...},
      "user": {...},
      "date": "2025-10-15",
      "time_slot": "10:00:00",
      "status": "ACTIVE"
    }
  ]
}

4. Get Booking Details

Endpoint:

GET /api/v1/bookings/{booking_id}/
Authorization: Bearer <access_token>

Example:

curl -H "Authorization: Bearer eyJhbGc..." \
  http://localhost:8000/api/v1/bookings/BK20251015100000P01/

5. Cancel Booking

Endpoint:

POST /api/v1/cancel/{booking_id}/
Authorization: Bearer <access_token>

Response (200 OK):

{
  "message": "Booking cancelled successfully"
}

cURL Example:

curl -X POST http://localhost:8000/api/v1/cancel/BK20251015100000P01/ \
  -H "Authorization: Bearer eyJhbGc..."

Note: Only the booking owner or admin can cancel a booking.


Room Operations

1. List All Rooms

Endpoint:

GET /api/v1/rooms/
Authorization: Bearer <access_token>

Response:

[
  {
    "id": 1,
    "room_number": "P01",
    "room_type": "PRIVATE",
    "capacity": 1,
    "created_at": "2025-10-12T09:00:00Z"
  },
  {
    "id": 9,
    "room_number": "C01",
    "room_type": "CONFERENCE",
    "capacity": 6,
    "created_at": "2025-10-12T09:00:00Z"
  },
  {
    "id": 13,
    "room_number": "S01",
    "room_type": "SHARED_DESK",
    "capacity": 4,
    "created_at": "2025-10-12T09:00:00Z"
  }
]

2. Check Available Rooms

Endpoint:

GET /api/v1/rooms/available/?date=2025-10-15&time_slot=10:00:00&room_type=PRIVATE
Authorization: Bearer <access_token>

Query Parameters:

  • date (required): Date in YYYY-MM-DD format
  • time_slot (required): Time in HH:MM:SS format
  • room_type (optional): PRIVATE, CONFERENCE, or SHARED_DESK

Response:

{
  "date": "2025-10-15",
  "time_slot": "10:00:00",
  "available_rooms": [
    {
      "id": 2,
      "room_number": "P02",
      "room_type": "PRIVATE",
      "capacity": 1
    },
    {
      "id": 3,
      "room_number": "P03",
      "room_type": "PRIVATE",
      "capacity": 1
    }
  ],
  "count": 7
}

cURL Example:

curl -H "Authorization: Bearer eyJhbGc..." \
  "http://localhost:8000/api/v1/rooms/available/?date=2025-10-15&time_slot=10:00:00&room_type=PRIVATE"

3. Room Availability Summary (All Time Slots)

Endpoint:

GET /api/v1/rooms/availability/?date=2025-10-15
Authorization: Bearer <access_token>

Shows availability for all hourly slots from 9 AM to 6 PM.


Team Management

1. Create Team

Endpoint:

POST /api/v1/teams/
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "name": "Development Team",
  "member_ids": [1, 2, 3, 4]
}

Response:

{
  "id": 1,
  "name": "Development Team",
  "members": [
    {
      "id": 1,
      "name": "John Doe",
      "age": 30,
      "gender": "M",
      "is_child": false
    },
    ...
  ],
  "size": 4,
  "adult_count": 4,
  "created_at": "2025-10-12T10:00:00Z"
}

cURL Example:

curl -X POST http://localhost:8000/api/v1/teams/ \
  -H "Authorization: Bearer eyJhbGc..." \
  -H "Content-Type: application/json" \
  -d '{"name": "Dev Team", "member_ids": [1, 2, 3]}'

2. List Teams

Endpoint:

GET /api/v1/teams/
Authorization: Bearer <access_token>

User Management

1. List Users

Endpoint:

GET /api/v1/users/
Authorization: Bearer <access_token>

2. Create User (Admin Only)

Endpoint:

POST /api/v1/users/
Authorization: Bearer <admin_token>

Request:

{
  "name": "Jane Smith",
  "age": 28,
  "gender": "F"
}

🎯 Complete Usage Example

Scenario: New User Books a Room

# Step 1: Register
REGISTER_RESPONSE=$(curl -s -X POST http://localhost:8000/api/v1/register/ \
  -H "Content-Type: application/json" \
  -d '{
    "username": "alice",
    "email": "alice@example.com",
    "password": "Pass123456",
    "password_confirm": "Pass123456",
    "name": "Alice Johnson",
    "age": 28,
    "gender": "F"
  }')

# Extract token
TOKEN=$(echo $REGISTER_RESPONSE | jq -r '.access')
USER_ID=$(echo $REGISTER_RESPONSE | jq -r '.user.profile.id')

echo "Token: $TOKEN"
echo "User ID: $USER_ID"

# Step 2: Check available rooms
curl -H "Authorization: Bearer $TOKEN" \
  "http://localhost:8000/api/v1/rooms/available/?date=2025-10-15&time_slot=10:00:00"

# Step 3: Book a private room
BOOKING_RESPONSE=$(curl -s -X POST http://localhost:8000/api/v1/bookings/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"room_type\": \"PRIVATE\",
    \"date\": \"2025-10-15\",
    \"time_slot\": \"10:00:00\",
    \"user_id\": $USER_ID
  }")

BOOKING_ID=$(echo $BOOKING_RESPONSE | jq -r '.booking_id')
echo "Booking ID: $BOOKING_ID"

# Step 4: Check my bookings
curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8000/api/v1/bookings/my-bookings/

# Step 5: Cancel booking (if needed)
curl -X POST http://localhost:8000/api/v1/cancel/$BOOKING_ID/ \
  -H "Authorization: Bearer $TOKEN"

Scenario: Team Books Conference Room

# Assuming 3 users already created with IDs 1, 2, 3

# Step 1: Login as admin
ADMIN_TOKEN=$(curl -s -X POST http://localhost:8000/api/token/ \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": "admin123"}' | jq -r '.access')

# Step 2: Create team
TEAM_RESPONSE=$(curl -s -X POST http://localhost:8000/api/v1/teams/ \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Project Alpha Team",
    "member_ids": [1, 2, 3]
  }')

TEAM_ID=$(echo $TEAM_RESPONSE | jq -r '.id')

# Step 3: Book conference room
curl -X POST http://localhost:8000/api/v1/bookings/ \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"room_type\": \"CONFERENCE\",
    \"date\": \"2025-10-15\",
    \"time_slot\": \"14:00:00\",
    \"team_id\": $TEAM_ID
  }"

Scenario: Shared Desk Booking

# Book shared desk for 3 users
curl -X POST http://localhost:8000/api/v1/bookings/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "room_type": "SHARED_DESK",
    "date": "2025-10-15",
    "time_slot": "11:00:00",
    "user_ids": [1, 2, 3]
  }'

Python Usage Examples

import requests
import json

BASE_URL = "http://localhost:8000/api/v1"

# 1. Register
register_data = {
    "username": "john",
    "email": "john@test.com",
    "password": "Pass123",
    "password_confirm": "Pass123",
    "name": "John",
    "age": 30,
    "gender": "M"
}
response = requests.post(f"{BASE_URL}/register/", json=register_data)
result = response.json()
token = result['access']
user_id = result['user']['id']

# 2. Check available rooms
headers = {'Authorization': f'Bearer {token}'}
params = {
    'date': '2025-10-15',
    'time_slot': '10:00:00',
    'room_type': 'PRIVATE'
}
response = requests.get(f"{BASE_URL}/rooms/available/", headers=headers, params=params)
available = response.json()
print(f"Available rooms: {available['count']}")

# 3. Book a room
booking_data = {
    "room_type": "PRIVATE",
    "date": "2025-10-15",
    "time_slot": "10:00:00",
    "user_id": user_id
}
response = requests.post(f"{BASE_URL}/bookings/", headers=headers, json=booking_data)
booking = response.json()
print(f"Booking created: {booking['booking_id']}")

# 4. Check my bookings
response = requests.get(f"{BASE_URL}/bookings/my-bookings/", headers=headers)
my_bookings = response.json()
print(f"Total bookings: {my_bookings['total_bookings']}")
print(f"Active bookings: {my_bookings['active_bookings']}")

# 5. Cancel booking
booking_id = booking['booking_id']
response = requests.post(f"{BASE_URL}/cancel/{booking_id}/", headers=headers)
print(response.json()['message'])

💡 Assumptions Made

1. Time Management

Assumption: Bookings are hourly slots from 9 AM to 6 PM
Rationale:

  • Standard business hours
  • Easy to understand and implement
  • Aligns with typical office workspace usage
  • Each slot is 1 hour (e.g., 10:00-11:00)

Implementation:

# Valid times: 09:00, 10:00, 11:00, ..., 17:00
# Invalid times: 08:00, 18:00, 20:00, etc.
CHECK(time_slot >= '09:00:00' AND time_slot < '18:00:00')

2. User System

Assumption: Two-tier user system (Auth + Profile)
Rationale:

  • Auth User: Handles login, security, permissions
  • Booking User: Handles booking-specific data (name, age, gender)
  • Linked via OneToOne for flexibility
  • Allows walk-in bookings without auth accounts

Implementation:

class User(models.Model):
    auth_user = models.OneToOneField(AuthUser, null=True, blank=True)
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()
    gender = models.CharField(max_length=1)

3. Child Handling

Assumption: Children (age < 10) count in team size but don't need seats
Rationale:

  • Reflects real-world office scenarios
  • Children accompany adults but don't occupy workspace
  • Still counted for fire safety/headcount purposes

Implementation:

@property
def is_child(self):
    return self.age < 10

@property
def adult_count(self):
    return self.members.filter(age__gte=10).count()

4. Room Capacity

Assumption: Fixed capacities per room type
Rationale:

  • Private: 1 person (individual workspace)
  • Conference: 6 seats (teams of 3-6)
  • Shared Desk: 4 seats (hot-desking)

Implementation:

# Room initialization
PRIVATE: capacity=1
CONFERENCE: capacity=6
SHARED_DESK: capacity=4

5. Booking Constraints

Assumption: One active booking per user per time slot
Rationale:

  • Prevents users from booking multiple rooms simultaneously
  • Realistic constraint (can't be in two places at once)
  • Simplifies conflict detection

Implementation:

# Check before booking
existing = Booking.objects.filter(
    user=user,
    date=date,
    time_slot=time_slot,
    status='ACTIVE'
).exists()

if existing:
    raise ValueError("User already has a booking")

6. Team Constraints

Assumption: Conference rooms require minimum 3 members
Rationale:

  • Conference rooms designed for team collaboration
  • Below 3 people should use private rooms or shared desks
  • Efficient resource utilization

Implementation:

if room_type == 'CONFERENCE' and team.size < 3:
    raise ValueError("Conference rooms require teams of 3 or more")

7. Cancellation Policy

Assumption: Soft delete (status change, not physical deletion)
Rationale:

  • Audit trail for compliance
  • Booking history preserved
  • Can analyze cancellation patterns
  • Slot immediately freed for others

Implementation:

booking.status = 'CANCELLED'  # Not booking.delete()

8. Concurrency Handling

Assumption: High concurrent booking attempts expected
Rationale:

  • Multiple users booking simultaneously
  • Need to prevent race conditions
  • Database-level protection required

Implementation:

with transaction.atomic():
    Room.objects.filter(...).select_for_update()  # Row-level locking
    # Booking creation

9. Authentication

Assumption: JWT tokens for stateless authentication
Rationale:

  • Scalable (no server-side sessions)
  • Mobile/SPA friendly
  • 1-hour access token (security)
  • 7-day refresh token (convenience)

Implementation:

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(hours=1),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
}

10. Role-Based Access

Assumption: Two user roles (Admin and Regular User)
Rationale:

  • Admins: Full system access, manage all bookings
  • Users: Create/view own bookings only
  • Admins can help users with issues

Implementation:

# Regular users see only their bookings
if not user.is_staff:
    queryset = queryset.filter(user=user)

# Only owners or admins can cancel
if not (user.is_staff or booking.user == user):
    return 403 Forbidden

11. Sequential Desk Filling

Assumption: Shared desks fill sequentially (seat 1, 2, 3, 4)
Rationale:

  • Organized seating arrangement
  • Easy to track occupancy
  • Clear seat assignment

Implementation:

for i, user in enumerate(users, 1):
    SharedDeskBooking.objects.create(
        booking=booking,
        user=user,
        seat_number=i  # 1, 2, 3, 4
    )

12. Partial Shared Desk Fills

Assumption: Shared desks can be partially filled (1-4 users)
Rationale:

  • Efficient resource utilization
  • Flexibility for users
  • If only 2 users need a desk, don't waste 4-seat capacity

Implementation:

if 1 <= len(user_ids) <= 4:
    # Allow booking
else:
    raise ValueError("1-4 users required")

13. Date Restrictions

Assumption: Cannot book past dates
Rationale:

  • Logical constraint
  • Prevents data entry errors
  • Database constraint for integrity

Implementation:

CHECK(date >= NOW())  # Database level
if date < datetime.now().date():  # Application level
    raise ValueError("Cannot book past dates")

14. Booking ID Format

Assumption: Auto-generated unique ID format
Rationale:

  • Easy to read: BK20251015100000P01
  • Contains timestamp and room number
  • Guaranteed unique
  • User-friendly

Implementation:

timestamp = timezone.now().strftime('%Y%m%d%H%M%S')
booking_id = f"BK{timestamp}{room.room_number}"
# Example: BK20251015100000P01

15. API Design

Assumption: RESTful API with standard HTTP methods
Rationale:

  • Industry standard
  • Predictable endpoints
  • Easy to integrate
  • Well-documented patterns

Implementation:

POST /api/v1/bookings/     → Create booking
GET  /api/v1/bookings/     → List bookings
POST /api/v1/cancel/{id}/  → Cancel booking
GET  /api/v1/rooms/available/ → Check availability

🗄️ Database Schema

Models

1. User (Booking Profile)

class User(models.Model):
    id = AutoField (primary key, auto-generated)
    auth_user = OneToOneField(AuthUser, null=True)
    name = CharField(max_length=100)
    age = PositiveIntegerField
    gender = CharField(choices=['M', 'F', 'O'])
    created_at = DateTimeField(auto_now_add=True)

Properties:

  • is_child → Returns True if age < 10

2. Team

class Team(models.Model):
    id = AutoField (primary key, auto-generated)
    name = CharField(max_length=100)
    members = ManyToManyField(User)
    created_at = DateTimeField(auto_now_add=True)

Properties:

  • size → Total member count
  • adult_count → Members with age >= 10

3. Room

class Room(models.Model):
    id = AutoField (primary key, auto-generated)
    room_number = CharField(max_length=10, unique=True)
    room_type = CharField(choices=['PRIVATE', 'CONFERENCE', 'SHARED_DESK'])
    capacity = PositiveIntegerField
    created_at = DateTimeField(auto_now_add=True)

Room Distribution:

  • P01-P08: Private Rooms (capacity=1)
  • C01-C04: Conference Rooms (capacity=6)
  • S01-S03: Shared Desks (capacity=4)

4. Booking

class Booking(models.Model):
    id = AutoField (primary key, auto-generated)
    booking_id = CharField(max_length=50, unique=True)
    room = ForeignKey(Room)
    user = ForeignKey(User, null=True)
    team = ForeignKey(Team, null=True)
    date = DateField
    time_slot = TimeField
    status = CharField(choices=['ACTIVE', 'CANCELLED'], default='ACTIVE')
    created_at = DateTimeField(auto_now_add=True)
    updated_at = DateTimeField(auto_now=True)

Constraints:

UNIQUE TOGETHER (room, date, time_slot, status)
CHECK (time_slot >= '09:00:00' AND time_slot < '18:00:00')
INDEX (date, time_slot)
INDEX (room, date, time_slot)
INDEX (user, date, time_slot)
INDEX (booking_id)

5. SharedDeskBooking

class SharedDeskBooking(models.Model):
    id = AutoField (primary key, auto-generated)
    booking = ForeignKey(Booking)
    user = ForeignKey(User)
    seat_number = PositiveIntegerField (1-4)

Constraints:

UNIQUE TOGETHER (booking, seat_number)

Design Decisions

Why separate User from Auth User?

  • Django's User is for authentication
  • Our User has booking-specific fields (age, gender)
  • Flexibility: Can have booking users without auth (walk-ins)
  • Clear separation of concerns

Why soft delete (status field)?

  • Preserve booking history for audits
  • Analyze cancellation patterns
  • Maintain data integrity
  • Slot immediately freed (status='CANCELLED')

Why SharedDeskBooking table?

  • Track individual seats on shared desks
  • Support partial fills (e.g., 2 out of 4 seats)
  • Clear seat assignment (1, 2, 3, 4)
  • Multiple users per desk booking

Why indexes on date, time_slot, room?

  • Fast availability queries
  • Common filter combinations
  • Improved query performance
  • Essential for concurrent bookings

🚦 Business Rules

Room Allocation Rules

Private Rooms

✅ Individual users only
✅ One person per room
✅ 8 rooms available (P01-P08)
❌ Cannot be booked by teams
❌ Cannot be shared

Conference Rooms

✅ Teams of 3+ members only
✅ Children counted in team size
✅ 4 rooms available (C01-C04)
❌ Cannot be booked by individuals
❌ Teams < 3 members rejected

Shared Desks

✅ 1-4 users per desk
✅ Partial fills allowed
✅ Sequential seat assignment
✅ 3 desks available (S01-S03)
❌ Cannot exceed 4 users

Validation Rules

# Time Slot Validation
09:00:00 <= time_slot < 18:00:00

# Date Validation
date >= today()

# User/Team Validation
(user_id XOR team_id) OR (user_ids for shared desks)

# Room Type Validation
PRIVATErequires user_id
CONFERENCErequires team_id AND team.size >= 3
SHARED_DESKrequires user_ids (1-4 items)

# Double Booking Prevention
No two active bookings for same (room, date, time_slot)
No user can have multiple bookings at same (date, time_slot)

Permissions

Action Regular User Admin
Register
Login
View rooms
Check availability
Create booking ✅ Own ✅ Any
View bookings ✅ Own only ✅ All
Cancel booking ✅ Own only ✅ Any
Create users
Create teams
Admin panel

🧪 Testing

Running Tests

# All tests
python manage.py test

# With Docker
docker-compose exec web python manage.py test

# Specific test class
python manage.py test bookings.tests.BookingServiceTest

# Verbose output
python manage.py test --verbosity=2

Test Coverage (1091 lines)

✅ Test Classes (13 total)

1. UserModelTest - User model functionality

  • User creation
  • is_child property (age < 10)

2. TeamModelTest - Team model functionality

  • Team creation
  • Team size calculation
  • Adult count (excluding children)

3. RoomModelTest - Room model functionality

  • Room creation for all types
  • Room capacity validation

4. BookingServiceTest - Business logic

  • Private room booking success/failure
  • Conference room booking with team validation
  • Team size validation (minimum 3 members)
  • Shared desk booking (1-4 users)
  • Shared desk capacity limits
  • Double-booking prevention
  • Invalid time slot rejection
  • Past date rejection
  • Available rooms calculation

5. UserAPITest - User API endpoints

  • Create user via API
  • List users

6. TeamAPITest - Team API endpoints

  • Create team with members
  • Team size validation

7. BookingAPITest - Booking API endpoints

  • Create private/conference/shared bookings
  • List bookings with pagination
  • Cancel booking
  • Get booking details
  • Check available rooms
  • Invalid date rejection
  • Invalid time slot rejection

8. RoomAvailabilityTest - Availability checking

  • 15 rooms initialization
  • Availability decreases after booking
  • Availability summary endpoint

9. UserRegistrationTest - User registration

  • Successful registration
  • Password mismatch validation
  • Duplicate username validation
  • Auto-login after registration

10. JWTAuthenticationTest - JWT authentication

  • Obtain JWT token
  • Access protected endpoint with token
  • Reject access without token

11. MyBookingsTest - Booking history

  • Empty booking history
  • Booking history with active bookings
  • Booking history with cancelled bookings
  • Active vs cancelled separation

12. PermissionTest - Authorization

  • Regular user cannot cancel others' bookings
  • Admin can cancel any booking
  • User can cancel own booking
  • Role-based access control

13. ConcurrencyTest - Race conditions

  • Concurrent booking prevention
  • select_for_update() locking
  • Only one booking succeeds

14. BookingIDTest - ID generation

  • Unique booking ID generation
  • Booking ID format (BK + timestamp + room)

15. SharedDeskTest - Shared desk specifics

  • Partial fills (1-4 users)
  • Sequential seat assignment
  • Maximum capacity enforcement

16. CancellationTest - Cancellation logic

  • Slot freed after cancellation
  • Cancelled bookings preserved (soft delete)

17. ValidationTest - All validations

  • Time before 9 AM rejected
  • Time after 6 PM rejected
  • Past dates rejected

18. RoomInitializationTest - Room setup

  • Exactly 15 rooms created
  • Correct room numbers (P01-P08, C01-C04, S01-S03)

Test Coverage Summary

Category Tests Status
Models 7 tests
Business Logic 12 tests
API Endpoints 15 tests
Authentication 5 tests
Permissions 3 tests
Concurrency 2 tests
Validations 8 tests
Room Management 4 tests
Total 56 tests

Expected Test Output

$ python manage.py test

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
........................................................
----------------------------------------------------------------------
Ran 56 tests in 3.5s

OK
Destroying test database for alias 'default'...

Coverage Areas

User Management

  • Registration, login, authentication
  • Two-tier user system (Auth + Profile)
  • Child vs adult users

Room Management

  • All 3 room types
  • Capacity validation
  • 15 rooms initialization

Booking Logic

  • All room types (Private, Conference, Shared)
  • Time slot validation (9 AM - 6 PM)
  • Date validation (no past dates)
  • Double-booking prevention
  • One slot per user rule

Team Logic

  • Team size validation (min 3 for conference)
  • Team member management
  • Child counting

Shared Desk Logic

  • Partial fills (1-4 users)
  • Sequential seats (1, 2, 3, 4)
  • Maximum capacity (4 users)

Cancellation

  • Slot freed immediately
  • Soft delete (status change)
  • Permission validation

Concurrency

  • Row-level locking
  • Atomic transactions
  • Race condition prevention

Permissions

  • Admin vs regular user
  • Owner-based access
  • JWT authentication

Edge Cases

  • Invalid time slots
  • Past dates
  • Duplicate bookings
  • Team size violations
  • Capacity limits

🚀 Deployment

Production with Docker

  1. Update .env:
SECRET_KEY=generate-strong-random-key-here
DEBUG=False
DB_ENGINE=postgresql
DB_NAME=booking_system
DB_USER=booking_user
DB_PASSWORD=strong-password-here
DB_HOST=db
DB_PORT=5432
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
CORS_ALLOWED_ORIGINS=https://yourdomain.com
  1. Deploy:
docker-compose up -d --build
docker-compose exec web python manage.py migrate
docker-compose exec web python manage.py init_rooms
docker-compose exec web python manage.py createsuperuser
docker-compose exec web python manage.py collectstatic --noinput

Security Checklist

  • Authentication required on all endpoints
  • JWT tokens with 1-hour expiry
  • HTTPS enforcement (when DEBUG=False)
  • Secure cookies
  • CSRF protection
  • XSS protection headers
  • SQL injection prevention (ORM)
  • Password strength validation
  • CORS whitelist
  • Database constraints
  • Row-level locking

📊 Error Handling

Common Errors

400 Bad Request

{
  "error": "User already has a booking for this time slot"
}

401 Unauthorized

{
  "detail": "Authentication credentials were not provided."
}

403 Forbidden

{
  "error": "You do not have permission to cancel this booking"
}

404 Not Found

{
  "error": "Booking not found or already cancelled"
}

🔧 Troubleshooting

Issue: "No such table: bookings_booking"
Solution: Run python manage.py makemigrations && python manage.py migrate

Issue: "Authentication credentials were not provided"
Solution: Add Authorization: Bearer <token> header

Issue: "No available rooms"
Solution: All rooms booked for that slot, try different time

Issue: "Conference rooms require teams of 3 or more"
Solution: Add more members to the team

Issue: "User already has a booking"
Solution: Cancel existing booking first or choose different time


📄 Project Structure

booking_system/
├── booking_system/           # Django project settings
│   ├── settings.py          # Configuration
│   ├── urls.py              # Main URL routing
│   ├── wsgi.py              # WSGI application
│   └── asgi.py              # ASGI application
├── bookings/                 # Main application
│   ├── models.py            # Database models
│   ├── serializers.py       # API serializers
│   ├── registration_serializers.py  # Registration logic
│   ├── views.py             # API views
│   ├── services.py          # Business logic layer
│   ├── permissions.py       # Custom permissions
│   ├── urls.py              # App URL routing
│   ├── admin.py             # Django admin config
│   ├── tests.py             # Test suite
│   └── management/
│       └── commands/
│           └── init_rooms.py  # Initialize 15 rooms
├── Dockerfile               # Docker image definition
├── docker-compose.yml       # Docker orchestration
├── requirements.txt         # Python dependencies
├── .env                     # Environment variables
├── .gitignore              # Git ignore rules
└── README.md               # This file

📞 Support & Documentation

Interactive API Documentation

Swagger UI: http://localhost:8000/api/docs/

  • Try all endpoints
  • See request/response formats
  • Test authentication
  • Explore schema

Admin Panel

Django Admin: http://localhost:8000/admin/

  • Manage users
  • View all bookings
  • Manage rooms and teams
  • System administration

Logs

# View application logs
tail -f booking_system.log

# Docker logs
docker-compose logs -f web

🎯 Quick Reference

API Endpoints Summary

Endpoint Method Auth Description
/api/v1/register/ POST No Register new user
/api/token/ POST No Login (get token)
/api/token/refresh/ POST No Refresh token
/api/v1/users/ GET/POST Yes Manage users
/api/v1/teams/ GET/POST Yes Manage teams
/api/v1/rooms/ GET Yes List rooms
/api/v1/rooms/available/ GET Yes Check availability
/api/v1/bookings/ GET/POST Yes List/Create bookings
/api/v1/bookings/my-bookings/ GET Yes User's history
/api/v1/bookings/{id}/ GET Yes Booking details
/api/v1/cancel/{id}/ POST Yes Cancel booking

Time Slots

09:00 - 10:00
10:00 - 11:00
11:00 - 12:00
12:00 - 13:00
13:00 - 14:00
14:00 - 15:00
15:00 - 16:00
16:00 - 17:00
17:00 - 18:00

Room Types

  • PRIVATE - Individual users (8 rooms)
  • CONFERENCE - Teams 3+ (4 rooms)
  • SHARED_DESK - 1-4 users (3 rooms)

📄 License

This project was created as part of a technical assessment for a Virtual Workspace Room Booking System.


Status: ✅ Production Ready
Version: 1.0.0
Last Updated: October 2025

For questions or issues, refer to the Swagger documentation at http://localhost:8000/api/docs/

About

a room booking service django apis

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published