Python OOP Project: Hotel Booking System

Python OOP Project: Hotel Booking System

Welcome to another hands-on Python tutorial! Today, we're building a Hotel Booking System using Object-Oriented Programming (OOP) principles. This project will help you practice key OOP concepts like classes, inheritance, encapsulation, and more. By the end, you'll have a functional system to manage rooms, guests, and bookings.

Let's start by defining our core classes: Room, Guest, and Booking. These will form the foundation of our system.

class Room:
    def __init__(self, number, room_type, price_per_night):
        self.number = number
        self.type = room_type
        self.price = price_per_night
        self.is_booked = False

    def book(self):
        if not self.is_booked:
            self.is_booked = True
            return True
        return False

    def release(self):
        self.is_booked = False

    def __str__(self):
        status = "Booked" if self.is_booked else "Available"
        return f"Room {self.number} ({self.type}): ${self.price}/night - {status}"

Next, we'll create the Guest class to store information about our customers.

class Guest:
    def __init__(self, name, email, phone):
        self.name = name
        self.email = email
        self.phone = phone

    def __str__(self):
        return f"Guest: {self.name}, Email: {self.email}, Phone: {self.phone}"

Now, let's create the Booking class that will link rooms and guests together.

class Booking:
    def __init__(self, guest, room, check_in, check_out):
        self.guest = guest
        self.room = room
        self.check_in = check_in
        self.check_out = check_out
        self.total_cost = self.calculate_cost()

    def calculate_cost(self):
        nights = (self.check_out - self.check_in).days
        return nights * self.room.price

    def __str__(self):
        return f"Booking: {self.guest.name} in Room {self.room.number} from {self.check_in} to {self.check_out}. Total: ${self.total_cost}"

To manage all these components, we'll create a Hotel class that acts as our main system controller.

class Hotel:
    def __init__(self, name):
        self.name = name
        self.rooms = []
        self.bookings = []

    def add_room(self, room):
        self.rooms.append(room)

    def find_available_rooms(self, room_type=None):
        available = [room for room in self.rooms if not room.is_booked]
        if room_type:
            available = [room for room in available if room.type == room_type]
        return available

    def book_room(self, guest, room_number, check_in, check_out):
        room = next((r for r in self.rooms if r.number == room_number), None)
        if not room or room.is_booked:
            return None

        if room.book():
            booking = Booking(guest, room, check_in, check_out)
            self.bookings.append(booking)
            return booking
        return None

    def check_out(self, room_number):
        room = next((r for r in self.rooms if r.number == room_number), None)
        if room and room.is_booked:
            room.release()
            return True
        return False

    def list_bookings(self):
        return self.bookings

Now let's see our system in action with a practical example.

from datetime import datetime, timedelta

# Create hotel
hotel = Hotel("Python Paradise Resort")

# Add some rooms
hotel.add_room(Room(101, "Single", 100))
hotel.add_room(Room(102, "Double", 150))
hotel.add_room(Room(103, "Suite", 300))

# Create a guest
guest1 = Guest("Alice Johnson", "alice@email.com", "555-1234")

# Book a room
check_in = datetime(2023, 12, 15)
check_out = datetime(2023, 12, 20)
booking = hotel.book_room(guest1, 102, check_in, check_out)

if booking:
    print("Booking successful!")
    print(booking)
else:
    print("Room not available.")

# Show available rooms
print("\nAvailable rooms:")
for room in hotel.find_available_rooms():
    print(room)

This basic implementation gives you a functional hotel booking system. You can extend it with features like payment processing, room services, or even a web interface.

Room Number Type Price/Night Availability
101 Single $100 Available
102 Double $150 Booked
103 Suite $300 Available

Now let's enhance our system with some additional features. We'll add room maintenance tracking and special room types using inheritance.

class MaintenanceMixin:
    def __init__(self):
        self.needs_maintenance = False

    def report_issue(self):
        self.needs_maintenance = True

    def fix_issue(self):
        self.needs_maintenance = False

class PremiumRoom(Room, MaintenanceMixin):
    def __init__(self, number, room_type, price_per_night, amenities):
        super().__init__(number, room_type, price_per_night)
        MaintenanceMixin.__init__(self)
        self.amenities = amenities

    def __str__(self):
        status = "Booked" if self.is_booked else "Available"
        maintenance = " - Needs Maintenance" if self.needs_maintenance else ""
        return f"Room {self.number} ({self.type}): ${self.price}/night - {status}{maintenance}"

Let's also implement a simple payment system to make our booking process more complete.

class Payment:
    def __init__(self, booking, amount, method="credit card"):
        self.booking = booking
        self.amount = amount
        self.method = method
        self.paid = False
        self.payment_date = None

    def process_payment(self):
        # Simulate payment processing
        self.paid = True
        self.payment_date = datetime.now()
        return True

    def __str__(self):
        status = "Paid" if self.paid else "Pending"
        return f"Payment for Booking #{id(self.booking)}: ${self.amount} ({self.method}) - {status}"

Here's how we can integrate payments into our hotel system:

# Enhanced Hotel class with payment processing
class EnhancedHotel(Hotel):
    def __init__(self, name):
        super().__init__(name)
        self.payments = []

    def process_booking_payment(self, booking, payment_method="credit card"):
        payment = Payment(booking, booking.total_cost, payment_method)
        if payment.process_payment():
            self.payments.append(payment)
            return payment
        return None

    def get_revenue_report(self):
        total = sum(p.amount for p in self.payments if p.paid)
        return f"Total Revenue: ${total}"
  • Create room inventory with different types and prices
  • Implement guest registration and profile management
  • Handle booking creation with date validation
  • Process payments and generate receipts
  • Manage room maintenance and availability
  • Generate reports on occupancy and revenue

Let's create a more comprehensive example showing these features working together:

# Create enhanced hotel
enhanced_hotel = EnhancedHotel("Luxury Python Resort")

# Add various room types
enhanced_hotel.add_room(PremiumRoom(201, "Deluxe", 250, ["minibar", "jacuzzi"]))
enhanced_hotel.add_room(Room(202, "Standard", 120))
enhanced_hotel.add_room(PremiumRoom(203, "Presidential", 500, ["butler", "private pool"]))

# Create guests
guest2 = Guest("Bob Smith", "bob@email.com", "555-5678")
guest3 = Guest("Carol Davis", "carol@email.com", "555-9012")

# Make bookings
booking2 = enhanced_hotel.book_room(guest2, 201, datetime(2023, 12, 18), datetime(2023, 12, 22))
booking3 = enhanced_hotel.book_room(guest3, 202, datetime(2023, 12, 20), datetime(2023, 12, 25))

# Process payments
if booking2:
    payment2 = enhanced_hotel.process_booking_payment(booking2)
    print(f"Payment processed: {payment2}")

if booking3:
    payment3 = enhanced_hotel.process_booking_payment(booking3, "debit card")
    print(f"Payment processed: {payment3}")

# Generate revenue report
print(enhanced_hotel.get_revenue_report())
Room Type Average Price Occupancy Rate Revenue Contribution
Standard $120 75% 30%
Deluxe $250 60% 40%
Presidential $500 40% 30%

Now let's implement some error handling and input validation to make our system more robust:

class BookingError(Exception):
    pass

class InvalidDateError(BookingError):
    pass

class RoomUnavailableError(BookingError):
    pass

class EnhancedBookingSystem(EnhancedHotel):
    def validate_booking_dates(self, check_in, check_out):
        if check_out <= check_in:
            raise InvalidDateError("Check-out date must be after check-in date")
        if check_in < datetime.now():
            raise InvalidDateError("Cannot book for past dates")

    def book_room(self, guest, room_number, check_in, check_out):
        try:
            self.validate_booking_dates(check_in, check_out)

            room = next((r for r in self.rooms if r.number == room_number), None)
            if not room:
                raise ValueError("Room not found")
            if room.is_booked:
                raise RoomUnavailableError(f"Room {room_number} is already booked")
            if room.needs_maintenance if hasattr(room, 'needs_maintenance') else False:
                raise RoomUnavailableError(f"Room {room_number} is under maintenance")

            if room.book():
                booking = Booking(guest, room, check_in, check_out)
                self.bookings.append(booking)
                return booking

        except (InvalidDateError, RoomUnavailableError, ValueError) as e:
            print(f"Booking failed: {e}")
            return None

Let's test our error handling with some edge cases:

# Test error handling
try:
    # Past date booking
    past_booking = enhanced_hotel.book_room(
        guest2, 202, 
        datetime(2022, 1, 1), 
        datetime(2022, 1, 5)
    )

    # Invalid date range
    invalid_dates = enhanced_hotel.book_room(
        guest3, 203,
        datetime(2023, 12, 25),
        datetime(2023, 12, 20)
    )

except Exception as e:
    print(f"Error: {e}")

# Try to book already booked room
duplicate_booking = enhanced_hotel.book_room(
    guest1, 201,
    datetime(2023, 12, 25),
    datetime(2023, 12, 30)
)

Finally, let's add some reporting functionality to help hotel management make data-driven decisions:

class HotelAnalytics:
    def __init__(self, hotel):
        self.hotel = hotel

    def occupancy_rate(self):
        total_rooms = len(self.hotel.rooms)
        booked_rooms = sum(1 for room in self.hotel.rooms if room.is_booked)
        return (booked_rooms / total_rooms) * 100 if total_rooms > 0 else 0

    def average_stay_length(self):
        if not self.hotel.bookings:
            return 0
        total_nights = sum((b.check_out - b.check_in).days for b in self.hotel.bookings)
        return total_nights / len(self.hotel.bookings)

    def revenue_by_room_type(self):
        revenue = {}
        for payment in self.hotel.payments:
            if payment.paid:
                room_type = payment.booking.room.type
                revenue[room_type] = revenue.get(room_type, 0) + payment.amount
        return revenue

# Usage
analytics = HotelAnalytics(enhanced_hotel)
print(f"Occupancy Rate: {analytics.occupancy_rate():.1f}%")
print(f"Average Stay: {analytics.average_stay_length():.1f} nights")
print("Revenue by Room Type:", analytics.revenue_by_room_type())
  • Start with simple classes and gradually add complexity
  • Use inheritance for specialized room types and features
  • Implement proper error handling for robust operation
  • Add analytics to track business performance
  • Consider using databases for persistent storage in real applications
  • Implement user authentication for multi-user access
  • Add reservation modification and cancellation features

This hotel booking system demonstrates how object-oriented programming can help you create organized, maintainable code for complex real-world applications. Each class has a clear responsibility, and the relationships between them model the actual hotel operations.

Remember that this is a simplified version. In a production system, you'd want to add features like database persistence, user authentication, a web interface, and more comprehensive error handling. However, this foundation gives you all the core concepts you need to build upon.

Keep coding and enhancing this system – try adding features like room service orders, housekeeping management, or integration with payment gateways. The possibilities are endless when you have a solid OOP foundation!