Загрузка данных
import json
import os
from dataclasses import dataclass, asdict
from typing import List, Optional, Dict, Any
DATA_FILE = "projects.json"
DENSITY_STEEL = 7850.0 # кг/м³
@dataclass
class Item:
id: str
name: str
dims_mm: tuple[float, float, float] # ДхШхВ в мм
qty: int
metal_thickness_mm: float
board_section_mm: tuple[float, float] # ширина, высота в мм
board_length_m: float
screws_per_item: int
bolts_per_item: int
eye_bolts_per_item: int
def volume_m3(self) -> float:
l, w, h = self.dims_mm
return (l * w * h) / 1_000_000_000.0
def surface_area_m2(self) -> float:
l, w, h = self.dims_mm
# сумма площадей всех граней
return 2 * ((l * w + l * h + w * h) / 1_000_000.0)
def metal_mass_kg(self) -> float:
area_m2 = self.surface_area_m2()
thickness_m = self.metal_thickness_mm / 1000.0
return area_m2 * thickness_m * DENSITY_STEEL
def board_volume_m3_per_item(self) -> float:
bw, bh = self.board_section_mm
section_m2 = (bw / 1000.0) * (bh / 1000.0)
return section_m2 * self.board_length_m
def total_values(self) -> Dict[str, Any]:
vol_m3 = self.volume_m3()
total_vol = vol_m3 * self.qty
area_m2 = self.surface_area_m2()
total_area = area_m2 * self.qty
metal_kg_per = self.metal_mass_kg()
total_metal = metal_kg_per * self.qty
board_vol_per = self.board_volume_m3_per_item()
total_board = board_vol_per * self.qty
return {
"volume_1_item_m3": round(vol_m3, 4),
"total_volume_m3": round(total_vol, 3),
"area_1_item_m2": round(area_m2, 2),
"total_area_m2": round(total_area, 1),
"metal_1_form_kg": round(metal_kg_per, 1),
"total_metal_kg": round(total_metal, 1),
"board_1_item_m3": round(board_vol_per, 3),
"total_board_m3": round(total_board, 3),
"screws_total": self.screws_per_item * self.qty,
"bolts_total": self.bolts_per_item * self.qty,
"eye_bolts_total": self.eye_bolts_per_item * self.qty,
}
@dataclass
class Order:
id: str
number: str
items: List[Item]
@dataclass
class Project:
name: str
orders: List[Order]
@classmethod
def load_all(cls) -> List["Project"]:
if not os.path.exists(DATA_FILE):
return []
try:
with open(DATA_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
projects = [
Project(
name=p["name"],
orders=[
Order(
id=o["id"],
number=o["number"],
items=[
Item(
id=i["id"],
name=i["name"],
dims_mm=tuple(i["dims_mm"]),
qty=i["qty"],
metal_thickness_mm=i["metal_thickness_mm"],
board_section_mm=tuple(i["board_section_mm"]),
board_length_m=i["board_length_m"],
screws_per_item=i["screws_per_item"],
bolts_per_item=i["bolts_per_item"],
eye_bolts_per_item=i["eye_bolts_per_item"],
)
for i in o["items"]
],
)
for o in p["orders"]
],
)
for p in data
]
return projects
except Exception:
print("Ошибка чтения файла данных. Начнём с пустого списка.")
return []
@staticmethod
def save_all(projects: List["Project"]) -> None:
data = [
{
"name": p.name,
"orders": [
{
"id": o.id,
"number": o.number,
"items": [asdict(i) for i in o.items],
}
for o in p.orders
],
}
for p in projects
]
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def generate_id(prefix: str) -> str:
import uuid
return f"{prefix}_{uuid.uuid4().hex[:6]}"
def input_int(prompt: str, min_val: Optional[int] = None) -> int:
while True:
try:
val = int(input(prompt))
if min_val is not None and val < min_val:
print(f"Значение должно быть не меньше {min_val}.")
continue
return val
except ValueError:
print("Пожалуйста, введите целое число.")
def input_float(prompt: str, min_val: Optional[float] = None) -> float:
while True:
try:
val = float(input(prompt))
if min_val is not None and val < min_val:
print(f"Значение должно быть не меньше {min_val}.")
continue
return val
except ValueError:
print("Пожалуйста, введите число.")
def parse_dims() -> tuple[float, float, float]:
while True:
s = input("Габариты (ДхШхВ, мм, через пробел, например: 1000 500 200): ").strip()
parts = s.replace(",", " ").split()
if len(parts) != 3:
print("Нужно ровно три числа.")
continue
try:
dims = tuple(float(x) for x in parts)
if any(d <= 0 for d in dims):
print("Все размеры должны быть больше 0.")
continue
return dims
except ValueError:
print("Введите корректные числа.")
def parse_board_section() -> tuple[float, float]:
while True:
s = input("Сечение доски (ширина×высота, мм, например: 150×50 или 150x50): ").strip().replace("×", "x")
parts = s.split("x")
if len(parts) != 2:
print("Формат: ширина×высота (например: 150×50).")
continue
try:
w, h = float(parts[0]), float(parts[1])
if w <= 0 or h <= 0:
print("Ширина и высота должны быть больше 0.")
continue
return w, h
except ValueError:
print("Введите корректные числа.")
def add_project(projects: List[Project]) -> None:
name = input("Название проекта: ").strip()
if not name:
print("Название не может быть пустым.")
return
projects.append(Project(name=name, orders=[]))
Project.save_all(projects)
print("Проект добавлен.")
def list_projects(projects: List[Project]) -> Optional[Project]:
if not projects:
print("Нет проектов.")
return None
print("\nПроекты:")
for i, p in enumerate(projects, start=1):
print(f"{i}. {p.name}")
while True:
try:
idx = int(input("Выберите номер проекта (или 0 для отмены): "))
if idx == 0:
return None
if 1 <= idx <= len(projects):
return projects[idx - 1]
print("Неверный номер.")
except ValueError:
print("Введите число.")
def add_order(project: Project) -> None:
number = input("Номер заказа (например: ORD-101): ").strip()
if not number:
print("Номер заказа не может быть пустым.")
return
if any(o.number == number for o in project.orders):
print("Такой заказ уже есть в проекте.")
return
project.orders.append(Order(id=generate_id("ord"), number=number, items=[]))
Project.save_all([p for p in all_projects if p.name != project.name] + [project])
print("Заказ добавлен.")
def add_item(project: Project, order: Order) -> None:
name = input("Наименование изделия: ").strip()
if not name:
print("Наименование не может быть пустым.")
return
dims = parse_dims()
qty = input_int("Количество изделий, шт: ", min_val=1)
metal_thick = input_float("Толщина металла формы, мм: ", min_val=0.1)
board_sec = parse_board_section()
board_len = input_float("Длина доски, м: ", min_val=0.5)
screws = input_int("Саморезы на 1 изделие, шт: ", min_val=0)
bolts = input_int("Болты+гайки на 1 изделие (парами), шт: ", min_val=0)
eye_bolts = input_int("Рым-болты на 1 изделие, шт: ", min_val=0)
item = Item(
id=generate_id("itm"),
name=name,
dims_mm=dims,
qty=qty,
metal_thickness_mm=metal_thick,
board_section_mm=board_sec,
board_length_m=board_len,
screws_per_item=screws,
bolts_per_item=bolts,
eye_bolts_per_item=eye_bolts,
)
order.items.append(item)
Project.save_all(all_projects)
print("Изделие добавлено.")
def show_item_details(project: Project, order: Order, item: Item) -> None:
vals = item.total_values()
print(f"\nДетали: {item.name} ({order.number}, проект {project.name})")
print(f"Объём 1 изделия: {vals['volume_1_item_m3']} м³")
print(f"Суммарный объём: {vals['total_volume_m3']} м³")
print(f"Площадь 1 изделия: {vals['area_1_item_m2']} м²")
print(f"Суммарная площадь: {vals['total_area_m2']} м²")
print(f"Металл на 1 форму: {vals['metal_1_form_kg']} кг")
print(f"Суммарно металл: {vals['total_metal_kg']} кг")
print(f"Доска на 1 изделие: {vals['board_1_item_m3']} м³")
print(f"Суммарно доски: {vals['total_board_m3']} м³")
print(f"Саморезы (суммарно): {vals['screws_total']} шт")
print(f"Болты+гайки (суммарно): {vals['bolts_total']} шт")
print(f"Рым-болты (суммарно): {vals['eye_bolts_total']} шт")
def select_order(project: Project) -> Optional[Order]:
if not project.orders:
print("В проекте нет заказов.")
return None
print("\nЗаказы:")
for i, o in enumerate(project.orders, start=1):
print(f"{i}. {o.number} (изделий: {len(o.items)})")
while True:
try:
idx = int(input("Выберите номер заказа (или 0 для отмены): "))
if idx == 0:
return None
if 1 <= idx <= len(project.orders):
return project.orders[idx - 1]
print("Неверный номер.")
except ValueError:
print("Введите число.")
def select_item(order: Order) -> Optional[Item]:
if not order.items:
print("В заказе нет изделий.")
return None
print("\nИзделия:")
for i, it in enumerate(order.items, start=1):
print(f"{i}. {it.name}")
while True:
try:
idx = int(input("Выберите номер изделия (или 0 для отмены): "))
if idx == 0:
return None
if 1 <= idx <= len(order.items):
return order.items[idx - 1]
print("Неверный номер.")
except ValueError:
print("Введите число.")
# --- Главный цикл ---
all_projects: List[Project] = Project.load_all()
while True:
print("\nМеню:")
print("1. Добавить проект")
print("2. Выбрать проект")
print("3. Показать все проекты")
print("0. Выход")
choice = input("Выберите действие: ").strip()
if choice == "1":
add_project(all_projects)
elif choice == "2":
project = list_projects(all_projects)
if project is None:
continue
while True:
print(f"\nПроект: {project.name}")
print("1. Добавить заказ")
print("2. Добавить изделие")
print("3. Посмотреть детали изделия")
print("4. Вернуться к выбору проекта")
sub = input("Действие: ").strip()
if sub == "1":
add_order(project)
elif sub == "2":
order = select_order(project)
if order:
add_item(project, order)
elif sub == "3":
order = select_order(project)
if not order:
continue
item = select_item(order)
if item:
show_item_details(project, order, item)
elif sub == "4":
break
else:
print("Неверная команда.")
elif choice == "3":
for i, p in enumerate(all_projects, start=1):
print(f"{i}. {p.name} (заказов: {len(p.orders)})")
elif choice == "0":
print("До свидания!")
break
else:
print("Неверная команда.")