SatisfactoryPlusCalculator/factorygame/data/models.py

135 lines
4.8 KiB
Python
Raw Normal View History

2024-01-29 17:37:30 +00:00
import datetime
import re
2024-01-29 17:37:30 +00:00
from typing import Optional
from sqlalchemy import String, Select, select, Table, Column, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
2024-02-02 21:35:34 +00:00
amount_per_step_regex = re.compile(r"^\d*(\.\d+)?")
time_per_step_regex = re.compile(r"^(\d+(\.\d+)?) seconds?")
2024-02-02 16:44:14 +00:00
2024-01-29 17:37:30 +00:00
class Base(DeclarativeBase):
pass
2024-02-02 16:44:14 +00:00
ingredients_table = Table(
"recipe_ingredients",
Base.metadata,
Column("recipe_id", ForeignKey("recipes.id"), primary_key=True),
Column("resource_flow_id", ForeignKey("resource_flows.id"), primary_key=True),
)
results_table = Table(
"recipe_results",
Base.metadata,
Column("recipe_id", ForeignKey("recipes.id"), primary_key=True),
Column("resource_flow_id", ForeignKey("resource_flows.id"), primary_key=True),
)
recipe_factories_table = Table(
"recipe_factories",
Base.metadata,
Column("recipe_id", ForeignKey("recipes.id"), primary_key=True),
Column("factory_id", ForeignKey("factories.id"), primary_key=True),
)
2024-01-29 17:37:30 +00:00
class Resource(Base):
__tablename__ = "resources"
id: Mapped[int] = mapped_column(primary_key=True)
label: Mapped[str] = mapped_column(String(127))
2024-02-02 16:44:14 +00:00
uri: Mapped[str]
2024-01-29 17:37:30 +00:00
flows: Mapped[list["ResourceFlow"]] = relationship(back_populates="resource")
2024-02-02 16:44:14 +00:00
recipes_populated_at: Mapped[Optional[datetime.datetime]]
2024-01-29 17:37:30 +00:00
@classmethod
def by_label(cls, search: str) -> Select[tuple["Resource"]]:
return select(cls).where(cls.label.ilike(search))
def __repr__(self):
return (
f"Resource(id={self.id}, "
f"label={self.label}, "
2024-02-02 16:44:14 +00:00
f"uri={self.uri}, "
2024-01-29 17:37:30 +00:00
f"recipes_populated_at={self.recipes_populated_at})"
)
class Factory(Base):
__tablename__ = "factories"
id: Mapped[int] = mapped_column(primary_key=True)
label: Mapped[str] = mapped_column(String(127))
2024-02-02 16:44:14 +00:00
uri: Mapped[str]
used_in: Mapped[list["Recipe"]] = relationship(secondary=recipe_factories_table, viewonly=True)
2024-01-29 17:37:30 +00:00
@classmethod
def by_label(cls, search: str) -> Select[tuple["Factory"]]:
return select(cls).where(cls.label.ilike(search))
def __repr__(self):
2024-02-02 16:44:14 +00:00
return f"Factory(id={self.id}, label={self.label}, uri={self.uri})"
2024-01-29 17:37:30 +00:00
class ResourceFlow(Base):
__tablename__ = "resource_flows"
id: Mapped[int] = mapped_column(primary_key=True)
ingredient_in: Mapped[Optional["Recipe"]] = relationship(secondary=ingredients_table, back_populates="ingredients")
result_of: Mapped[Optional["Recipe"]] = relationship(secondary=results_table, back_populates="results")
resource_id: Mapped[int] = mapped_column(ForeignKey("resources.id"))
resource: Mapped["Resource"] = relationship(back_populates="flows")
amount: Mapped[str]
time: Mapped[str]
def amount_per_minute(self) -> float:
2024-02-02 21:35:34 +00:00
amount_per_step_match = amount_per_step_regex.match(self.amount)
assert amount_per_step_match, (
f"Amount per crafting step of resource {self.resource.label} in crafting recipe "
f"{self.result_of or self.ingredient_in} could not be parsed"
)
amount_per_step = float(amount_per_step_match.group(0))
time_per_step_match = time_per_step_regex.match(self.time)
assert time_per_step_match, (
f"Time per crafting step of resource {self.resource.label} in crafting recipe "
f"{self.result_of or self.ingredient_in} could not be parsed"
)
time_per_step = float(time_per_step_match.group(1))
return amount_per_step * (60.0 / time_per_step)
def describe(self) -> str:
return f"{repr(str(self.resource.label))}x{self.amount}"
2024-01-29 17:37:30 +00:00
def __repr__(self):
return f"ResourceFlow(id={self.id}, resource_id={self.resource_id}, amount={self.amount}, time={self.time})"
class Recipe(Base):
__tablename__ = "recipes"
id: Mapped[int] = mapped_column(primary_key=True)
2024-02-02 16:44:14 +00:00
factories: Mapped[list["Factory"]] = relationship(secondary=recipe_factories_table, back_populates="used_in")
2024-01-29 17:37:30 +00:00
ingredients: Mapped[list["ResourceFlow"]] = relationship(
secondary=ingredients_table, back_populates="ingredient_in"
)
results: Mapped[list["ResourceFlow"]] = relationship(secondary=results_table, back_populates="result_of")
2024-02-02 16:44:14 +00:00
def join_factories(self):
return ", ".join(map(lambda factory: factory.label, self.factories))
def describe(self) -> str:
def list_flows(flows: list["ResourceFlow"]) -> str:
return ", ".join(map(ResourceFlow.describe, flows))
return (
2024-02-02 16:44:14 +00:00
f"in machine(s): {self.join_factories()}, "
f"ingredient(s): {list_flows(self.ingredients)}, "
f"result(s): {list_flows(self.results)}"
)
2024-01-29 17:37:30 +00:00
def __repr__(self):
2024-02-02 21:35:34 +00:00
return (
f"Recipe(id={self.id}, factories={self.factories}, ingredients={self.ingredients}, results={self.results})"
)