Skip to content

Commit

Permalink
feat: adds higher level objects for operations
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgilman committed May 25, 2024
1 parent 3c088fe commit 58ee456
Show file tree
Hide file tree
Showing 10 changed files with 452 additions and 30 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,41 @@ Bump our export volume and then buy some more cloth off the Aderhampton market:
1476.096
```

#### Operations

Get the operations for all of our weaveries:

```python
>>> from pymerc.api.models.common import BuildingType
>>> player.operations.by_building_type(BuildingType.Weavery)
OperationsList([<pymerc.game.operation.Operation at 0x7ffbb00de6f0>])
```

We have a single operation going on associated with a weavery.
Check how much it is currently outputting:

```python
>>> player.operations.by_building_type(BuildingType.Weavery).outputs
{<Item.Cloth: 'cloth'>: 400.0}
```

Check all of our operations that are taking cloth as an input:

```python
>>> player.operations.by_item_input(Item.Cloth)
OperationsList([<pymerc.game.operation.Operation at 0x7ffbb00debd0>,
<pymerc.game.operation.Operation at 0x7ffbb00dede0>,
<pymerc.game.operation.Operation at 0x7ffbb00dee70>,
<pymerc.game.operation.Operation at 0x7ffbb00dcf80>])
```

Check how much cloth all of these operations are consuming in total:

```python
>>> player.operations.by_item_input(Item.Cloth).inputs[Item.Cloth]
197.0
```

#### Data Analysis

Compare our total and actual imports:
Expand Down
5 changes: 4 additions & 1 deletion pymerc/api/buildings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ async def get_operations(self, id: int) -> buildings.BuildingOperation:
BuildingOperation: The building operation information.
"""
response = await self.client.get(f"{BASE_URL}{id}/operations")
return buildings.BuildingOperation.model_validate(response.json())
if response.status_code == 404:
return buildings.BuildingOperation()
else:
return buildings.BuildingOperation.model_validate(response.json())

async def set_manager(
self, id: int, item: common.Item, manager: common.InventoryManager
Expand Down
5 changes: 5 additions & 0 deletions pymerc/api/models/towns.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ class TownCommoners(BaseModel):
migration: float
sustenance: list[TownDemandCategory]

@property
def demands(self) -> list[TownDemand]:
"""The demands of the commoners."""
return [demand for category in self.sustenance for demand in category.products]


class TownDemandCategory(BaseModel):
"""Represents a category of demands in a town."""
Expand Down
57 changes: 55 additions & 2 deletions pymerc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
from pymerc.api.static import StaticAPI
from pymerc.api.towns import TownsAPI
from pymerc.api.transports import TransportsAPI
from pymerc.api.models import common
from pymerc.exceptions import TurnInProgressException
from pymerc.game.building import Building
from pymerc.game.operation import BuildingOperation, Operation
from pymerc.game.player import Player
from pymerc.game.recipe import Recipe
from pymerc.game.storehouse import Storehouse
from pymerc.game.town import Town
from pymerc.game.transport import Transport
Expand Down Expand Up @@ -104,20 +107,59 @@ async def put(self, url: str, json: any, **kwargs) -> httpx.Response:
"""
return await self.session.put(url, json=json, **kwargs)

async def building(self, id: int) -> Building:
async def building(self, player: Player, id: int) -> Building:
"""Get a building by its ID.
Args:
player (Player): The player.
id (int): The ID of the building.
Returns:
Building: The building with the given ID.
"""
b = Building(self, id)
b = Building(self, player, id)
await b.load()

return b

async def building_operation(
self, player: Player, building_id: int
) -> BuildingOperation:
"""Get the operations for a building.
Args:
player (Player): The player.
building_id (int): The ID of the building.
Returns:
BuildingOperations: The building operation information.
"""
o = BuildingOperation(self, player, building_id)
await o.load()

return o

async def operation(
self,
player: Player,
building_operation: BuildingOperation,
operation: common.Operation,
) -> Operation:
"""Create a new operation from a common.Operation object.
Args:
player (Player): The player.
building_operation (BuildingOperation): The building operation the operation is associated with.
operation (common.Operation): The operation.
Returns:
Operation: The operation.
"""
o = Operation(self, player, building_operation, operation)
await o.load()

return o

async def player(self) -> Player:
"""Get the current player.
Expand All @@ -129,6 +171,17 @@ async def player(self) -> Player:

return p

async def recipe(self, recipe: common.Recipe) -> Recipe:
"""Create a new recipe from a common.Recipe object.
Returns:
Recipe: The recipe.
"""
r = Recipe(self, recipe)
await r.load()

return r

async def storehouse(self, player: Player) -> Storehouse:
"""Get the player's storehouse.
Expand Down
59 changes: 50 additions & 9 deletions pymerc/game/building.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,42 @@
from pymerc.api.models import common
from pymerc.api.models.buildings import Building as BuildingModel
from pymerc.game.recipe import Recipe
from pymerc.game.operation import BuildingOperation, Operation, OperationsList

if TYPE_CHECKING:
from pymerc.client import Client
from pymerc.game.player import Player


class Building:
"""A higher level representation of a building in the game."""

data: BuildingModel
player: Player

def __init__(self, client: Client, id: int):
def __init__(self, client: Client, player: Player, id: int):
self._client = client
self.player = player
self.id = id

async def load(self):
"""Loads the data for the building."""
self.data = await self._client.buildings_api.get(self.id)
self.operations_data = await self._client.buildings_api.get_operations(self.id)

@property
def building_operation(self) -> Optional[BuildingOperation]:
"""Returns the building operation."""
return self.player.operations.get(self.id, None)

@property
def flows(self) -> Optional[dict[common.Item, common.InventoryFlow]]:
"""The flows of the building."""
return self.operations_data.total_flow
"""Returns the flows of the building."""
if self.building_operation.total_flow:
return self.building_operation.data.total_flow
elif self.operation:
return self.operation.data.flows
else:
return None

@property
def inventory(self) -> Optional[common.Inventory]:
Expand All @@ -45,16 +58,29 @@ def items(self) -> Optional[dict[common.Item, common.InventoryAccountAsset]]:
else:
return None

@property
def operation(self) -> Optional[Operation]:
"""Returns the operation of the building."""
if len(self.operations) == 1:
return self.operations[0]
else:
return None

@property
def operations(self) -> Optional[OperationsList]:
"""Returns the operations of the building."""
if self.id in self.player.operations:
if not self.player.operations[self.id].operations:
return self.player.storehouse.operations.by_building_id(self.id)

return self.player.operations[self.id].operations
return None

@property
def managers(self) -> dict[common.Item, common.InventoryManager]:
"""The managers of the building."""
return self.data.storage.inventory.managers

@property
def operations(self) -> Optional[list[common.Operation]]:
"""The operations of the building."""
return self.operations_data.operations

@property
def previous_flows(self) -> Optional[dict[common.Item, common.InventoryFlow]]:
"""The flows of the building."""
Expand Down Expand Up @@ -210,6 +236,21 @@ async def calculate_current_labor_need(self) -> float:
class BuildingsList(UserList):
"""A list of buildings."""

def by_id(self, id: int) -> Optional[Building]:
"""Get a building by its ID.
Args:
id (int): The ID of the building.
Returns:
Building: The building with the given ID if it exists, otherwise None.
"""
for building in self:
if building.id == id:
return building

return None

def by_type(self, type: common.BuildingType) -> BuildingsList:
"""Get all buildings of a certain type.
Expand Down
Loading

0 comments on commit 58ee456

Please sign in to comment.