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.
Files changed (17) hide show
  1. {async_universalis-4.0.0.dev0/async_universalis.egg-info → async_universalis-4.1.0.dev0}/PKG-INFO +1 -1
  2. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/__init__.py +86 -47
  3. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0/async_universalis.egg-info}/PKG-INFO +1 -1
  4. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/LICENSE +0 -0
  5. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/MANIFEST.in +0 -0
  6. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/README.md +0 -0
  7. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/_enums.py +0 -0
  8. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/_types.py +0 -0
  9. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/errors.py +0 -0
  10. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/items.json +0 -0
  11. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis/py.typed +0 -0
  12. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis.egg-info/SOURCES.txt +0 -0
  13. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis.egg-info/dependency_links.txt +0 -0
  14. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis.egg-info/requires.txt +0 -0
  15. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/async_universalis.egg-info/top_level.txt +0 -0
  16. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/pyproject.toml +0 -0
  17. {async_universalis-4.0.0.dev0 → async_universalis-4.1.0.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: async_universalis
3
- Version: 4.0.0.dev0
3
+ Version: 4.1.0.dev0
4
4
  Summary: A bare-bones wrapper package to utilitize Universalis API in python.
5
5
  Author-email: k8thekat <Cadwalladerkatelynn@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -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.0.0-dev"
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 `<World>`.
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 `<DataCenter>`.
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
- - `<CurrentData>` will also have an additional attribute called `dc_name`.
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 `<World>`.
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 `<DataCenter>`.
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
- - `<CurrentData>` will also have an additional attribute called `dc_name`.
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 = MultiPart(
445
- universalis=self,
446
- resolved_items=[CurrentData(universalis=self, data=value) for value in res.get("items").values() if "listings" in value],
447
- **res,
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 `<World>`.
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 `<DataCenter>`.
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 `<World>`.
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 `<DataCenter>`.
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 `get_current_data` instead.
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 = MultiPart(
627
- universalis=self,
628
- resolved_items=[HistoryData(universalis=self, data=value) for value in res.get("items").values() if "entries" in value],
629
- **res,
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
- setattr(self, key, f"{round(value):,d}")
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([CurrentDataEntries(data=entry, world_name=self.world_name) for entry in value])
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([HistoryDataEntries(data=entry) for entry in value])
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
- setattr(self, key, f"{round(value):,d}")
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
- setattr(self, key, f"{round(value):,d}")
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
- setattr(self, key, f"{round(value):,d}")
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: async_universalis
3
- Version: 4.0.0.dev0
3
+ Version: 4.1.0.dev0
4
4
  Summary: A bare-bones wrapper package to utilitize Universalis API in python.
5
5
  Author-email: k8thekat <Cadwalladerkatelynn@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE