Load materials from the Materials startup event
This commit is contained in:
parent
4a2d5d5cb7
commit
677f122a03
2 changed files with 120 additions and 7 deletions
|
@ -27,13 +27,15 @@ def _load() -> typing.Dict[str, Material]:
|
||||||
lines = path.read_text().splitlines()
|
lines = path.read_text().splitlines()
|
||||||
reader = csv.reader(lines[1:])
|
reader = csv.reader(lines[1:])
|
||||||
return {
|
return {
|
||||||
symbol: Material(name, type_, category, int(grade))
|
symbol.lower(): Material(name, type_, category, int(grade))
|
||||||
for fdevid, symbol, grade, type_, category, name in reader
|
for fdevid, symbol, grade, type_, category, name in reader
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
materials: typing.Dict[str, Material] = _load()
|
materials: typing.Dict[str, Material] = _load()
|
||||||
|
|
||||||
|
_Inventory = typing.TypeVar("_Inventory", bound="Inventory")
|
||||||
|
|
||||||
|
|
||||||
class Inventory(collections.UserDict):
|
class Inventory(collections.UserDict):
|
||||||
"""A mapping of every available material with an associated quantity."""
|
"""A mapping of every available material with an associated quantity."""
|
||||||
|
@ -47,11 +49,18 @@ class Inventory(collections.UserDict):
|
||||||
self.update(items)
|
self.update(items)
|
||||||
|
|
||||||
def __setitem__(self, key, value) -> None:
|
def __setitem__(self, key, value) -> None:
|
||||||
if key not in materials:
|
normalized_key = key.lower()
|
||||||
|
if normalized_key not in materials:
|
||||||
raise KeyError(key)
|
raise KeyError(key)
|
||||||
if value < 0:
|
if value < 0:
|
||||||
raise ValueError(value)
|
raise ValueError(value)
|
||||||
super().__setitem__(key, value)
|
super().__setitem__(normalized_key, value)
|
||||||
|
|
||||||
|
def __getitem__(self, key: str) -> int:
|
||||||
|
normalized_key = key.lower()
|
||||||
|
if normalized_key not in materials:
|
||||||
|
raise KeyError(key)
|
||||||
|
return super().__getitem__(normalized_key)
|
||||||
|
|
||||||
def has(self, items: typing.Union[typing.Dict[str, int], "Inventory"]) -> bool:
|
def has(self, items: typing.Union[typing.Dict[str, int], "Inventory"]) -> bool:
|
||||||
return all([self[name] >= quantity for name, quantity in items.items()])
|
return all([self[name] >= quantity for name, quantity in items.items()])
|
||||||
|
@ -62,6 +71,23 @@ class Inventory(collections.UserDict):
|
||||||
def __sub__(self, other: "Inventory") -> "Inventory":
|
def __sub__(self, other: "Inventory") -> "Inventory":
|
||||||
return Inventory({name: self[name] - qty for name, qty in other.items()})
|
return Inventory({name: self[name] - qty for name, qty in other.items()})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_event(
|
||||||
|
cls: typing.Type[_Inventory], event: typing.Dict[str, typing.Any]
|
||||||
|
) -> _Inventory:
|
||||||
|
if event.get("event") != "Materials":
|
||||||
|
raise ValueError("Not a 'Materials' event")
|
||||||
|
return cls(
|
||||||
|
{
|
||||||
|
material["Name"]: material["Count"]
|
||||||
|
for material in [
|
||||||
|
*event.get("Raw", []),
|
||||||
|
*event.get("Manufactured", []),
|
||||||
|
*event.get("Encoded", []),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Materials(Inventory):
|
class Materials(Inventory):
|
||||||
"""Same as Inventory, but omits zeroed materials."""
|
"""Same as Inventory, but omits zeroed materials."""
|
||||||
|
@ -75,18 +101,20 @@ class Materials(Inventory):
|
||||||
self.update(items)
|
self.update(items)
|
||||||
|
|
||||||
def __setitem__(self, key: str, value: int) -> None:
|
def __setitem__(self, key: str, value: int) -> None:
|
||||||
if key not in materials:
|
normalized_key = key.lower()
|
||||||
|
if normalized_key not in materials:
|
||||||
raise KeyError(key)
|
raise KeyError(key)
|
||||||
if value < 0:
|
if value < 0:
|
||||||
raise ValueError(value)
|
raise ValueError(value)
|
||||||
if value == 0:
|
if value == 0:
|
||||||
return
|
return
|
||||||
super().__setitem__(key, value)
|
super().__setitem__(normalized_key, value)
|
||||||
|
|
||||||
def __getitem__(self, key: str) -> int:
|
def __getitem__(self, key: str) -> int:
|
||||||
if key not in materials:
|
normalized_key = key.lower()
|
||||||
|
if normalized_key not in materials:
|
||||||
raise KeyError(key)
|
raise KeyError(key)
|
||||||
return self.data.get(key, 0)
|
return self.data.get(normalized_key.lower(), 0)
|
||||||
|
|
||||||
def __add__(self, other: "Inventory") -> "Materials":
|
def __add__(self, other: "Inventory") -> "Materials":
|
||||||
return Materials({name: self[name] + other[name] for name in materials})
|
return Materials({name: self[name] + other[name] for name in materials})
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import fractions
|
import fractions
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
import typing
|
import typing
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
@ -86,6 +88,9 @@ class InventoryTests(unittest.TestCase):
|
||||||
) -> "materials.Inventory":
|
) -> "materials.Inventory":
|
||||||
return materials.Inventory(items)
|
return materials.Inventory(items)
|
||||||
|
|
||||||
|
def from_event(self, event: typing.Dict[str, typing.Any]) -> "materials.Inventory":
|
||||||
|
return materials.Inventory.from_event(event)
|
||||||
|
|
||||||
def test_create_empty(self) -> None:
|
def test_create_empty(self) -> None:
|
||||||
inventory = self.make()
|
inventory = self.make()
|
||||||
self.assertEqual(set(materials.materials.keys()), set(inventory.keys()))
|
self.assertEqual(set(materials.materials.keys()), set(inventory.keys()))
|
||||||
|
@ -140,6 +145,83 @@ class InventoryTests(unittest.TestCase):
|
||||||
a - b
|
a - b
|
||||||
self.assertEqual(exc.exception.args, (-1,))
|
self.assertEqual(exc.exception.args, (-1,))
|
||||||
|
|
||||||
|
def test_load_from_materials_event(self) -> None:
|
||||||
|
event = json.loads(
|
||||||
|
pathlib.Path(__file__).parent.joinpath("events/materials.json").read_text()
|
||||||
|
)
|
||||||
|
event = {
|
||||||
|
"timestamp": "2022-01-23T02:58:12Z",
|
||||||
|
"event": "Materials",
|
||||||
|
"Raw": [
|
||||||
|
{"Name": "manganese", "Count": 127},
|
||||||
|
{"Name": "iron", "Count": 37},
|
||||||
|
{"Name": "phosphorus", "Count": 2},
|
||||||
|
{"Name": "nickel", "Count": 5},
|
||||||
|
{"Name": "lead", "Count": 69},
|
||||||
|
{"Name": "germanium", "Count": 12},
|
||||||
|
],
|
||||||
|
"Manufactured": [
|
||||||
|
{
|
||||||
|
"Name": "salvagedalloys",
|
||||||
|
"Name_Localised": "Salvaged Alloys",
|
||||||
|
"Count": 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "crystalshards",
|
||||||
|
"Name_Localised": "Crystal Shards",
|
||||||
|
"Count": 36,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "shieldemitters",
|
||||||
|
"Name_Localised": "Shield Emitters",
|
||||||
|
"Count": 36,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"Encoded": [
|
||||||
|
{
|
||||||
|
"Name": "bulkscandata",
|
||||||
|
"Name_Localised": "Anomalous Bulk Scan Data",
|
||||||
|
"Count": 28,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "shieldcyclerecordings",
|
||||||
|
"Name_Localised": "Distorted Shield Cycle Recordings",
|
||||||
|
"Count": 66,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "archivedemissiondata",
|
||||||
|
"Name_Localised": "Irregular Emission Data",
|
||||||
|
"Count": 6,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
self.make(
|
||||||
|
{
|
||||||
|
"manganese": 127,
|
||||||
|
"iron": 37,
|
||||||
|
"phosphorus": 2,
|
||||||
|
"nickel": 5,
|
||||||
|
"lead": 69,
|
||||||
|
"germanium": 12,
|
||||||
|
"salvagedalloys": 1,
|
||||||
|
"crystalshards": 36,
|
||||||
|
"shieldemitters": 36,
|
||||||
|
"bulkscandata": 28,
|
||||||
|
"shieldcyclerecordings": 66,
|
||||||
|
"archivedemissiondata": 6,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
self.from_event(event),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_load_from_invalid_event(self) -> None:
|
||||||
|
event: typing.Dict[str, typing.Any] = {}
|
||||||
|
with self.assertRaises(ValueError) as exc:
|
||||||
|
self.from_event(event)
|
||||||
|
self.assertEqual(exc.exception.args, ("Not a 'Materials' event",))
|
||||||
|
|
||||||
|
|
||||||
class MaterialsTests(InventoryTests):
|
class MaterialsTests(InventoryTests):
|
||||||
def make(
|
def make(
|
||||||
|
@ -147,6 +229,9 @@ class MaterialsTests(InventoryTests):
|
||||||
) -> "materials.Materials":
|
) -> "materials.Materials":
|
||||||
return materials.Materials(items)
|
return materials.Materials(items)
|
||||||
|
|
||||||
|
def from_event(self, event: typing.Dict[str, typing.Any]) -> "materials.Materials":
|
||||||
|
return materials.Materials.from_event(event)
|
||||||
|
|
||||||
def test_create_empty(self) -> None:
|
def test_create_empty(self) -> None:
|
||||||
empty = self.make()
|
empty = self.make()
|
||||||
self.assertEqual(set(), set(empty.keys()))
|
self.assertEqual(set(), set(empty.keys()))
|
||||||
|
|
Loading…
Reference in a new issue