diff --git a/README.md b/README.md
index dbcd01351cca650ec888ee561b73741453b83755..64347809c2e2a37f909bd2ae85a68c13588331fc 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,9 @@ airline-ticket-sales-service/ # Корневая директория прое
├── main.py # Основной файл программы, "точка входа"
├── models/
│ ├── flight.py # Класс для управления рейсами
-│ ├── __init__.py
+│ ├── __init__.py
+│ ├── flight_types.py
+│ ├── exceptions.py
├── pyproject.toml # Зависимости проекта
├── requirements.txt
├── .gitignore
@@ -83,6 +85,44 @@ airline-ticket-sales-service/ # Корневая директория прое
3. Установить время вылета
4. Установить время прибытия
5. Завершить заполнение
+
+**Вопрос:** Как работает класс InternationalFlight?
+
+Класс InternationalFlight используется для управления международными рейсами. Он позволяет:
+
+- устанавливать и получать информацию о необходимости визы (visa_required).
+
+- Получать информацию обо всех созданных международных рейсах через метод get_all_instances.
+
+**Вопрос:** Как работает класс CharterFlight?
+
+Класс CharterFlight предназначен для управления чартерными рейсами. Он позволяет:
+
+- устанавливать и получать информацию об операторе рейса (operator).
+
+- получать информацию обо всех созданных чартерных рейсах через метод get_all_instances.
+
+**Вопрос:** Как работает класс DomesticFlight?
+
+Класс DomesticFlight используется для управления внутренними рейсами. Он позволяет:
+
+- устанавливать и получать информацию о том, является ли рейс лоукостом (is_low_cost).
+
+- получать информацию обо всех созданных внутренних рейсах через метод get_all_instances.
+
+**Вопрос:** Как получить информацию обо всех рейсах?
+
+- Для каждого типа рейса (международные, чартерные, внутренние) используйте соответствующий статический метод:
+
+**InternationalFlight.get_all_instances()**
+
+**CharterFlight.get_all_instances()**
+
+**DomesticFlight.get_all_instances()**
+
+**Вопрос:** Что делать, если возникает ошибка при установке данных?
+
+- Если возникает ошибка (например, ValueError), проверьте вводимые данные.
## To do
- [ ] Реализовать удобный интерфейс
diff --git a/models/flight.py b/models/flight.py
index d1f46cfe5ac63a6220aecdbaf2b74457579ad154..2cd6574478d0b6d2e2bc3fca6e65b9830963136c 100644
--- a/models/flight.py
+++ b/models/flight.py
@@ -1,154 +1,180 @@
+from typing import TypeVar, Generic
from datetime import datetime
-class Flight:
- def __init__(self):
- """
- Инициализирует объект Flight с начальными значениями.
- """
- self._flight_number: str | None = None
- self._departure: str | None = None
- self._destination: str | None = None
- self._departure_time: str | None = None
- self._arrival_time: str | None = None
+class LoggingMixin:
+ """Миксин для логирования действий."""
- def get_flight_number(self) -> str | None:
+ def log_action(self, action: str) -> None:
"""
- Возвращает номер рейса.
+ Логирует действие с указанием класса, в котором оно произошло.
- Возвращает:
- str | None: Номер рейса, если он установлен, иначе None.
+ :param action: Действие, которое нужно залогировать.
"""
- return self._flight_number
+ print(f"[LOG] {action} - {self.__class__.__name__}")
- def set_flight_number(self, flight_number: str) -> None:
- """
- Устанавливает номер рейса.
+class IDMixin:
+ """Миксин для генерации уникальных идентификаторов."""
- Аргументы:
- flight_number (str): Номер рейса. Должен быть непустой строкой.
-
- Вызывает:
- ValueError: Если номер рейса не является непустой строкой.
- """
- if isinstance(flight_number, str) and flight_number.strip():
- self._flight_number = flight_number
- else:
- raise ValueError("Flight number must be a non-empty string")
+ _id_counter: int = 0
- def get_departure(self) -> str | None:
+ def generate_unique_id(self) -> int:
"""
- Возвращает пункт отправления.
+ Генерирует уникальный идентификатор.
- Возвращает:
- str | None: Пункт отправления, если он установлен, иначе None.
+ :return: Уникальный целочисленный идентификатор.
"""
- return self._departure
+ IDMixin._id_counter += 1
+ return IDMixin._id_counter
- def set_departure(self, departure: str) -> None:
- """
- Устанавливает пункт отправления.
+class FlightMeta(type):
+ """Метакласс для автоматической регистрации всех классов рейсов."""
- Аргументы:
- departure (str): Пункт отправления. Должен быть непустой строкой.
+ registered_flights: list = []
- Вызывает:
- ValueError: Если пункт отправления не является непустой строкой.
+ def __new__(cls, name: str, bases: tuple, dct: dict) -> type:
"""
- if isinstance(departure, str) and departure.strip():
- self._departure = departure
- else:
- raise ValueError("The departure point must be a non-empty string")
+ Создает новый класс и регистрирует его, если это не базовый класс Flight.
- def get_destination(self) -> str | None:
+ :param name: Имя класса.
+ :param bases: Кортеж базовых классов.
+ :param dct: Словарь атрибутов класса.
+ :return: Новый класс.
"""
- Возвращает пункт назначения.
+ new_class = super().__new__(cls, name, bases, dct)
+ if name != "Flight":
+ cls.registered_flights.append(new_class)
+ return new_class
- Возвращает:
- str | None: Пункт назначения, если он установлен, иначе None.
- """
- return self._destination
+FlightNumber = TypeVar("FlightNumber")
- def set_destination(self, destination: str) -> None:
- """
- Устанавливает пункт назначения.
+class Flight(LoggingMixin, IDMixin, Generic[FlightNumber], metaclass=FlightMeta):
+ """Базовый класс для представления рейса."""
- Аргументы:
- destination (str): Пункт назначения. Должен быть непустой строкой.
+ __slots__ = [
+ "_flight_number",
+ "_departure",
+ "_destination",
+ "_departure_time",
+ "_arrival_time",
+ "_available_seats",
+ "_flight_id",
+ ]
- Вызывает:
- ValueError: Если пункт назначения не является непустой строкой.
- """
- if isinstance(destination, str) and destination.strip():
- self._destination = destination
+ def __init__(self) -> None:
+ """Инициализирует объект рейса."""
+ super().__init__()
+ self._flight_number: FlightNumber | None = None
+ self._departure: str | None = None
+ self._destination: str | None = None
+ self._departure_time: str | None = None
+ self._arrival_time: str | None = None
+ self._available_seats: int = 0
+ self._flight_id: int = self.generate_unique_id()
+ self.log_action("Flight created")
+
+ @property
+ def flight_number(self) -> FlightNumber | None:
+ """Возвращает номер рейса."""
+ return self._flight_number
+
+ @flight_number.setter
+ def flight_number(self, value: FlightNumber) -> None:
+ """Устанавливает номер рейса."""
+ if isinstance(value, str) and value.strip():
+ self._flight_number = value
+ self.log_action(f"Flight number set to {value}")
else:
- raise ValueError("Destination must be a non-empty string")
+ raise ValueError("Flight number must be a non-empty string")
- def get_departure_time(self) -> str | None:
- """
- Возвращает время вылета.
+ @property
+ def departure(self) -> str | None:
+ """Возвращает пункт отправления."""
+ return self._departure
- Возвращает:
- str | None: Время вылета, если оно установлено, иначе None.
- """
- return self._departure_time
+ @departure.setter
+ def departure(self, value: str) -> None:
+ """Устанавливает пункт отправления."""
+ if isinstance(value, str) and value.strip():
+ self._departure = value
+ self.log_action(f"Departure set to {value}")
+ else:
+ raise ValueError("The departure point must be a non-empty string")
- def set_departure_time(self, departure_time: str) -> None:
- """
- Устанавливает время вылета.
+ @property
+ def destination(self) -> str | None:
+ """Возвращает пункт назначения."""
+ return self._destination
- Аргументы:
- departure_time (str): Время вылета. Должно быть строкой в формате ISO.
+ @destination.setter
+ def destination(self, value: str) -> None:
+ """Устанавливает пункт назначения."""
+ if isinstance(value, str) and value.strip():
+ self._destination = value
+ self.log_action(f"Destination set to {value}")
+ else:
+ raise ValueError("Destination must be a non-empty string")
- Вызывает:
- ValueError: Если время вылета некорректно или позже времени прибытия.
- """
- if self._is_valid_time(departure_time) and (
- self._arrival_time is None or self._arrival_time > departure_time
+ @property
+ def departure_time(self) -> str | None:
+ """Возвращает время отправления."""
+ return self._departure_time
+
+ @departure_time.setter
+ def departure_time(self, value: str) -> None:
+ """Устанавливает время отправления."""
+ if self._is_valid_time(value) and (
+ self._arrival_time is None or value < self._arrival_time
):
- self._departure_time = departure_time
+ self._departure_time = value
+ self.log_action(f"Departure time set to {value}")
else:
raise ValueError(
"Incorrect departure time or it is later than arrival time"
)
- def get_arrival_time(self) -> str | None:
- """
- Возвращает время прибытия.
-
- Возвращает:
- str | None: Время прибытия, если оно установлено, иначе None.
- """
+ @property
+ def arrival_time(self) -> str | None:
+ """Возвращает время прибытия."""
return self._arrival_time
- def set_arrival_time(self, arrival_time: str) -> None:
- """
- Устанавливает время прибытия.
-
- Аргументы:
- arrival_time (str): Время прибытия. Должно быть строкой в формате ISO.
-
- Вызывает:
- ValueError: Если время прибытия некорректно или раньше времени вылета.
- """
- if self._is_valid_time(arrival_time) and (
- self._departure_time is None or arrival_time > self._departure_time
+ @arrival_time.setter
+ def arrival_time(self, value: str) -> None:
+ """Устанавливает время прибытия."""
+ if self._is_valid_time(value) and (
+ self._departure_time is None or value > self._departure_time
):
- self._arrival_time = arrival_time
+ self._arrival_time = value
+ self.log_action(f"Arrival time set to {value}")
else:
raise ValueError(
"Incorrect arrival time or it is earlier than departure time"
)
+ @property
+ def available_seats(self) -> int:
+ """Возвращает количество свободных мест."""
+ return self._available_seats
+
+ @available_seats.setter
+ def available_seats(self, value: int) -> None:
+ """Устанавливает количество свободных мест."""
+ if value < 0:
+ raise ValueError("Available seats cannot be negative")
+ self._available_seats = value
+ self.log_action(f"Available seats set to {value}")
+
+ @property
+ def flight_id(self) -> int:
+ """Возвращает уникальный ID рейса."""
+ return self._flight_id
+
def _is_valid_time(self, time_str: str) -> bool:
"""
Проверяет, является ли строка времени корректной в формате ISO.
- Аргументы:
- time_str (str): Строка времени для проверки.
-
- Возвращает:
- bool: True, если строка времени корректна, иначе False.
+ :param time_str: Строка времени для проверки.
+ :return: True, если строка времени корректна, иначе False.
"""
try:
datetime.fromisoformat(time_str)
@@ -156,12 +182,11 @@ class Flight:
except ValueError:
return False
- def get_flight_info(self) -> dict[str, str | None]:
+ def get_flight_info(self) -> dict:
"""
Возвращает информацию о рейсе в виде словаря.
- Возвращает:
- dict[str, str | None]: Словарь, содержащий информацию о рейсе.
+ :return: Словарь с информацией о рейсе.
"""
return {
"flight_number": self._flight_number,
@@ -169,4 +194,23 @@ class Flight:
"destination": self._destination,
"departure_time": self._departure_time,
"arrival_time": self._arrival_time,
+ "available_seats": self._available_seats,
}
+
+
+class FlightAvailability:
+ """Класс для проверки доступности рейса."""
+
+ @staticmethod
+ def check_availability(flight: Flight) -> bool:
+ """
+ Проверяет, доступен ли рейс для бронирования.
+
+ :param flight: Рейс для проверки.
+ :return: True, если рейс доступен, иначе False.
+ """
+ if not flight.flight_number:
+ return False
+ if flight.available_seats == 0:
+ return False
+ return True
diff --git a/models/flight_types.py b/models/flight_types.py
index 0dece7299a60ba9d53c23f79907a2f5bf83de460..fccf71f316b65096524c81dc45545ec3879bf3ae 100644
--- a/models/flight_types.py
+++ b/models/flight_types.py
@@ -1,184 +1,94 @@
-from models.flight import Flight
+from models.flight import Flight, FlightNumber
-class InternationalFlight(Flight):
- __instances = []
+class InternationalFlight(Flight[FlightNumber]):
+ __slots__ = ["_visa_required"]
+ _instances = []
def __init__(self):
- """
- Инициализирует объект InternationalFlight.
- """
super().__init__()
- self.__visa_required: bool | None = None
- InternationalFlight.__instances.append(self)
-
- def get_visa_required(self) -> bool | None:
- """
- Возвращает информацию о необходимости визы.
-
- Returns:
- bool | None: True, если виза требуется, иначе False или None.
- """
- return self.__visa_required
-
- def set_visa_required(self, visa_required: bool) -> None:
- """
- Устанавливает необходимость визы.
-
- Args:
- visa_required (bool): True, если виза требуется, иначе False.
-
- Raises:
- ValueError: Если значение не является булевым.
- """
- if isinstance(visa_required, bool):
- self.__visa_required = visa_required
- else:
- raise ValueError("Visa required must be a boolean")
+ self._visa_required: bool | None = None
+ InternationalFlight._instances.append(self)
- def get_flight_info(self) -> dict[str, str | bool | None]:
- """
- Возвращает информацию о рейсе.
+ @property
+ def visa_required(self) -> bool | None:
+ """Возвращает, требуется ли виза для международного рейса."""
+ return self._visa_required
- Returns:
- dict[str, str | bool | None]: Словарь с информацией о рейсе.
- """
- base_info = super().get_flight_info()
- base_info["visa_required"] = self.__visa_required
- return base_info
+ @visa_required.setter
+ def visa_required(self, value: bool) -> None:
+ """Устанавливает, требуется ли виза для международного рейса."""
+ self._visa_required = value
+ self.log_action(f"Visa required set to {value}")
- @staticmethod
- def get_all_instances() -> list[tuple[dict[str, str | bool | None], str]]:
+ def get_flight_info(self) -> dict:
"""
- Возвращает список всех созданных объектов InternationalFlight с их информацией.
+ Возвращает информацию о международном рейсе.
- Returns:
- list[tuple[dict[str, str | bool | None], str]]:
- Список кортежей с информацией о рейсе и его типе.
+ :return: Словарь с информацией о рейсе, включая необходимость визы.
"""
- return [
- (flight.get_flight_info(), "International")
- for flight in InternationalFlight.__instances
- ]
+ info = super().get_flight_info()
+ info["visa_required"] = self._visa_required
+ return info
-
-class CharterFlight(Flight):
- __instances = []
+class CharterFlight(Flight[FlightNumber]):
+ __slots__ = ["_operator"]
+ _instances = []
def __init__(self):
- """
- Инициализирует объект CharterFlight.
- """
super().__init__()
- self.__operator: str | None = None
- CharterFlight.__instances.append(self)
+ self._operator: str | None = None
+ CharterFlight._instances.append(self)
- def get_operator(self) -> str | None:
- """
- Возвращает оператора чартерного рейса.
+ @property
+ def operator(self) -> str | None:
+ """Возвращает оператора чартерного рейса."""
+ return self._operator
- Returns:
- str | None: Название оператора или None.
- """
- return self.__operator
+ @operator.setter
+ def operator(self, value: str) -> None:
+ """Устанавливает оператора чартерного рейса."""
+ self._operator = value
+ self.log_action(f"Operator set to {value}")
- def set_operator(self, operator: str) -> None:
+ def get_flight_info(self) -> dict:
"""
- Устанавливает оператора чартерного рейса.
+ Возвращает информацию о чартерном рейсе.
- Args:
- operator (str): Название оператора.
-
- Raises:
- ValueError: Если название оператора не является непустой строкой.
+ :return: Словарь с информацией о рейсе, включая оператора.
"""
- if isinstance(operator, str) and operator.strip():
- self.__operator = operator
- else:
- raise ValueError("Operator must be a non-empty string")
+ info = super().get_flight_info()
+ info["operator"] = self._operator
+ return info
- def get_flight_info(self) -> dict[str, str | None]:
- """
- Возвращает информацию о рейсе.
- Returns:
- dict[str, str | None]: Словарь с информацией о рейсе.
- """
- base_info = super().get_flight_info()
- base_info["operator"] = self.__operator
- return base_info
-
- @staticmethod
- def get_all_instances() -> list[tuple[dict[str, str | None], str]]:
- """
- Возвращает список всех созданных объектов CharterFlight с их информацией.
-
- Returns:
- list[tuple[dict[str, str | None], str]]:
- Список кортежей с информацией о рейсе и его типе.
- """
- return [
- (flight.get_flight_info(), "Charter")
- for flight in CharterFlight.__instances
- ]
+class DomesticFlight(Flight[FlightNumber]):
+ __slots__ = ["_is_low_cost"]
+ _instances = []
-class DomesticFlight(Flight):
- __instances = []
-
def __init__(self):
- """
- Инициализирует объект DomesticFlight.
- """
super().__init__()
- self.__is_low_cost: bool | None = None
- DomesticFlight.__instances.append(self)
+ self._is_low_cost: bool | None = None
+ DomesticFlight._instances.append(self)
- def get_is_low_cost(self) -> bool | None:
- """
- Возвращает информацию о том, является ли рейс лоукост.
+ @property
+ def is_low_cost(self) -> bool | None:
+ """Возвращает, является ли рейс низкобюджетным."""
+ return self._is_low_cost
- Returns:
- bool | None: True, если рейс лоукост, иначе False или None.
- """
- return self.__is_low_cost
-
- def set_is_low_cost(self, is_low_cost: bool) -> None:
- """
- Устанавливает, является ли рейс лоукост.
-
- Args:
- is_low_cost (bool): True, если рейс лоукост, иначе False.
-
- Raises:
- ValueError: Если значение не является булевым.
- """
- if isinstance(is_low_cost, bool):
- self.__is_low_cost = is_low_cost
- else:
- raise ValueError("Is low cost must be a boolean")
-
- def get_flight_info(self) -> dict[str, str | bool | None]:
- """
- Возвращает информацию о рейсе.
-
- Returns:
- dict[str, str | bool | None]: Словарь с информацией о рейсе.
- """
- base_info = super().get_flight_info()
- base_info["is_low_cost"] = self.__is_low_cost
- return base_info
+ @is_low_cost.setter
+ def is_low_cost(self, value: bool) -> None:
+ """Устанавливает, является ли рейс низкобюджетным."""
+ self._is_low_cost = value
+ self.log_action(f"Is low cost set to {value}")
- @staticmethod
- def get_all_instances() -> list[tuple[dict[str, str | bool | None], str]]:
+ def get_flight_info(self) -> dict:
"""
- Возвращает список всех созданных объектов DomesticFlight с их информацией.
+ Возвращает информацию о внутреннем рейсе.
- Returns:
- list[tuple[dict[str, str | bool | None], str]]:
- Список кортежей с информацией о рейсе и его типе.
+ :return: Словарь с информацией о рейсе, включая флаг низкобюджетности.
"""
- return [
- (flight.get_flight_info(), "Domestic")
- for flight in DomesticFlight.__instances
- ]
+ info = super().get_flight_info()
+ info["is_low_cost"] = self._is_low_cost
+ return info