Загрузка данных


from datetime import datetime
from typing import List, Optional

class Dimensions:
    def __init__(self, length: float, width: float, height: float):
        self.length = length
        self.width = width
        self.height = height

class IStorageUnit:
    def store(self, parcel) -> bool: pass
    def remove(self, tracking_number: str): pass
    def can_store(self, parcel) -> bool: pass

class Parcel:
    def __init__(self, tracking_number: str, weight: float, dimensions: Dimensions):
        self.tracking_number = tracking_number
        self.weight = weight
        self.dimensions = dimensions

class Human:
    def __init__(self, name: str, passport: str):
        self.name = name
        self.passport = passport

class Employee(Human):
    def __init__(self, name: str, passport: str, stress_level: int = 50):
        super().__init__(name, passport)
        self.stress_level = stress_level

class FrozenParcel(Parcel):
    def __init__(self, tracking_number: str, weight: float, dimensions: Dimensions, temperature: float = -18):
        super().__init__(tracking_number, weight, dimensions)
        self.temperature = temperature

class FragileParcel(Parcel):
    pass

class Shelf(IStorageUnit):
    def __init__(self, capacity: float, shelf_id: int):
        self.capacity = capacity
        self.shelf_id = shelf_id
        self.current_load = 0.0
        self.parcels = []
    
    def can_store(self, parcel: Parcel) -> bool:
        if isinstance(parcel, FrozenParcel):
            return False
        return self.current_load + parcel.weight <= self.capacity
    
    def store(self, parcel: Parcel) -> bool:
        if self.can_store(parcel):
            self.parcels.append(parcel)
            self.current_load += parcel.weight
            return True
        return False
    
    def remove(self, tracking_number: str):
        for i, p in enumerate(self.parcels):
            if p.tracking_number == tracking_number:
                self.current_load -= p.weight
                return self.parcels.pop(i)
        return None

class Fridge(IStorageUnit):
    def __init__(self, capacity: float, fridge_id: int, temperature: float = -18):
        self.capacity = capacity
        self.fridge_id = fridge_id
        self.current_load = 0.0
        self.parcels = []
    
    def can_store(self, parcel: Parcel) -> bool:
        if not isinstance(parcel, FrozenParcel):
            return False
        return self.current_load + parcel.weight <= self.capacity
    
    def store(self, parcel: Parcel) -> bool:
        if self.can_store(parcel):
            self.parcels.append(parcel)
            self.current_load += parcel.weight
            return True
        return False
    
    def remove(self, tracking_number: str):
        for i, p in enumerate(self.parcels):
            if p.tracking_number == tracking_number:
                self.current_load -= p.weight
                return self.parcels.pop(i)
        return None

class FloorCorner(IStorageUnit):
    def __init__(self, max_weight: float, corner_id: int):
        self.max_weight = max_weight
        self.current_weight = 0.0
        self.parcels = []
        self.corner_id = corner_id
    
    def can_store(self, parcel: Parcel) -> bool:
        return self.current_weight + parcel.weight <= self.max_weight
    
    def store(self, parcel: Parcel) -> bool:
        if self.can_store(parcel):
            self.parcels.append(parcel)
            self.current_weight += parcel.weight
            return True
        return False
    
    def remove(self, tracking_number: str):
        for i, p in enumerate(self.parcels):
            if p.tracking_number == tracking_number:
                self.current_weight -= p.weight
                return self.parcels.pop(i)
        return None

class ActionLogger:
    def __init__(self):
        self.logs = []
    
    def log(self, actor: str, action: str, details: str = ""):
        timestamp = datetime.now().strftime("[%H:%M:%S]")
        entry = f"{timestamp} {actor}: {action}"
        if details:
            entry += f" ({details})"
        self.logs.append(entry)
    
    def get_logs(self):
        return self.logs

class PickupPoint:
    def __init__(self, address: str, employee: Employee):
        self.address = address
        self.employee = employee
        self.shelves = []
        self.fridges = []
        self.floor_corners = []
        self.logger = ActionLogger()
        self.logger.log(employee.name, "открыл ПВЗ", f"по адресу {address}")
    
    def add_shelf(self, capacity: float) -> int:
        shelf_id = len(self.shelves) + 1
        self.shelves.append(Shelf(capacity, shelf_id))
        self.logger.log(self.employee.name, "добавил полку", f"вместимость {capacity} кг")
        return shelf_id
    
    def add_fridge(self, capacity: float, temperature: float = -18) -> int:
        fridge_id = len(self.fridges) + 1
        self.fridges.append(Fridge(capacity, fridge_id, temperature))
        return fridge_id
    
    def add_floor_corner(self, max_weight: float) -> int:
        corner_id = len(self.floor_corners) + 1
        self.floor_corners.append(FloorCorner(max_weight, corner_id))
        return corner_id
    
    def receive_parcel(self, courier: Human, parcel: Parcel) -> bool:
        self.logger.log(courier.name, "принял посылку", parcel.tracking_number)
        
        storage = None
        storage_type = ""
        storage_id = None
        
        if isinstance(parcel, FrozenParcel):
            for f in self.fridges:
                if f.can_store(parcel):
                    storage, storage_type, storage_id = f, "холодильник", f.fridge_id
                    break
        elif isinstance(parcel, FragileParcel):
            for s in self.shelves:
                if s.can_store(parcel) and len(s.parcels) == 0:
                    storage, storage_type, storage_id = s, "полку", s.shelf_id
                    break
            if not storage:
                for c in self.floor_corners:
                    if c.can_store(parcel):
                        storage, storage_type, storage_id = c, "угол", c.corner_id
                        break
        else:
            for s in self.shelves:
                if s.can_store(parcel):
                    storage, storage_type, storage_id = s, "полку", s.shelf_id
                    break
            if not storage:
                for c in self.floor_corners:
                    if c.can_store(parcel):
                        storage, storage_type, storage_id = c, "угол", c.corner_id
                        break
        
        if not storage:
            self.logger.log(self.employee.name, "НЕ удалось разместить посылку", f"{parcel.tracking_number} — нет подходящего места")
            return False
        
        try:
            if storage.store(parcel):
                self.logger.log(self.employee.name, "разместил посылку", f"{parcel.tracking_number} на {storage_type} {storage_id}")
                return True
            else:
                self.logger.log(self.employee.name, "не подошла полка", f"{parcel.tracking_number} — полка отказала")
                return False
        except Exception as e:
            self.logger.log(self.employee.name, "ошибка при размещении", f"{parcel.tracking_number} — {str(e)}")
            return False
    
    def serve_client(self, client: Human, tracking_number: str):
        self.logger.log(self.employee.name, "запрос на выдачу", tracking_number)
        
        for s in self.shelves:
            p = s.remove(tracking_number)
            if p:
                self.logger.log(self.employee.name, "снял посылку", f"{tracking_number} с полки {s.shelf_id}")
                self.logger.log(self.employee.name, "выдал посылку", f"{tracking_number} клиенту {client.name}")
                return p
        
        for f in self.fridges:
            p = f.remove(tracking_number)
            if p:
                self.logger.log(self.employee.name, "снял посылку", f"{tracking_number} с холодильника {f.fridge_id}")
                self.logger.log(self.employee.name, "выдал посылку", f"{tracking_number} клиенту {client.name}")
                return p
        
        for c in self.floor_corners:
            p = c.remove(tracking_number)
            if p:
                self.logger.log(self.employee.name, "снял посылку", f"{tracking_number} с угла {c.corner_id}")
                self.logger.log(self.employee.name, "выдал посылку", f"{tracking_number} клиенту {client.name}")
                return p
        
        self.logger.log(self.employee.name, "посылка не найдена", tracking_number)
        return None
    
    def get_logs(self):
        return self.logger.get_logs()