diff --git a/.gitignore b/.gitignore index 6769e21..b0b6f3a 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file +.idea/ \ No newline at end of file diff --git a/Design_Parking_Lot/problem-statement.md b/Design_Parking_Lot/problem-statement.md index 258e419..9efe41f 100644 --- a/Design_Parking_Lot/problem-statement.md +++ b/Design_Parking_Lot/problem-statement.md @@ -109,6 +109,7 @@ We welcome contributions to this project. If you are interested in contributing, - Finalize the testing module. - Rectify the output format. - Enhance the command set by adding new commands. +- Add Thread safety in implementation. If you are interested in contributing to any of these tasks, please feel free to reach out for more information. diff --git a/Design_Parking_Lot/src/commands/commands.py b/Design_Parking_Lot/src/commands/commands.py index ee35a81..66c42e2 100644 --- a/Design_Parking_Lot/src/commands/commands.py +++ b/Design_Parking_Lot/src/commands/commands.py @@ -1,6 +1,5 @@ from model.entrance_gate import EntranceGate from model.exit_gate import ExitGate -from components.factory.parking_spot_manager_factory import ParkingSpotManagerFactory class Commands: def __init__(self, command, entrance_gate: EntranceGate, exit_gate: ExitGate, parking_spot_manager_factory): @@ -8,6 +7,17 @@ def __init__(self, command, entrance_gate: EntranceGate, exit_gate: ExitGate, pa self.entrance_gate = entrance_gate self.exit_gate = exit_gate self.parking_spot_manager_factory = parking_spot_manager_factory + self.command_handlers = { + 'park_vehicle': self._handle_park_vehicle, + 'free_parking_spot': self._handle_free_parking_spot, + 'get_vehicles_by_color': self._handle_get_vehicles_by_color, + 'get_vehicles_by_registration_no': self._handle_get_vehicles_by_registration_no, + 'list_all_parked_vehicles': self._handle_list_all_parked_vehicles, + 'add_parking_spot': self._handle_add_parking_spot, + 'remove_parking_spot': self._handle_remove_parking_spot, + 'generate_report': self._handle_generate_report, + 'exit': self._handle_exit + } """ These commands are limited in scope and do not encompass all potential test cases. @@ -17,47 +27,89 @@ def __init__(self, command, entrance_gate: EntranceGate, exit_gate: ExitGate, pa Additionally, the output format may not be optimal and will be improved at a later time. """ def execute(self): - command = self.command.split(' ') - if command[0] == 'park_vehicle': - if len(command) != 4: - raise ValueError(f'Invalid command: {self.command}\nSuggested command format: park_vehicle ') - self.entrance_gate.park_vehicle(registration_no=command[1], color=command[2], vehicle_type=command[3]) - - elif command[0] == 'free_parking_spot': - if len(command) != 3: - raise ValueError(f'Invalid command: {self.command}\nSuggested command format: free_parking_spot ') - parking_spot_manager = self.parking_spot_manager_factory.get_spot_manager(command[2]) - parking_spot_manager.free_parking_spot(command[1]) - - elif command[0] == 'get_vehicles_by_color': - if len(command) != 2: - raise ValueError(f'Invalid command: {self.command}\nSuggested command format: get_vehicles_by_color ') - self.entrance_gate.get_vehicles_by_color(color=command[1]) - - elif command[0] == 'get_vehicles_by_registration_no': - if len(command) != 2: - raise ValueError(f'Invalid command: {self.command}\nSuggested command format: get_vehicles_by_registration_no ') - self.entrance_gate.get_vehicle_by_registration_no(command[1]) - - elif command[0] == 'list_all_parked_vehicles': - if len(command) != 1: - raise ValueError(f'Invalid command: {self.command}\nSuggested command format: list_all_parking_vehicles') - self.entrance_gate.list_all_parked_vehicles() - - elif command[0] == 'add_parking_spot': - if len(command) != 3: - raise ValueError(f'Invalid command: {self.command}\nSuggested command format: add_parking_spot ') - parking_spot_manager = self.parking_spot_manager_factory.get_spot_manager(command[2]) - parking_spot_manager.add_parking_spot(command[1]) - - elif command[0] == 'remove_parking_spot': - if len(command) != 3: - raise ValueError(f'Invalid command: {self.command}\nSuggested command format: remove_parking_spot ') - parking_spot_manager = self.parking_spot_manager_factory.get_spot_manager(command[2]) - parking_spot_manager.remove_parking_spot(command[1]) - - elif command[0] == 'exit': - exit() - - else: - raise ValueError(f'Invalid command: {self.command}') \ No newline at end of file + parts = self.command.strip().split(' ') + cmd = parts[0] + + handler = self.command_handlers.get(cmd) + if not handler: + raise ValueError(f'Invalid command: {self.command}') + + handler(parts) + + # === Command Handlers === + + def _handle_park_vehicle(self, args): + if len(args) != 4: + raise ValueError(f'Invalid command: {" ".join(args)}\nSuggested format: park_vehicle ') + _, registration_no, color, vehicle_type = args + self.entrance_gate.park_vehicle(registration_no, color, vehicle_type) + + def _handle_free_parking_spot(self, args): + if len(args) != 3: + raise ValueError(f'Invalid command: {" ".join(args)}\nSuggested format: free_parking_spot ') + _, spot_number, vehicle_type = args + manager = self.parking_spot_manager_factory.get_spot_manager(vehicle_type) + manager.free_parking_spot(spot_number) + + def _handle_get_vehicles_by_color(self, args): + if len(args) != 2: + raise ValueError(f'Invalid command: {" ".join(args)}\nSuggested format: get_vehicles_by_color ') + _, color = args + self.entrance_gate.get_vehicles_by_color(color) + + def _handle_get_vehicles_by_registration_no(self, args): + if len(args) != 2: + raise ValueError(f'Invalid command: {" ".join(args)}\nSuggested format: get_vehicles_by_registration_no ') + _, registration_no = args + self.entrance_gate.get_vehicle_by_registration_no(registration_no) + + def _handle_list_all_parked_vehicles(self, args): + if len(args) != 1: + raise ValueError(f'Invalid command: {" ".join(args)}\nSuggested format: list_all_parked_vehicles') + self.entrance_gate.list_all_parked_vehicles() + + def _handle_add_parking_spot(self, args): + if len(args) != 3: + raise ValueError(f'Invalid command: {" ".join(args)}\nSuggested format: add_parking_spot ') + _, spot_no, vehicle_type = args + manager = self.parking_spot_manager_factory.get_spot_manager(vehicle_type) + manager.add_parking_spot(spot_no) + + def _handle_remove_parking_spot(self, args): + if len(args) != 3: + raise ValueError(f'Invalid command: {" ".join(args)}\nSuggested format: remove_parking_spot ') + _, spot_no, vehicle_type = args + manager = self.parking_spot_manager_factory.get_spot_manager(vehicle_type) + manager.remove_parking_spot(spot_no) + + def _handle_generate_report(self, args): + report_data = self.entrance_gate.get_report_data() + + print("=" * 20) + print("PARKING LOT REPORT") + print("=" * 20) + + total_vehicles = 0 + + for vehicle_type, data in report_data.items(): + print(f"\n Vehicle Type: {vehicle_type}") + print(f"Total Spots : {data['total_spots']}") + print(f"Available Spots : {data['available_spots']}") + print(f"Occupied Spots : {data['occupied_spots']}") + print("Parked Vehicles :") + + if not data['vehicles']: + print("No vehicles parked.") + else: + for idx, v in enumerate(data['vehicles'], 1): + print(f" {idx}. RegNo: {v['registration_no']}, Color: {v['color']}, Spot: {v['spot_number']}") + + total_vehicles += data['occupied_spots'] + + print("\n Summary:") + print(f"Total Parked Vehicles: {total_vehicles}") + print("=" * 20) + + @staticmethod + def _handle_exit(args): + exit() diff --git a/Design_Parking_Lot/src/components/factory/parking_spot_manager_factory.py b/Design_Parking_Lot/src/components/factory/parking_spot_manager_factory.py index e1c6085..7ae7d5c 100644 --- a/Design_Parking_Lot/src/components/factory/parking_spot_manager_factory.py +++ b/Design_Parking_Lot/src/components/factory/parking_spot_manager_factory.py @@ -1,21 +1,17 @@ -from abc import ABC, abstractmethod - -from components.parking_spot_managers.two_wheeler_parking_spot_manager import TwoWheelerParkingSpotManager +from components.parking_spot_managers.two_wheeler_parking_spot_manager import TwoWheelerParkingSpotManager from components.parking_spot_managers.four_wheeler_parking_spot_manager import FourWheelerParkingSpotManager from components.parking_spot_managers.huv_parking_spot_manager import HUVParkingSpotManager class ParkingSpotManagerFactory: def __init__(self): - self.two_wheeler_parking_spot_manager = TwoWheelerParkingSpotManager() - self.four_wheeler_parking_spot_manager = FourWheelerParkingSpotManager() - self.huv_parking_spot_manager = HUVParkingSpotManager() - - def get_spot_manager(self, vehicle_type): - if vehicle_type == "TwoWheeler": - return self.two_wheeler_parking_spot_manager - elif vehicle_type == "FourWheeler": - return self.four_wheeler_parking_spot_manager - elif vehicle_type == "HUV": - return self.huv_parking_spot_manager - else: - raise ValueError("Invalid vehicle_type: {}".format(vehicle_type)) \ No newline at end of file + self._registry = { + "TwoWheeler": TwoWheelerParkingSpotManager(), + "FourWheeler": FourWheelerParkingSpotManager(), + "HUV": HUVParkingSpotManager() + } + + def get_spot_manager(self, vehicle_type: str): + manager = self._registry.get(vehicle_type) + if not manager: + raise ValueError(f"Invalid vehicle type: {vehicle_type}") + return manager diff --git a/Design_Parking_Lot/src/components/parking_spot_managers/four_wheeler_parking_spot_manager.py b/Design_Parking_Lot/src/components/parking_spot_managers/four_wheeler_parking_spot_manager.py index 0f719cd..f5c1607 100644 --- a/Design_Parking_Lot/src/components/parking_spot_managers/four_wheeler_parking_spot_manager.py +++ b/Design_Parking_Lot/src/components/parking_spot_managers/four_wheeler_parking_spot_manager.py @@ -1,18 +1,12 @@ -from abc import ABC, abstractmethod from components.parking_spot_managers.parking_spot_manager import ParkingSpotManager from model.parking_spot import FourWheelerParkingSpot class FourWheelerParkingSpotManager(ParkingSpotManager): - def __init__(self): - self.parking_spot_list = {} - self.total_spots = 0 - self.available_spots = 0 def add_parking_spot(self, spot_number): - parking_spot = FourWheelerParkingSpot(spot_number=spot_number) - try: - self.parking_spot_list.update({spot_number: parking_spot}) - self.available_spots += 1 - self.total_spots += 1 - except: - raise KeyError(f"Parking Spot with this spot number ({spot_number}) already exists") \ No newline at end of file + if spot_number in self.parking_spot_list: + raise KeyError(f"Spot {spot_number} already exists.") + + self.parking_spot_list[spot_number] = FourWheelerParkingSpot(spot_number=spot_number) + self.total_spots += 1 + self.available_spots += 1 diff --git a/Design_Parking_Lot/src/components/parking_spot_managers/huv_parking_spot_manager.py b/Design_Parking_Lot/src/components/parking_spot_managers/huv_parking_spot_manager.py index 7c7e2ac..7c6b30c 100644 --- a/Design_Parking_Lot/src/components/parking_spot_managers/huv_parking_spot_manager.py +++ b/Design_Parking_Lot/src/components/parking_spot_managers/huv_parking_spot_manager.py @@ -3,16 +3,11 @@ from model.parking_spot import HUVParkingSpot class HUVParkingSpotManager(ParkingSpotManager): - def __init__(self): - self.parking_spot_list = {} - self.total_spots = 0 - self.available_spots = 0 def add_parking_spot(self, spot_number): - parking_spot = HUVParkingSpot(spot_number=spot_number) - try: - self.parking_spot_list.update({spot_number: parking_spot}) - self.available_spots += 1 - self.total_spots += 1 - except: - raise KeyError(f"Parking Spot with this spot number ({spot_number}) already exists") \ No newline at end of file + if spot_number in self.parking_spot_list: + raise KeyError(f"Spot {spot_number} already exists.") + + self.parking_spot_list[spot_number] = HUVParkingSpot(spot_number=spot_number) + self.total_spots += 1 + self.available_spots += 1 diff --git a/Design_Parking_Lot/src/components/parking_spot_managers/parking_spot_manager.py b/Design_Parking_Lot/src/components/parking_spot_managers/parking_spot_manager.py index e99ba48..5d351ae 100644 --- a/Design_Parking_Lot/src/components/parking_spot_managers/parking_spot_manager.py +++ b/Design_Parking_Lot/src/components/parking_spot_managers/parking_spot_manager.py @@ -1,32 +1,35 @@ from abc import ABC, abstractmethod -from model.parking_spot import ParkingSpot -from components.strategy.parking_strategy import ParkingStrategy -from model.vehicle import * +from components.strategy.parking_strategy import ParkingStrategyContext + + +class ParkingSpotManager(ABC): + def __init__(self): + self.parking_spot_list = {} + self.total_spots = 0 + self.available_spots = 0 -class ParkingSpotManager: @abstractmethod def add_parking_spot(self, spot_number): pass def remove_parking_spot(self, spot_number): - if spot_number in self.parking_spot_list: - if self.parking_spot_list[spot_number].is_empty == False: - raise ValueError(f"A vehicle is already parked in spot: {spot_number}") - - print(f'Available parking spots: {list(self.parking_spot_list.keys())}') - if spot_number in self.parking_spot_list: - value = self.parking_spot_list.pop(spot_number, None) - self.available_spots -= 1 - self.total_spots -= 1 - print(f'After removing spot({spot_number}) from parking spots: {list(self.parking_spot_list.keys())}') + spot = self.parking_spot_list.get(spot_number) + if not spot: + raise KeyError(f"Parking spot {spot_number} does not exist.") + + if not spot.is_empty: + raise ValueError(f"A vehicle is already parked in spot: {spot_number}") + + self.parking_spot_list.pop(spot_number) + self.available_spots -= 1 + self.total_spots -= 1 + print(f'Removed spot {spot_number}. Remaining Spots: {list(self.parking_spot_list.keys())}') def is_full(self): - if self.available_spots == 0: - return True - return False + return self.available_spots == 0 def find_parking_spot(self, strategy: str = "default"): - return ParkingStrategy().find_parking_spot(parking_spot_list=self.parking_spot_list, strategy=strategy) + return ParkingStrategyContext().find_parking_spot(parking_spot_list=self.parking_spot_list, strategy=strategy) def park_vehicle(self, vehicle): if self.is_full(): @@ -34,35 +37,32 @@ def park_vehicle(self, vehicle): return parking_spot = self.find_parking_spot() parking_spot.park_vehicle(vehicle) + self.available_spots -= 1 return parking_spot def free_parking_spot(self, spot_number): - if spot_number in self.parking_spot_list: - parking_spot = self.parking_spot_list[spot_number] - parking_spot.remove_vehicle() - self.available_spots += 1 + spot = self.parking_spot_list.get(spot_number) + if not spot: + print("This parking spot does not exist.") + return + if spot.is_empty: + print("Spot is already empty.") return - print("This parking spot is not available") + spot.remove_vehicle() + self.available_spots += 1 def get_spots_by_vehicle_color(self, color): - parking_spots = [ - self.parking_spot_list[key] for key in self.parking_spot_list.keys() - if (self.parking_spot_list[key].is_empty==False - and self.parking_spot_list[key].vehicle.color==color) + return [ + spot for spot in self.parking_spot_list.values() + if not spot.is_empty and spot.vehicle.color == color ] - return parking_spots def get_spot_by_vehicle_registration_no(self, registration_no): - parking_spot = [ - self.parking_spot_list[key] for key in self.parking_spot_list.keys() - if (self.parking_spot_list[key].is_empty==False - and self.parking_spot_list[key].vehicle.registration_no==registration_no) - ] - return None if (len(parking_spot)==0) else parking_spot[0] + for spot in self.parking_spot_list.values(): + if not spot.is_empty and spot.vehicle.registration_no == registration_no: + return spot + return None def list_all_parked_spots(self): - spots = [ - self.parking_spot_list[key] for key in self.parking_spot_list.keys() if (self.parking_spot_list[key].is_empty==False) - ] - return spots \ No newline at end of file + return [spot for spot in self.parking_spot_list.values() if not spot.is_empty] \ No newline at end of file diff --git a/Design_Parking_Lot/src/components/parking_spot_managers/two_wheeler_parking_spot_manager.py b/Design_Parking_Lot/src/components/parking_spot_managers/two_wheeler_parking_spot_manager.py index 39ab3e2..daf98a9 100644 --- a/Design_Parking_Lot/src/components/parking_spot_managers/two_wheeler_parking_spot_manager.py +++ b/Design_Parking_Lot/src/components/parking_spot_managers/two_wheeler_parking_spot_manager.py @@ -4,16 +4,11 @@ from model.vehicle import * class TwoWheelerParkingSpotManager(ParkingSpotManager): - def __init__(self): - self.parking_spot_list = {} - self.total_spots = 0 - self.available_spots = 0 def add_parking_spot(self, spot_number): - parking_spot = TwoWheelerParkingSpot(spot_number=spot_number) - try: - self.parking_spot_list.update({spot_number: parking_spot}) - self.available_spots += 1 - self.total_spots += 1 - except: - print(f"Parking Spot with this spot number ({spot_number}) already exists") \ No newline at end of file + if spot_number in self.parking_spot_list: + raise KeyError(f"Spot {spot_number} already exists.") + + self.parking_spot_list[spot_number] = TwoWheelerParkingSpot(spot_number=spot_number) + self.total_spots += 1 + self.available_spots += 1 \ No newline at end of file diff --git a/Design_Parking_Lot/src/components/strategy/parking_strategy.py b/Design_Parking_Lot/src/components/strategy/parking_strategy.py index f334de2..e5160d8 100644 --- a/Design_Parking_Lot/src/components/strategy/parking_strategy.py +++ b/Design_Parking_Lot/src/components/strategy/parking_strategy.py @@ -1,33 +1,91 @@ -from abc import ABC, abstractmethod +from abc import ABC, abstractmethod -class ParkingStrategy: - def find_parking_spot(self, parking_spot_list, strategy="default"): - """default strategy is first come first serve""" - if strategy == "nearest": - return NearestParkingStrategy().find_parking_spot(parking_spot_list) - return FirstComeFirstServe().find_parking_spot(parking_spot_list) -class NearestParkingStrategy: - """ - For simplicity, the logic assumes that even numbered spots are closest in ascending order and - odd numbered spots are farthest away. However, you can implement your own logic as desired. +class ParkingStrategy(ABC): + """Abstract base class for parking strategies.""" + + @abstractmethod + def find_parking_spot(self, parking_spot_list: dict): + pass + + +class FirstComeFirstServe(ParkingStrategy): + """Assign the first available parking spot (ordered by insertion or ID).""" + + def find_parking_spot(self, parking_spot_list: dict): + for spot in parking_spot_list.values(): + if spot.is_empty: + print(f'FirstComeFirstServe selected Spot: {spot.spot_number}') + return spot + print("No available spots.") + return None - As an example, if there are 10 spots numbered from 0 to 9, - sorting them in ascending order of proximity would result - in the following list: [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]. +class NearestParkingStrategy(ParkingStrategy): """ + Even-numbered spots are considered closer than odd-numbered ones. + Prioritize even-numbered available spots, then odd-numbered. + """ + + def find_parking_spot(self, parking_spot_list: dict): + even_spots = sorted( + [spot for spot in parking_spot_list.values() if spot.is_empty and int(spot.spot_number) % 2 == 0], + key=lambda x: int(x.spot_number) + ) + if even_spots: + print(f'NearestParkingStrategy selected even Spot: {even_spots[0].spot_number}') + return even_spots[0] + + odd_spots = sorted( + [spot for spot in parking_spot_list.values() if spot.is_empty and int(spot.spot_number) % 2 != 0], + key=lambda x: int(x.spot_number) + ) + if odd_spots: + print(f'NearestParkingStrategy selected odd Spot: {odd_spots[0].spot_number}') + return odd_spots[0] + + raise Exception("No available parking spots") + + +class HighestChargeStrategy(ParkingStrategy): + """Choose the most expensive available parking spot.""" + + def find_parking_spot(self, parking_spot_list: dict): + available_spots = [ + spot for spot in parking_spot_list.values() if spot.is_empty + ] + if not available_spots: + raise Exception("No available parking spots") + + best_spot = max(available_spots, key=lambda s: s.hour_charge) + print(f'HighestChargeStrategy selected Spot: {best_spot.spot_number} with charge {best_spot.hour_charge}') + return best_spot + + +class RandomStrategy(ParkingStrategy): + """Choose a random spot from available ones.""" + + def find_parking_spot(self, parking_spot_list: dict): + import random + available_spots = [ + spot for spot in parking_spot_list.values() if spot.is_empty + ] + if not available_spots: + raise Exception("No available parking spots") + chosen = random.choice(available_spots) + print(f'RandomStrategy selected Spot: {chosen.spot_number}') + return chosen + + +class ParkingStrategyContext: + """Context class to select and use a parking strategy.""" + @staticmethod + def find_parking_spot(parking_spot_list: dict, strategy: str = "default"): + strategy_map = { + "default": FirstComeFirstServe(), + "nearest": NearestParkingStrategy(), + "expensive": HighestChargeStrategy(), + "random": RandomStrategy(), + } - def find_parking_spot(self, parking_dict): - even_keys = [key for key in parking_dict.keys() if key % 2 == 0 and parking_dict[key].is_empty()] - if even_keys: - return parking_dict[min(even_keys)] - else: - odd_keys = [key for key in parking_dict.keys() if key % 2 != 0 and parking_dict[key].is_empty()] - return parking_dict[min(odd_keys)] - -class FirstComeFirstServe: - def find_parking_spot(self, parking_spot_list): - for spot_no, parking_spot in parking_spot_list.items(): - if parking_spot.is_empty: - print(f'Parking Spot: {parking_spot.spot_number}') - return parking_spot \ No newline at end of file + selected_strategy = strategy_map.get(strategy, FirstComeFirstServe()) + return selected_strategy.find_parking_spot(parking_spot_list) diff --git a/Design_Parking_Lot/src/input.txt b/Design_Parking_Lot/src/input.txt index dfbf642..a12e6ab 100644 --- a/Design_Parking_Lot/src/input.txt +++ b/Design_Parking_Lot/src/input.txt @@ -1,5 +1,5 @@ -park_vehicle ABC-123 Red TwoWheeler -park_vehicle DEF-456 White FourWheeler +park_vehicle Red ABC-123 TwoWheeler +park_vehicle White DEF-456 FourWheeler list_all_parked_vehicles free_parking_spot 0 TwoWheeler list_all_parked_vehicles diff --git a/Design_Parking_Lot/src/model/entrance_gate.py b/Design_Parking_Lot/src/model/entrance_gate.py index 84c15e2..e6b1189 100644 --- a/Design_Parking_Lot/src/model/entrance_gate.py +++ b/Design_Parking_Lot/src/model/entrance_gate.py @@ -5,11 +5,10 @@ class EntranceGate: def __init__(self, parking_spot_manager_factory: ParkingSpotManagerFactory): self.parking_spot_manager_factory = parking_spot_manager_factory - + self.supported_vehicle_types = ["TwoWheeler", "FourWheeler", "HUV"] + def find_parking_spot(self, vehicle_type, strategy="default"): - parking_spot_manager = self.parking_spot_manager_factory.get_spot_manager(vehicle_type) - parking_spot = parking_spot_manager.find_parking_spot(strategy=strategy) - return parking_spot + return self._get_manager(vehicle_type).find_parking_spot(strategy=strategy) def generate_ticket(self, color, registration_no, vehicle_type, strategy="default"): vehicle = Vehicle().create_vehicle(color=color, registration_no=registration_no, vehicle_type=vehicle_type) @@ -20,39 +19,67 @@ def generate_ticket(self, color, registration_no, vehicle_type, strategy="defaul def park_vehicle(self, color, registration_no, vehicle_type): vehicle = Vehicle().create_vehicle(color=color, registration_no=registration_no, vehicle_type=vehicle_type) - parking_spot_manager = self.parking_spot_manager_factory.get_spot_manager(vehicle_type) - parking_spot_manager.park_vehicle(vehicle) + self._get_manager(vehicle_type).park_vehicle(vehicle) def get_vehicles_by_color(self, color, vehicle_type="all"): - if vehicle_type == "all": - self.get_vehicles_by_color(color, vehicle_type="TwoWheeler") - self.get_vehicles_by_color(color, vehicle_type="FourWheeler") - self.get_vehicles_by_color(color, vehicle_type="HUV") - elif vehicle_type in ['TwoWheeler', 'FourWheeler', 'HUV']: - spots = self.parking_spot_manager_factory.get_spot_manager(vehicle_type).get_spots_by_vehicle_color(color) - print(f'{vehicle_type} of {color} color is:') - for spot in spots: - print(f'RegistrationNo: {spot.vehicle.get_vehicle_registration_no()}') - print(f'Assigned Spot Number: {spot.spot_number}') - else: - print('Wrong vehicle type') + vehicle_types = self.supported_vehicle_types if vehicle_type == "all" else [vehicle_type] + + for v_type in vehicle_types: + manager = self._get_manager(v_type) + spots = manager.get_spots_by_vehicle_color(color) + if spots: + print(f'\n{v_type}s with color "{color}":') + for spot in spots: + self._print_vehicle_info(spot) def get_vehicle_by_registration_no(self, registration_no): - for vehicle_type in ['TwoWheeler', 'FourWheeler', 'HUV']: - spot = self.parking_spot_manager_factory.get_spot_manager(vehicle_type).get_spot_by_vehicle_registration_no(registration_no) + for v_type in self.supported_vehicle_types: + spot = self._get_manager(v_type).get_spot_by_vehicle_registration_no(registration_no) if spot: - print(f'Registration Number: {spot.vehicle.get_vehicle_registration_no()}') - print(f'Assigned Spot Number: {spot.spot_number}') - print(f'Color: {spot.vehicle.get_vehicle_color()}') - return + print(f'\nVehicle found in {v_type}:') + self._print_vehicle_info(spot) + return print(f'No vehicle found with registration number: {registration_no}') def list_all_parked_vehicles(self): - for vehicle_type in ['TwoWheeler', 'FourWheeler', 'HUV']: - spots = self.parking_spot_manager_factory.get_spot_manager(vehicle_type).list_all_parked_spots() + print("\nList of all parked vehicles:") + for v_type in self.supported_vehicle_types: + spots = self._get_manager(v_type).list_all_parked_spots() for spot in spots: - print(f'Registration Number: {spot.vehicle.get_vehicle_registration_no()}') - print(f'Assigned Spot Number: {spot.spot_number}') - print(f'Vehicle Type: {spot.vehicle.get_vehicle_type()}') - print(f'Color: {spot.vehicle.get_vehicle_color()}') - print('') + self._print_vehicle_info(spot) + + def get_report_data(self): + report = {} + + for vehicle_type in self.supported_vehicle_types: + manager = self.parking_spot_manager_factory.get_spot_manager(vehicle_type) + parked = manager.list_all_parked_spots() + total = manager.total_spots + available = manager.available_spots + report[vehicle_type] = { + 'total_spots': total, + 'available_spots': available, + 'occupied_spots': total - available, + 'vehicles': [ + { + 'registration_no': spot.vehicle.get_vehicle_registration_no(), + 'color': spot.vehicle.get_vehicle_color(), + 'spot_number': spot.spot_number + } for spot in parked + ] + } + + return report + + # --- Helper methods --- + def _get_manager(self, vehicle_type): + if vehicle_type not in self.supported_vehicle_types: + raise ValueError(f"Unsupported vehicle type: {vehicle_type}") + return self.parking_spot_manager_factory.get_spot_manager(vehicle_type) + + @staticmethod + def _print_vehicle_info(spot): + print(f' Registration No: {spot.vehicle.get_vehicle_registration_no()}') + print(f' Spot Number : {spot.spot_number}') + print(f' Vehicle Type : {spot.vehicle.get_vehicle_type()}') + print(f' Color : {spot.vehicle.get_vehicle_color()}') diff --git a/Design_Parking_Lot/src/model/exit_gate.py b/Design_Parking_Lot/src/model/exit_gate.py index 87e9af5..5e6f97c 100644 --- a/Design_Parking_Lot/src/model/exit_gate.py +++ b/Design_Parking_Lot/src/model/exit_gate.py @@ -1,5 +1,4 @@ -import datetime -import math +import datetime import random from model.ticket import Ticket @@ -10,40 +9,35 @@ def __init__(self, parking_spot_manager_factory: ParkingSpotManagerFactory): self.parking_spot_manager_factory = parking_spot_manager_factory def free_parking_spot(self, ticket: Ticket): - spot_number = ticket.spot_number + spot_number = ticket.parking_spot.spot_number vehicle_type = ticket.vehicle.get_vehicle_type() parking_spot_manager = self.parking_spot_manager_factory.get_spot_manager(vehicle_type) parking_spot_manager.free_parking_spot(spot_number) - def cost_computation(self, ticket: Ticket): - entry_time = ticket.entry_time - exit_time = datetime.datetime.now().timestamp() - """ - When the program is executed, the entry_time and exit_time are almost same, - which is not a realistic scenario. To better understand the program, - a random number of seconds in the range [1000, 10000] is added to the - parking_time calculation to account for this issue. - """ + @staticmethod + def _calculate_parking_duration(entry_time: float, exit_time: float = None): + if exit_time is None: + exit_time = datetime.datetime.now().timestamp() + duration_secs = (exit_time - entry_time) + random.randint(1000, 10000) # Simulated duration + hours = int(duration_secs) // 3600 + minutes = (int(duration_secs) % 3600) // 60 + return hours, minutes - parking_time_secs = (exit_time - entry_time) + random.randint(1000, 10000) # In seconds + def compute_cost(self, ticket: Ticket): + hours, minutes = self._calculate_parking_duration(ticket.entry_time) + cost = (hours * ticket.parking_spot.hour_charge) + (minutes * ticket.parking_spot.min_charge) - def seconds_to_hours_minutes(seconds): - hours = seconds // 3600 - minutes = (seconds % 3600) // 60 - return hours, minutes - - parking_time_hrs, parking_time_mins = seconds_to_hours_minutes(parking_time_secs) - parking_cost = parking_time_hrs * ticket.parking_spot.hour_charge + \ - parking_time_mins * ticket.parking_spot.min_charge - - print(f'The vehicle has been parked for {parking_time_hrs} hours and {parking_time_mins} minutes, incurring a cost of {parking_cost}.') - - return parking_cost + print( + f"[Cost] Parking duration: {hours}h {minutes}m | " + f"Rate: ₹{ticket.parking_spot.hour_charge}/hr, ₹{ticket.parking_spot.min_charge}/min | " + f"Total: ₹{cost}" + ) + return cost def payment(self, ticket: Ticket): - parking_cost = self.cost_computation(ticket) + parking_cost = self.compute_cost(ticket) print(f'The parking fee of {parking_cost} was paid in cash.') - # You payment logic can be written here + # Real payment gateway integration can be added here (QR, Card, UPI, etc.) \ No newline at end of file diff --git a/Design_Parking_Lot/src/model/parking_spot.py b/Design_Parking_Lot/src/model/parking_spot.py index 99294ee..b55524a 100644 --- a/Design_Parking_Lot/src/model/parking_spot.py +++ b/Design_Parking_Lot/src/model/parking_spot.py @@ -1,8 +1,15 @@ -from abc import ABC, abstractmethod +from abc import ABC from model.vehicle import Vehicle class ParkingSpot(ABC): + def __init__(self, spot_number, hour_charge, min_charge, is_empty=True): + self.spot_number = spot_number + self.vehicle = None + self.hour_charge = hour_charge + self.min_charge = min_charge + self.is_empty = is_empty + def park_vehicle(self, vehicle: Vehicle): self.is_empty = False self.vehicle = vehicle @@ -11,30 +18,20 @@ def park_vehicle(self, vehicle: Vehicle): def remove_vehicle(self): self.is_empty = True self.vehicle = Vehicle() - print('Parking spot {self.spot_number} is now available!') + print(f'Parking spot {self.spot_number} is now available!') return self.spot_number class TwoWheelerParkingSpot(ParkingSpot): - def __init__(self, spot_number, hour_charge=100, min_charge=1, is_empty=True): - self.spot_number = spot_number - self.vehicle = None - self.hour_charge = hour_charge - self.min_charge = min_charge - self.is_empty = is_empty + def __init__(self, spot_number): + super().__init__(spot_number, hour_charge=100, min_charge=1) + class FourWheelerParkingSpot(ParkingSpot): - def __init__(self, spot_number, hour_charge=200, min_charge=2, is_empty=True): - self.spot_number = spot_number - self.vehicle = None - self.hour_charge = hour_charge - self.min_charge = min_charge - self.is_empty = is_empty + def __init__(self, spot_number): + super().__init__(spot_number, hour_charge=200, min_charge=2) + class HUVParkingSpot(ParkingSpot): - def __init__(self, spot_number, hour_charge=300, min_charge=3, is_empty=True): - self.spot_number = spot_number - self.vehicle = None - self.hour_charge = hour_charge - self.min_charge = min_charge - self.is_empty = is_empty \ No newline at end of file + def __init__(self, spot_number): + super().__init__(spot_number, hour_charge=300, min_charge=3) diff --git a/Design_Parking_Lot/src/model/ticket.py b/Design_Parking_Lot/src/model/ticket.py index 4702a18..3596060 100644 --- a/Design_Parking_Lot/src/model/ticket.py +++ b/Design_Parking_Lot/src/model/ticket.py @@ -12,4 +12,4 @@ def generate_ticket(self): print(f'Entry time: {self.entry_time}') print(f'Spot No.: {self.parking_spot.spot_number}') print(f'Vehicle type: {self.vehicle.registration_no}') - print(f'Vehicle color: {self.vehicle.color}') \ No newline at end of file + print(f'Vehicle color: {self.vehicle.color}') diff --git a/Design_Parking_Lot/src/model/vehicle.py b/Design_Parking_Lot/src/model/vehicle.py index d14f147..1493d88 100644 --- a/Design_Parking_Lot/src/model/vehicle.py +++ b/Design_Parking_Lot/src/model/vehicle.py @@ -1,13 +1,21 @@ import abc class Vehicle(abc.ABC): - def create_vehicle(self, color, registration_no, vehicle_type): + def __init__(self, color=None, registration_no=None, vehicle_type=None): + self.type = vehicle_type + self.color = color + self.registration_no = registration_no + + @staticmethod + def create_vehicle(color, registration_no, vehicle_type): if vehicle_type == "TwoWheeler": return Bike(color, registration_no) - if vehicle_type == "FourWheeler": + elif vehicle_type == "FourWheeler": return Car(color, registration_no) - if vehicle_type == "HUV": + elif vehicle_type == "HUV": return Bus(color, registration_no) + else: + raise ValueError(f"Unknown vehicle type: {vehicle_type}") def get_vehicle_type(self): return self.type @@ -21,18 +29,12 @@ def get_vehicle_registration_no(self): class Bike(Vehicle): def __init__(self, color=None, registration_no=None): - self.type = "TwoWheeler" - self.color = color - self.registration_no = registration_no + super().__init__(color, registration_no, "TwoWheeler") class Car(Vehicle): def __init__(self, color=None, registration_no=None): - self.type = "FourWheeler" - self.color = color - self.registration_no = registration_no + super().__init__(color, registration_no, "FourWheeler") class Bus(Vehicle): def __init__(self, color=None, registration_no=None): - self.type = "HUV" - self.color = color - self.registration_no = registration_no \ No newline at end of file + super().__init__(color, registration_no, "HUV")