Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion problems/parking-lot.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

## Classes, Interfaces and Enumerations
1. The **ParkingLot** class follows the Singleton pattern to ensure only one instance of the parking lot exists. It maintains a list of levels and provides methods to park and unpark vehicles.
2. The **Level** class represents a level in the parking lot and contains a list of parking spots. It handles parking and unparking of vehicles within the level.
2. The **ParkingFloor** class represents a level in the parking lot and contains a list of parking spots. It handles parking and unparking of vehicles within the level.
3. The **ParkingSpot** class represents an individual parking spot and tracks the availability and the parked vehicle.
4. The **Vehicle** class is an abstract base class for different types of vehicles. It is extended by Car, Motorcycle, and Truck classes.
5. The **VehicleType** enum defines the different types of vehicles supported by the parking lot.
Expand Down
48 changes: 48 additions & 0 deletions solutions/python/concertticketbookingsystem/booking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from datetime import datetime
from typing import List
from seat import Seat
from user import User
from concert import Concert
from enums import BookingStatus, SeatStatus

class Booking:
def __init__(self, id: str, user: User, concert: Concert, seats: List[Seat]):
self.id = id
self.user = user
self.concert = concert
self.seats = seats
self.totalPrice = sum(seat.price for seat in self.seats)
self.bookingStatus = BookingStatus.PENDING

def confirmBooking(self):
if self.bookingStatus == BookingStatus.PENDING:
self.bookingStatus = BookingStatus.CONFIRMED
for seat in self.seats:
seat.confirm_booking()
return True

def cancelBooking(self):
print(f"Booking {self.id} cancelled")
# Release all seats
for seat in self.seats:
seat.release()
self.bookingStatus = BookingStatus.CANCELLED
return True

def getId(self) -> str:
return self.id

def getUser(self) -> "User":
return self.user

def getConcert(self) -> "Concert":
return self.concert

def getSeats(self) -> List["Seat"]:
return self.seats

def getTotalPrice(self) -> float:
return self.totalPrice

def getStatus(self) -> "BookingStatus":
return
27 changes: 27 additions & 0 deletions solutions/python/concertticketbookingsystem/concert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from datetime import datetime
from typing import List
from seat import Seat
from dataclasses import dataclass

@dataclass
class Concert:
id: str
artist: str
venue: str
dateTime: datetime
seats: List[Seat]

def getId(self) -> str:
return self.id

def getArtist(self) -> str:
return self.artist

def getVenue(self) -> str:
return self.venue

def getDateTime(self) -> datetime:
return self.dateTime

def getSeats(self) -> List["Seat"]:
return self.seats
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from datetime import datetime
from threading import Lock
from typing import List
from concert import Concert
from user import User
from seat import Seat, SeatNotAvailableException
from booking import Booking
from enums import SeatStatus
import uuid
class ConcertTicketBookingSystem:
_instance = None

def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.concerts = {}
cls._instance.bookings = {}
cls._instance._lock = Lock()
return cls._instance

def addConcert(self, concert: Concert):
self.concerts[concert.id] = concert

def getConcert(self, concertId: str) -> "Concert":
if concertId in self.concerts:
return self.concerts[concertId]
print(f"No concert with concertId {concertId} found")

def searchConcerts(self, artist: str, venue: str, dateTime: datetime) -> List["Concert"]:
return [ concert for _, concert in self.concerts.items() \
if concert.getArtist() == artist \
and concert.getVenue() == venue \
and concert.getDateTime() == dateTime ]

def bookTickets(self, user: User, concert: Concert, seats: List["Seat"]) -> "Booking":
with self._lock:
# ensure all the seats are available
for seat in seats:
if seat.seatStatus != SeatStatus.AVAILABLE:
raise SeatNotAvailableException("Seat not available")
for seat in seats:
# Reserve all seats
seat.book()

bookingId = self._generateId(user, concert)
booking = Booking(bookingId, user, concert, seats)

self.bookings[bookingId] = booking
self._processPayment(booking)
booking.confirmBooking()
print(f"Booking {bookingId} confirmed")
return booking

def cancelBooking(self, bookingId: str):
with self._lock:
if bookingId in self.bookings:
booking = self.bookings.pop(bookingId)
booking.cancelBooking()

def _processPayment(self, booking: Booking) -> bool:
price = booking.getTotalPrice()
print(f"Processed payment for amount {price}")
return True

def _generateId(self, user: User, concert: Concert) -> str:
return f"BKG{user.id}-{concert.id}-{uuid.uuid4()}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from datetime import datetime, timedelta
from typing import List
from concertticketbookingsystem import ConcertTicketBookingSystem
from concert import Concert
from seat import Seat, SeatType, SeatStatus
from user import User

class ConcertTicketBookingSystemDemo:
@staticmethod
def run():
# Create concert ticket booking system instance
booking_system = ConcertTicketBookingSystem()

# Create concerts
concert1_seats = ConcertTicketBookingSystemDemo._generate_seats(10)
concert1 = Concert("C001", "Artist 1", "Venue 1", datetime.now().replace(day=10, hour=20, minute=0, second=0, microsecond=0), concert1_seats)
booking_system.addConcert(concert1)

concert2_seats = ConcertTicketBookingSystemDemo._generate_seats(5)
concert2 = Concert("C002", "Artist 2", "Venue 2", datetime.now().replace(day=15, hour=19, minute=30, second=0, microsecond=0), concert2_seats)
booking_system.addConcert(concert2)

# Create users
user1 = User("U001", "John Doe", "john@example.com")
user2 = User("U002", "Jane Smith", "jane@example.com")

# Search concerts
search_results = booking_system.searchConcerts("Artist 1", "Venue 1", concert1.getDateTime())
print("Search Results:")
for concert in search_results:
print(f"Concert: {concert.getArtist()} at {concert.getVenue()}")

# Book tickets
print("Booking 3 tickets for concert 1")
selected_seats1 = ConcertTicketBookingSystemDemo._select_seats(concert1, 3)
booking1 = booking_system.bookTickets(user1, concert1, selected_seats1)
seats = concert1.getSeats()
for seat in seats:
print(seat)

print("Booking 2 tickets for concert2")
selected_seats2 = ConcertTicketBookingSystemDemo._select_seats(concert2, 2)
booking2 = booking_system.bookTickets(user2, concert2, selected_seats2)
seats = concert2.getSeats()
for seat in seats:
print(seat)

# Cancel booking
print("Cancelling Booking1 with 3 seats")
booking_system.cancelBooking(booking1.id)
print("Seats after cancelling booking 1 - all are available")
seats = concert1.getSeats()
for seat in seats:
print(seat)

# Book tickets again
print("Booking 2 tickets for concert 1")
selected_seats3 = ConcertTicketBookingSystemDemo._select_seats(concert1, 2)
booking3 = booking_system.bookTickets(user2, concert1, selected_seats3)
print("Seats after booking with booking 3")
seats = concert1.getSeats()
for seat in seats:
print(seat)

@staticmethod
def _generate_seats(number_of_seats: int) -> List[Seat]:
seats = []
for i in range(1, number_of_seats + 1):
seat_number = f"S{i}"
seat_type = SeatType.VIP if i <= 1 else SeatType.PREMIUM if i <= 3 else SeatType.REGULAR
price = 100.0 if seat_type == SeatType.VIP else 75.0 if seat_type == SeatType.PREMIUM else 50.0
seats.append(Seat(seat_number, seat_number, seat_type, price))
return seats

@staticmethod
def _select_seats(concert: Concert, number_of_seats: int) -> List[Seat]:
available_seats = [seat for seat in concert.seats if seat.seatStatus == SeatStatus.AVAILABLE]
return available_seats[:number_of_seats]

if __name__ == "__main__":
ConcertTicketBookingSystemDemo.run()
16 changes: 16 additions & 0 deletions solutions/python/concertticketbookingsystem/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from enum import Enum

class SeatType(Enum):
REGULAR = "REGULAR"
PREMIUM = "PREMIUM"
VIP = "VIP"

class SeatStatus(Enum):
AVAILABLE = "AVAILABLE"
BOOKED = "BOOKED"
RESERVED = "RESERVED"

class BookingStatus(Enum):
PENDING = "PENDING"
CONFIRMED = "CONFIRMED"
CANCELLED = "CANCELLED"
61 changes: 61 additions & 0 deletions solutions/python/concertticketbookingsystem/seat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from enums import SeatType, SeatStatus
import threading

class SeatNotAvailableException(Exception):
pass

class Seat:
def __init__(self, id: str, seatNumber: str, seatType: SeatType, price: float):
self.id = id
self.seatNumber = seatNumber
self.seatType = seatType
self.price = price
self.seatStatus = SeatStatus.AVAILABLE
self._lock = threading.Lock()

# We will use a state machine to ensure correctness
# This way, cant book if seat is already reserved,
# Cant release is seat is not booked etc.
# The lock ensures thread safety, state machine ensures correctness
def book(self):
with self._lock:
if self.seatStatus != SeatStatus.AVAILABLE:
raise Exception("Seat not available")
self.seatStatus = SeatStatus.RESERVED

def confirm_booking(self):
# This function is called after payment is confirmed so the seat status
# gets updated from reserved to booked.
with self._lock:
if self.seatStatus != SeatStatus.RESERVED:
raise Exception("Invalid state")
self.seatStatus = SeatStatus.BOOKED

def release(self):
with self._lock:
# If not really required by added for sake of correctness in transitions
if self.seatStatus == SeatStatus.AVAILABLE:
print("Seat was already in available state.")
return
self.seatStatus = SeatStatus.AVAILABLE

def getPrice(self) -> float:
return self.price

def getId(self) -> str:
return self.id

def getSeatType(self) -> "SeatType":
return self.seatType

def getSeatNumber(self) -> str:
return self.seatNumber

def getSeatStatus(self) -> "SeatStatus":
return self.seatStatus

def __str__(self):
return f"Seat: {self.id}, Status: {self.seatStatus}"

def __repr__(self):
return f"Seat(id={self.id})"
8 changes: 8 additions & 0 deletions solutions/python/concertticketbookingsystem/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from dataclasses import dataclass

# Since we wont be altering the contents of user objects, mark frozen.
@dataclass(frozen=True)
class User:
id: str
name: str
email: str
4 changes: 2 additions & 2 deletions solutions/python/parkinglot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

## Classes, Interfaces and Enumerations
1. The **ParkingLot** class follows the Singleton pattern to ensure only one instance of the parking lot exists. It maintains a list of levels and provides methods to park and unpark vehicles.
2. The **Level** class represents a level in the parking lot and contains a list of parking spots. It handles parking and unparking of vehicles within the level.
2. The **ParkingFloor** class represents a level in the parking lot and contains a list of parking spots. It handles parking and unparking of vehicles within the level.
3. The **ParkingSpot** class represents an individual parking spot and tracks the availability and the parked vehicle.
4. The **Vehicle** class is an abstract base class for different types of vehicles. It is extended by Car, Motorcycle, and Truck classes.
5. The **VehicleType** enum defines the different types of vehicles supported by the parking lot.
Expand All @@ -20,4 +20,4 @@
## Design Patterns Used:
1. Singleton Pattern: Ensures only one instance of the ParkingLot class.
2. Factory Pattern (optional extension): Could be used for creating vehicles based on input.
3. Observer Pattern (optional extension): Could notify customers about available spots.
3. Observer Pattern (optional extension): Could notify customers about available spots.