async-universalis 4.0.0.dev0__tar.gz → 4.1.0.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {async_universalis-4.0.0.dev0/async_universalis.egg-info → async_universalis-4.1.0.dev0}/PKG-INFO +1 -1
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/__init__.py +86 -47
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0/async_universalis.egg-info}/PKG-INFO +1 -1
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/LICENSE +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/MANIFEST.in +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/README.md +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/_enums.py +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/_types.py +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/errors.py +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/items.json +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/py.typed +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis.egg-info/SOURCES.txt +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis.egg-info/dependency_links.txt +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis.egg-info/requires.txt +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis.egg-info/top_level.txt +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/pyproject.toml +0 -0
- {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/setup.cfg +0 -0
|
@@ -23,7 +23,7 @@ from __future__ import annotations
|
|
|
23
23
|
__title__ = "Universalis API wrapper"
|
|
24
24
|
__author__ = "k8thekat"
|
|
25
25
|
__license__ = "GNU"
|
|
26
|
-
__version__ = "4.
|
|
26
|
+
__version__ = "4.1.0-dev"
|
|
27
27
|
__credits__ = "Universalis and Square Enix"
|
|
28
28
|
|
|
29
29
|
|
|
@@ -241,14 +241,6 @@ class UniversalisAPI:
|
|
|
241
241
|
data = await session.get(url=url, **request_params)
|
|
242
242
|
|
|
243
243
|
LOGGER.debug("<%s._request> | Status Code: %s | Content Type: %s", __class__.__name__, data.status, data.content_type)
|
|
244
|
-
if not 200 <= data.status < 300:
|
|
245
|
-
raise UniversalisError(data.status, url, "generic http request")
|
|
246
|
-
if data.status == 400:
|
|
247
|
-
raise UniversalisError(
|
|
248
|
-
data.status,
|
|
249
|
-
url,
|
|
250
|
-
"invalid parameters",
|
|
251
|
-
)
|
|
252
244
|
# 404 - The world/DC or item requested is invalid. When requesting multiple items at once, an invalid item ID will not trigger this.
|
|
253
245
|
# Instead, the returned list of unresolved item IDs will contain the invalid item ID or IDs.
|
|
254
246
|
if data.status == 404:
|
|
@@ -257,7 +249,14 @@ class UniversalisAPI:
|
|
|
257
249
|
url,
|
|
258
250
|
"invalid World/DC or Item ID",
|
|
259
251
|
)
|
|
260
|
-
|
|
252
|
+
if data.status == 400:
|
|
253
|
+
raise UniversalisError(
|
|
254
|
+
data.status,
|
|
255
|
+
url,
|
|
256
|
+
"invalid parameters",
|
|
257
|
+
)
|
|
258
|
+
if not 200 <= data.status < 300:
|
|
259
|
+
raise UniversalisError(data.status, url, "generic http request")
|
|
261
260
|
self.api_call_time = datetime.datetime.now(datetime.UTC)
|
|
262
261
|
res: Any = await data.json()
|
|
263
262
|
return res
|
|
@@ -284,11 +283,11 @@ class UniversalisAPI:
|
|
|
284
283
|
|
|
285
284
|
|
|
286
285
|
.. note::
|
|
287
|
-
- If you specify a
|
|
286
|
+
- If you specify a :class:`World` when getting marketboard data..
|
|
288
287
|
- All `<CurrentData.listings>` and `<CurrentData.recent_history>` will not have the attributes `world_name`.
|
|
289
|
-
- If you specify a
|
|
288
|
+
- If you specify a :class:`DataCenter` when getting marketboard data...
|
|
290
289
|
- All `<CurrentData.listings>` and `<CurrentData.recent_history>` will have the `world_id` and `world_name` attributes.
|
|
291
|
-
-
|
|
290
|
+
- :class:`CurrentData` will also have an additional attribute called `dc_name`.
|
|
292
291
|
|
|
293
292
|
.. note::
|
|
294
293
|
You can change the default DataCenter by setting the `<UniversalisAPI>.datacenter` property.
|
|
@@ -359,11 +358,11 @@ class UniversalisAPI:
|
|
|
359
358
|
- See `https://docs.universalis.app/` and use their forms to generate a string with the fields you want.
|
|
360
359
|
|
|
361
360
|
.. note::
|
|
362
|
-
- If you specify a
|
|
361
|
+
- If you specify a :class:`World` when getting marketboard data..
|
|
363
362
|
- All `<CurrentData.listings>` and `<CurrentData.recent_history>` will not have the attributes `world_name`.
|
|
364
|
-
- If you specify a
|
|
363
|
+
- If you specify a :class:`DataCenter` when getting marketboard data...
|
|
365
364
|
- All `<CurrentData.listings>` and `<CurrentData.recent_history>` will have the `world_id` and `world_name` attributes.
|
|
366
|
-
-
|
|
365
|
+
- :class:`CurrentData` will also have an additional attribute called `dc_name`.
|
|
367
366
|
|
|
368
367
|
|
|
369
368
|
.. note::
|
|
@@ -441,11 +440,18 @@ class UniversalisAPI:
|
|
|
441
440
|
LOGGER.debug("<%s._get_bulk_current_data>. | URL: %s | Response:\n%s", __class__.__name__, api_url, res)
|
|
442
441
|
|
|
443
442
|
# results.extend([CurrentData(universalis=self, data=value) for value in res.get("items").values() if "listings" in value])
|
|
444
|
-
data
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
443
|
+
if data is None:
|
|
444
|
+
data = MultiPart(
|
|
445
|
+
universalis=self,
|
|
446
|
+
resolved_items=[
|
|
447
|
+
CurrentData(universalis=self, data=value) for value in res.get("items").values() if "listings" in value
|
|
448
|
+
],
|
|
449
|
+
**res,
|
|
450
|
+
)
|
|
451
|
+
else:
|
|
452
|
+
data.items.extend([CurrentData(universalis=self, data=value) for value in res.get("items").values() if "listings" in value])
|
|
453
|
+
data.unresolved_items.extend(res["unresolvedItems"])
|
|
454
|
+
|
|
449
455
|
return data
|
|
450
456
|
|
|
451
457
|
async def get_history_data(
|
|
@@ -470,9 +476,9 @@ class UniversalisAPI:
|
|
|
470
476
|
|
|
471
477
|
|
|
472
478
|
.. note::
|
|
473
|
-
- If you specify a
|
|
479
|
+
- If you specify a :class:`World` when getting marketboard data..
|
|
474
480
|
- All `<HistoryData.entries>` will not have the attributes `world_name`.
|
|
475
|
-
- If you specify a
|
|
481
|
+
- If you specify a :class:`DataCenter` when getting marketboard data...
|
|
476
482
|
- All `<HistoryData.entries>` will have the `world_id` and `world_name` attributes.
|
|
477
483
|
- `<HistoryData>` will also have an additional attribute called `dc_name`.
|
|
478
484
|
|
|
@@ -538,9 +544,9 @@ class UniversalisAPI:
|
|
|
538
544
|
|
|
539
545
|
|
|
540
546
|
.. note::
|
|
541
|
-
- If you specify a
|
|
547
|
+
- If you specify a :class:`World` when getting marketboard data..
|
|
542
548
|
- All `<HistoryData.entries>` will not have the attributes `world_name`.
|
|
543
|
-
- If you specify a
|
|
549
|
+
- If you specify a :class:`DataCenter` when getting marketboard data...
|
|
544
550
|
- All `<HistoryData.entries>` will have the `world_id` and `world_name` attributes.
|
|
545
551
|
- `<HistoryData>` will also have an additional attribute called `dc_name`.
|
|
546
552
|
|
|
@@ -592,7 +598,7 @@ class UniversalisAPI:
|
|
|
592
598
|
if world_or_dc is None:
|
|
593
599
|
world_or_dc = self.default_datacenter
|
|
594
600
|
|
|
595
|
-
# If we are given a single entry in our list; use the `
|
|
601
|
+
# If we are given a single entry in our list; use the `get_history_data` instead.
|
|
596
602
|
# We could modify the `join` statement below; but this is far easier and provides the same results.
|
|
597
603
|
# So if the `dcName` key exists, we searched by a DataCenter.
|
|
598
604
|
# otherwise the `worldName` and `worldID` key will exist.
|
|
@@ -623,11 +629,15 @@ class UniversalisAPI:
|
|
|
623
629
|
res,
|
|
624
630
|
)
|
|
625
631
|
# results.extend(HistoryData(universalis=self, data=value) for value in res.get("items").values() if "entries" in value)
|
|
626
|
-
data
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
632
|
+
if data is None:
|
|
633
|
+
data = MultiPart(
|
|
634
|
+
universalis=self,
|
|
635
|
+
resolved_items=[HistoryData(universalis=self, data=value) for value in res.get("items").values() if "entries" in value],
|
|
636
|
+
**res,
|
|
637
|
+
)
|
|
638
|
+
else:
|
|
639
|
+
data.items.extend([HistoryData(universalis=self, data=value) for value in res.get("items").values() if "entries" in value])
|
|
640
|
+
data.unresolved_items.extend(res["unresolvedItems"])
|
|
631
641
|
return data
|
|
632
642
|
|
|
633
643
|
@staticmethod
|
|
@@ -710,9 +720,9 @@ class Generic:
|
|
|
710
720
|
_repr_keys: list[str]
|
|
711
721
|
|
|
712
722
|
world_id: Optional[int]
|
|
713
|
-
world_name: Optional[str]
|
|
714
|
-
# This value only exists if you look up results by "Datacenter" instead of "World"
|
|
723
|
+
# world_name: Optional[str]
|
|
715
724
|
dc_name: Optional[str]
|
|
725
|
+
"This value only exists if you look up results by `Datacenter` instead of `World`"
|
|
716
726
|
_raw: DataTypedAliase | MultiPartData
|
|
717
727
|
|
|
718
728
|
def __init__(self, data: DataTypedAliase | MultiPartData) -> None:
|
|
@@ -732,6 +742,23 @@ class Generic:
|
|
|
732
742
|
f"{e}: {getattr(self, e)}" for e in sorted(self.__dict__) if e.startswith("_") is False
|
|
733
743
|
])
|
|
734
744
|
|
|
745
|
+
@property
|
|
746
|
+
def world_name(self) -> Optional[str]:
|
|
747
|
+
"""The Final Fantasy 14 World name, if applicable.
|
|
748
|
+
|
|
749
|
+
.. note::
|
|
750
|
+
- If you specify a :class:`World` when getting marketboard data..
|
|
751
|
+
- All `<CurrentData.listings>` and `<CurrentData.recent_history>` will not have the attributes `world_name`.
|
|
752
|
+
- If you specify a :class:`DataCenter` when getting marketboard data...
|
|
753
|
+
- All `<CurrentData.listings>` and `<CurrentData.recent_history>` will have the `world_id` and `world_name` attributes.
|
|
754
|
+
- :class:`CurrentData` will also have an additional attribute called `dc_name`.
|
|
755
|
+
"""
|
|
756
|
+
return self._world_name
|
|
757
|
+
|
|
758
|
+
@world_name.setter
|
|
759
|
+
def world_name(self, value: Optional[str]) -> None:
|
|
760
|
+
self._world_name: Optional[str] = value
|
|
761
|
+
|
|
735
762
|
|
|
736
763
|
class GenericData(Generic):
|
|
737
764
|
"""Base class for mutual attributes and properties for Universalis data.
|
|
@@ -906,6 +933,7 @@ class CurrentData(GenericData):
|
|
|
906
933
|
self._universalis = universalis
|
|
907
934
|
self._repr_keys = [
|
|
908
935
|
"world_name",
|
|
936
|
+
"dc_name",
|
|
909
937
|
"last_upload_time",
|
|
910
938
|
"item_id",
|
|
911
939
|
"regular_sale_velocity",
|
|
@@ -923,6 +951,7 @@ class CurrentData(GenericData):
|
|
|
923
951
|
# We get it early here, as the for loop won't set it to `None` if the data isn't there.
|
|
924
952
|
# This is being used for `CurrentDataEntries` as fetching "world" data doesn't provide the field to `listings`.
|
|
925
953
|
self.world_name = data.get("worldName", None)
|
|
954
|
+
self.dc_name = data.get("dcName", None)
|
|
926
955
|
|
|
927
956
|
for key_, value in data.items():
|
|
928
957
|
key = UniversalisAPI.from_camel_case(key_name=key_)
|
|
@@ -931,8 +960,8 @@ class CurrentData(GenericData):
|
|
|
931
960
|
self.listings = value
|
|
932
961
|
|
|
933
962
|
# This should handle price formatting.
|
|
934
|
-
elif "price" in key.lower() and (isinstance(value, (int, float))):
|
|
935
|
-
|
|
963
|
+
# elif "price" in key.lower() and (isinstance(value, (int, float))):
|
|
964
|
+
# setattr(self, key, f"{round(value):,d}")
|
|
936
965
|
|
|
937
966
|
elif key.lower() == "has_data" and isinstance(value, int):
|
|
938
967
|
self.has_data = bool(value)
|
|
@@ -948,7 +977,9 @@ class CurrentData(GenericData):
|
|
|
948
977
|
|
|
949
978
|
@listings.setter
|
|
950
979
|
def listings(self, value: list[CurrentListing]) -> None:
|
|
951
|
-
self._listings: list[CurrentDataEntries] = sorted([
|
|
980
|
+
self._listings: list[CurrentDataEntries] = sorted([
|
|
981
|
+
CurrentDataEntries(data=entry, world_name=self.world_name, dc_name=self.dc_name) for entry in value
|
|
982
|
+
])
|
|
952
983
|
|
|
953
984
|
@property
|
|
954
985
|
def recent_history(self) -> list[HistoryDataEntries]:
|
|
@@ -957,7 +988,9 @@ class CurrentData(GenericData):
|
|
|
957
988
|
|
|
958
989
|
@recent_history.setter
|
|
959
990
|
def recent_history(self, value: list[HistoryEntries]) -> None:
|
|
960
|
-
self._recent_history: list[HistoryDataEntries] = sorted([
|
|
991
|
+
self._recent_history: list[HistoryDataEntries] = sorted([
|
|
992
|
+
HistoryDataEntries(data=entry, world_name=self.world_name, dc_name=self.dc_name) for entry in value
|
|
993
|
+
])
|
|
961
994
|
|
|
962
995
|
|
|
963
996
|
class CurrentDataEntries(Generic):
|
|
@@ -1037,7 +1070,7 @@ class CurrentDataEntries(Generic):
|
|
|
1037
1070
|
_last_review_time: datetime.datetime | int
|
|
1038
1071
|
_materia: int
|
|
1039
1072
|
|
|
1040
|
-
def __init__(self, data: CurrentListing, *, world_name: Optional[str] = None) -> None:
|
|
1073
|
+
def __init__(self, data: CurrentListing, *, world_name: Optional[str] = None, dc_name: Optional[str] = None) -> None:
|
|
1041
1074
|
"""Build your JSON response :class:`CurrentDataEntries`.
|
|
1042
1075
|
|
|
1043
1076
|
Represents the data from property `<CurrentData>.listings`.
|
|
@@ -1048,20 +1081,23 @@ class CurrentDataEntries(Generic):
|
|
|
1048
1081
|
The JSON response data as a dict.
|
|
1049
1082
|
world_name: :class:`Optional[str]`
|
|
1050
1083
|
The Final Fantasy 14 World name, if applicable.
|
|
1084
|
+
dc_name: :class:`Optional[str]`
|
|
1085
|
+
The Final Fantasy 14 DataCenter name, if applicable.
|
|
1051
1086
|
|
|
1052
1087
|
"""
|
|
1053
1088
|
super().__init__(data=data)
|
|
1054
|
-
self._repr_keys = ["world_name", "price_per_unit", "quantity", "hq", "materia", "total", "tax"]
|
|
1089
|
+
self._repr_keys = ["world_name", "dc_name", "price_per_unit", "quantity", "hq", "materia", "total", "tax"]
|
|
1055
1090
|
|
|
1056
1091
|
self.world_name = world_name
|
|
1092
|
+
self.dc_name = dc_name
|
|
1057
1093
|
for key_, value in data.items():
|
|
1058
1094
|
key = UniversalisAPI.from_camel_case(key_name=key_)
|
|
1059
1095
|
if key.lower() in {"on_mannequin", "is_crafted", "hq"} and isinstance(value, int):
|
|
1060
1096
|
setattr(self, key, bool(value))
|
|
1061
1097
|
|
|
1062
1098
|
# This should handle price formatting.
|
|
1063
|
-
elif isinstance(value, (int, float)) and ("price" in key.lower() or key.lower() == "total" or key.lower() == "tax"):
|
|
1064
|
-
|
|
1099
|
+
# elif isinstance(value, (int, float)) and ("price" in key.lower() or key.lower() == "total" or key.lower() == "tax"):
|
|
1100
|
+
# setattr(self, key, f"{round(value):,d}")
|
|
1065
1101
|
|
|
1066
1102
|
else:
|
|
1067
1103
|
setattr(self, key, value)
|
|
@@ -1218,8 +1254,8 @@ class HistoryData(GenericData):
|
|
|
1218
1254
|
if key.lower() == "entries" and isinstance(value, list):
|
|
1219
1255
|
self.entries = value
|
|
1220
1256
|
# This should handle price formatting.
|
|
1221
|
-
elif isinstance(value, (int, float)) and "velocity" in key:
|
|
1222
|
-
|
|
1257
|
+
# elif isinstance(value, (int, float)) and "velocity" in key:
|
|
1258
|
+
# setattr(self, key, f"{round(value):,d}")
|
|
1223
1259
|
else:
|
|
1224
1260
|
setattr(self, key, value)
|
|
1225
1261
|
self.name = self._universalis._get_item(self.item_id) # type: ignore[reportPrivateUsage] # noqa: SLF001
|
|
@@ -1277,7 +1313,7 @@ class HistoryDataEntries(Generic):
|
|
|
1277
1313
|
world_id: Optional[int]
|
|
1278
1314
|
_timestamp: datetime.datetime | int
|
|
1279
1315
|
|
|
1280
|
-
def __init__(self, data: HistoryEntries, *, world_name: Optional[str] = None) -> None:
|
|
1316
|
+
def __init__(self, data: HistoryEntries, *, world_name: Optional[str] = None, dc_name: Optional[str] = None) -> None:
|
|
1281
1317
|
"""Build your JSON response :class:`HistoryDataEntries`.
|
|
1282
1318
|
|
|
1283
1319
|
Represents the data from property `<HistoryData>.entries` and `<CurrentData>.recent_history`.
|
|
@@ -1288,11 +1324,14 @@ class HistoryDataEntries(Generic):
|
|
|
1288
1324
|
The JSON response data as a dict.
|
|
1289
1325
|
world_name: :class:`Optional[str]`
|
|
1290
1326
|
The Final Fantasy 14 World name, if applicable.
|
|
1327
|
+
dc_name: :class:`Optional[str]`
|
|
1328
|
+
The Final Fantasy 14 DataCenter name, if applicable.
|
|
1291
1329
|
|
|
1292
1330
|
"""
|
|
1293
1331
|
super().__init__(data=data)
|
|
1294
|
-
self._repr_keys = ["world_name", "timestamp", "quantity", "price_per_unit", "hq"]
|
|
1332
|
+
self._repr_keys = ["world_name", "dc_name", "timestamp", "quantity", "price_per_unit", "hq"]
|
|
1295
1333
|
self.world_name = world_name
|
|
1334
|
+
self.dc_name = dc_name
|
|
1296
1335
|
|
|
1297
1336
|
for key_, value in data.items():
|
|
1298
1337
|
key: str = UniversalisAPI.from_camel_case(key_name=key_)
|
|
@@ -1300,8 +1339,8 @@ class HistoryDataEntries(Generic):
|
|
|
1300
1339
|
setattr(self, key, bool(value))
|
|
1301
1340
|
|
|
1302
1341
|
# This should handle price formatting.
|
|
1303
|
-
elif isinstance(value, (int, float)) and "price" in key:
|
|
1304
|
-
|
|
1342
|
+
# elif isinstance(value, (int, float)) and "price" in key:
|
|
1343
|
+
# setattr(self, key, f"{round(value):,d}")
|
|
1305
1344
|
else:
|
|
1306
1345
|
setattr(self, key, value)
|
|
1307
1346
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|