MouseTools 4.2.0__tar.gz → 4.3.1__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 (31) hide show
  1. {mousetools-4.2.0 → mousetools-4.3.1}/PKG-INFO +1 -1
  2. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/api/menu.py +19 -13
  3. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/auth.py +5 -0
  4. mousetools-4.3.1/mousetools/channels/__init__.py +125 -0
  5. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/channels/calendar/__init__.py +4 -49
  6. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/channels/characters/__init__.py +4 -49
  7. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/channels/facilities/__init__.py +4 -55
  8. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/channels/facilitystatus/__init__.py +25 -42
  9. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/channels/forecastedwaittimes/__init__.py +4 -50
  10. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/decorators.py +2 -0
  11. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/mixins/couchbase.py +5 -2
  12. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/mixins/disney.py +1 -1
  13. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools.egg-info/PKG-INFO +1 -1
  14. {mousetools-4.2.0 → mousetools-4.3.1}/pyproject.toml +1 -1
  15. mousetools-4.2.0/mousetools/channels/__init__.py +0 -62
  16. {mousetools-4.2.0 → mousetools-4.3.1}/LICENSE +0 -0
  17. {mousetools-4.2.0 → mousetools-4.3.1}/README.md +0 -0
  18. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/__init__.py +0 -0
  19. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/api/__init__.py +0 -0
  20. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/api/search.py +0 -0
  21. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/channels/enums.py +0 -0
  22. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/channels/facilities/enums.py +0 -0
  23. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/enums.py +0 -0
  24. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/mixins/__init__.py +0 -0
  25. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools/resources/environments.bin +0 -0
  26. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools.egg-info/SOURCES.txt +0 -0
  27. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools.egg-info/dependency_links.txt +0 -0
  28. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools.egg-info/requires.txt +0 -0
  29. {mousetools-4.2.0 → mousetools-4.3.1}/mousetools.egg-info/top_level.txt +0 -0
  30. {mousetools-4.2.0 → mousetools-4.3.1}/setup.cfg +0 -0
  31. {mousetools-4.2.0 → mousetools-4.3.1}/tests/test_mixins.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mousetools
3
- Version: 4.2.0
3
+ Version: 4.3.1
4
4
  Summary: An unofficial Python wrapper for Disney's API
5
5
  Author: Scott Caratozzolo
6
6
  License-Expression: MIT
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import typing
2
3
  from datetime import datetime, timedelta
3
4
  from zoneinfo import ZoneInfo
@@ -8,7 +9,7 @@ from mousetools.auth import auth_obj
8
9
  from mousetools.decorators import disney_property, json_property
9
10
  from mousetools.mixins.disney import DisneyAPIMixin
10
11
 
11
- # menus -> menu -> menugroups -> menuitems
12
+ logger = logging.getLogger(__name__)
12
13
 
13
14
 
14
15
  class MenuItem:
@@ -351,7 +352,17 @@ class Menus(DisneyAPIMixin):
351
352
 
352
353
  def refresh(self) -> None:
353
354
  """Pulls initial data if none exists or if it is older than the refresh interval"""
354
- if self._disney_data is None or datetime.now(tz=self._tz) - self.last_refresh > self._refresh_interval:
355
+ no_data_check = self._disney_data is None
356
+ time_since_pull = datetime.now(tz=self._tz) - self._disney_data_pull_time
357
+ old_data_check = time_since_pull > self._refresh_interval
358
+ logger.debug(
359
+ "No data check: %s, Time since last update: %s, Old data check: %s",
360
+ no_data_check,
361
+ time_since_pull,
362
+ old_data_check,
363
+ )
364
+ if no_data_check or old_data_check:
365
+ logger.info("Refreshing menu data %s", self.entity_id)
355
366
  self._disney_data = self.get_disney_data(self._menu_service_url)
356
367
  self._disney_data_pull_time = datetime.now(tz=self._tz)
357
368
 
@@ -396,19 +407,14 @@ class Menus(DisneyAPIMixin):
396
407
  """
397
408
  return self._disney_data["ancestorLocationLandArea"]
398
409
 
399
- @property
400
- def last_refresh(self) -> datetime:
410
+ @disney_property()
411
+ def last_refresh(self) -> typing.Optional[datetime]:
401
412
  """
402
413
  The last time the menus were refreshed.
403
414
 
404
415
  Returns:
405
- (datetime): The last time the menus were refreshed.
416
+ (typing.Optional[datetime]): The last time the menus were refreshed.
406
417
  """
407
- if self._disney_data is None:
408
- self._disney_data = self.get_disney_data(self.entity_id)
409
- try:
410
- refresh_time = isoparse(self._disney_data["lastRefresh"])
411
- refresh_time = refresh_time.replace(tzinfo=self._tz)
412
- return refresh_time
413
- except (KeyError, TypeError, ValueError):
414
- return self._disney_data_pull_time
418
+ refresh_time = isoparse(self._disney_data["lastRefresh"])
419
+ refresh_time = refresh_time.replace(tzinfo=self._tz)
420
+ return refresh_time
@@ -1,5 +1,6 @@
1
1
  import base64
2
2
  import json
3
+ import logging
3
4
  import typing
4
5
  from datetime import datetime, timedelta
5
6
  from importlib.resources import as_file, files
@@ -7,6 +8,8 @@ from importlib.resources import as_file, files
7
8
  import httpx
8
9
  from Crypto.Cipher import AES
9
10
 
11
+ logger = logging.getLogger(__name__)
12
+
10
13
 
11
14
  class Authentication:
12
15
  def __init__(self):
@@ -38,6 +41,7 @@ class Authentication:
38
41
  "client_id": self._environments["authzClientId"],
39
42
  }
40
43
 
44
+ logger.debug("Sending request for authentication token")
41
45
  response = httpx.post(
42
46
  f"{self._environments['authzServiceUrl']}/token",
43
47
  headers=headers,
@@ -57,6 +61,7 @@ class Authentication:
57
61
  """
58
62
 
59
63
  if self.time_of_expire is None or (datetime.now() > self.time_of_expire):
64
+ logger.info("Requesting new authentication token")
60
65
  access_token, expires_in = self.authenticate()
61
66
  self.time_of_expire = datetime.now() + timedelta(seconds=(expires_in - 10))
62
67
  self.access_token = access_token
@@ -0,0 +1,125 @@
1
+ import logging
2
+ import typing
3
+ from datetime import datetime, timedelta
4
+ from enum import Enum
5
+
6
+ from dateutil.parser import isoparse
7
+
8
+ from mousetools.channels.enums import DestinationTimezones, DLRCouchbaseChannels, WDWCouchbaseChannels
9
+ from mousetools.decorators import disney_property
10
+ from mousetools.enums import DestinationShort, EntityType
11
+ from mousetools.mixins.couchbase import CouchbaseMixin
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class CouchbaseChildChannel(CouchbaseMixin):
17
+ def __init__(self, channel_id: str, lazy_load: bool = True):
18
+ if isinstance(channel_id, Enum):
19
+ channel_id = channel_id.value
20
+
21
+ self.channel_id: str = channel_id
22
+ self.entity_id: str = channel_id.rsplit(".", 1)[-1]
23
+
24
+ self._destination_short: DestinationShort = self.channel_id.split(".")[0]
25
+ self._tz: DestinationTimezones = (
26
+ DestinationTimezones.WALT_DISNEY_WORLD
27
+ if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
28
+ else DestinationTimezones.DISNEYLAND_RESORT
29
+ )
30
+ self._refresh_interval: timedelta = timedelta(hours=12)
31
+ self._cb_data: typing.Optional[dict] = None
32
+ self._cb_data_pull_time: datetime = datetime.now(tz=self._tz.value)
33
+ if not lazy_load:
34
+ self.refresh()
35
+
36
+ def refresh(self) -> None:
37
+ """Pulls initial data if none exists or if it is older than the refresh interval"""
38
+
39
+ no_data_check = self._cb_data is None
40
+ time_since_pull = datetime.now(tz=self._tz.value) - self._cb_data_pull_time
41
+ old_data_check = time_since_pull > self._refresh_interval
42
+ logger.debug(
43
+ "No data check: %s, Time since pull: %s, Old data check: %s", no_data_check, time_since_pull, old_data_check
44
+ )
45
+ if no_data_check or old_data_check:
46
+ logger.info("Refreshing %s", self.channel_id)
47
+ self._cb_data = self.get_channel_data(self.channel_id)
48
+ self._cb_data_pull_time = datetime.now(tz=self._tz.value)
49
+
50
+ @disney_property()
51
+ def last_update(self) -> typing.Optional[datetime]:
52
+ """
53
+ The last time disney updated the data.
54
+
55
+ Returns:
56
+ (typing.Optional[datetime]): The last time the entity's data was updated, or None if no such data exists.
57
+ """
58
+ dt = isoparse(self._cb_data["lastUpdate"])
59
+ dt = dt.replace(tzinfo=self._tz.value)
60
+ return dt
61
+
62
+
63
+ class CouchbaseChannel(CouchbaseMixin):
64
+ def __init__(self, channel_id: typing.Union[WDWCouchbaseChannels, DLRCouchbaseChannels], lazy_load: bool = True):
65
+ """
66
+ Args:
67
+ channel_id (typing.Union[WDWCouchbaseChannels, DLRCouchbaseChannels]): Channel ID from the enum
68
+ lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
69
+ """
70
+ if isinstance(channel_id, Enum):
71
+ channel_id = channel_id.value
72
+ self.channel_id = channel_id
73
+
74
+ self._destination_short: DestinationShort = channel_id.split(".")[0]
75
+ self._tz: DestinationTimezones = (
76
+ DestinationTimezones.WALT_DISNEY_WORLD
77
+ if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
78
+ else DestinationTimezones.DISNEYLAND_RESORT
79
+ )
80
+
81
+ self._cb_data = None
82
+ self._refresh_interval = timedelta(minutes=10)
83
+ self._cb_data_pull_time = datetime.now(tz=self._tz.value)
84
+
85
+ if not lazy_load:
86
+ self.refresh()
87
+
88
+ def refresh(self) -> None:
89
+ """Pulls initial data if none exists or if it is older than the refresh interval"""
90
+ no_data_check = self._cb_data is None
91
+ time_since_pull = datetime.now(tz=self._tz.value) - self._cb_data_pull_time
92
+ old_data_check = time_since_pull > self._refresh_interval
93
+ logger.debug(
94
+ "No data check: %s, Time since last update: %s, Old data check: %s",
95
+ no_data_check,
96
+ time_since_pull,
97
+ old_data_check,
98
+ )
99
+ if no_data_check or old_data_check:
100
+ logger.info("Refreshing %s", self.channel_id)
101
+ self._cb_data = self.get_channel_changes(self.channel_id)
102
+ self._cb_data_pull_time = datetime.now(tz=self._tz.value)
103
+
104
+ def get_children_channel_ids(self) -> list[str]:
105
+ """Returns a list of child channel ids for the channel."""
106
+ self.refresh()
107
+
108
+ return [i["id"] for i in self._cb_data["results"]]
109
+
110
+ def _get_children_objects_helper(self, class_obj, entity_type: EntityType) -> list:
111
+ self.refresh()
112
+ channels = []
113
+ check_for = f"entitytype={entity_type.value}".lower()
114
+ for i in self._cb_data["results"]:
115
+ if check_for == i["id"].rsplit(";")[-1].lower():
116
+ channels.append(class_obj(i["id"]))
117
+ return channels
118
+
119
+ def __repr__(self):
120
+ return f"{self.__class__.__name__}(channel_id='{self.channel_id}')"
121
+
122
+ def __eq__(self, other) -> bool:
123
+ if isinstance(other, CouchbaseChannel):
124
+ return self.channel_id == other.channel_id
125
+ return False
@@ -1,21 +1,17 @@
1
1
  import logging
2
2
  import typing
3
- from datetime import datetime, timedelta
4
- from enum import Enum
5
3
  from zoneinfo import ZoneInfo
6
4
 
7
5
  from dateutil.parser import isoparse
8
6
 
9
- from mousetools.channels import CouchbaseChannel
10
- from mousetools.channels.enums import CouchbaseChannels, DestinationTimezones
7
+ from mousetools.channels import CouchbaseChannel, CouchbaseChildChannel
8
+ from mousetools.channels.enums import CouchbaseChannels
11
9
  from mousetools.decorators import disney_property
12
- from mousetools.enums import DestinationShort
13
- from mousetools.mixins.couchbase import CouchbaseMixin
14
10
 
15
11
  logger = logging.getLogger(__name__)
16
12
 
17
13
 
18
- class CalendarChildChannel(CouchbaseMixin):
14
+ class CalendarChildChannel(CouchbaseChildChannel):
19
15
  def __init__(
20
16
  self,
21
17
  channel_id: str,
@@ -26,34 +22,11 @@ class CalendarChildChannel(CouchbaseMixin):
26
22
  channel_id (str): Calendar Channel ID
27
23
  lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
28
24
  """
29
- if isinstance(channel_id, Enum):
30
- channel_id = channel_id.value
31
-
32
- self.channel_id: str = channel_id
33
- self.entity_id: str = channel_id.rsplit(".", 1)[-1]
34
-
35
- self._destination_short: DestinationShort = self.channel_id.split(".")[0]
36
- self._tz: DestinationTimezones = (
37
- DestinationTimezones.WALT_DISNEY_WORLD
38
- if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
39
- else DestinationTimezones.DISNEYLAND_RESORT
40
- )
41
- self._refresh_interval: timedelta = timedelta(minutes=30)
42
-
43
- self._cb_data: typing.Optional[dict] = None
44
- self._cb_data_pull_time: datetime = datetime.now(tz=self._tz.value)
45
- if not lazy_load:
46
- self.refresh()
25
+ super().__init__(channel_id, lazy_load=lazy_load)
47
26
 
48
27
  def __repr__(self):
49
28
  return f"{self.__class__.__name__}(channel_id='{self.channel_id}')"
50
29
 
51
- def refresh(self) -> None:
52
- """Pulls initial data if none exists or if it is older than the refresh interval"""
53
- if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
54
- self._cb_data = self.get_channel_data(self.channel_id)
55
- self._cb_data_pull_time = datetime.now(tz=self._tz.value)
56
-
57
30
  @disney_property()
58
31
  def calendar_id(self) -> typing.Optional[str]:
59
32
  """The ID of the calendar
@@ -63,24 +36,6 @@ class CalendarChildChannel(CouchbaseMixin):
63
36
  """
64
37
  return self._cb_data["id"]
65
38
 
66
- @property
67
- def last_update(self) -> datetime:
68
- """The last time the data was updated.
69
-
70
- Returns:
71
- (datetime): The last time the entity's data was updated, or None if no such data exists.
72
- """
73
- if self._cb_data is None:
74
- self._cb_data = self.get_channel_data(self.channel_id)
75
-
76
- try:
77
- dt = isoparse(self._cb_data["lastUpdate"])
78
- dt = dt.replace(tzinfo=self._tz.value)
79
- return dt
80
- except (KeyError, ValueError, TypeError):
81
- logger.debug("No last updated found for %s", self.channel_id)
82
- return self._cb_data_pull_time
83
-
84
39
  @disney_property()
85
40
  def all_closed(self) -> typing.Optional[list[dict]]:
86
41
  """The list of closed facilities.
@@ -1,20 +1,16 @@
1
1
  import logging
2
- from datetime import datetime, timedelta
3
- from enum import Enum
4
2
  from typing import Optional
5
3
 
6
4
  from dateutil.parser import isoparse
7
5
 
8
- from mousetools.channels import CouchbaseChannel
9
- from mousetools.channels.enums import CouchbaseChannels, DestinationTimezones
6
+ from mousetools.channels import CouchbaseChannel, CouchbaseChildChannel
7
+ from mousetools.channels.enums import CouchbaseChannels
10
8
  from mousetools.decorators import disney_property
11
- from mousetools.enums import DestinationShort
12
- from mousetools.mixins.couchbase import CouchbaseMixin
13
9
 
14
10
  logger = logging.getLogger(__name__)
15
11
 
16
12
 
17
- class CharactersChildChannel(CouchbaseMixin):
13
+ class CharactersChildChannel(CouchbaseChildChannel):
18
14
  def __init__(
19
15
  self,
20
16
  channel_id: str,
@@ -25,52 +21,11 @@ class CharactersChildChannel(CouchbaseMixin):
25
21
  channel_id (str): Forecasted Wait Times Channel ID
26
22
  lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
27
23
  """
28
- if isinstance(channel_id, Enum):
29
- channel_id = channel_id.value
30
-
31
- self.channel_id: str = channel_id
32
- self.entity_id: str = channel_id.rsplit(".", 1)[-1]
33
-
34
- self._destination_short: DestinationShort = self.channel_id.split(".")[0]
35
- self._tz: DestinationTimezones = (
36
- DestinationTimezones.WALT_DISNEY_WORLD
37
- if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
38
- else DestinationTimezones.DISNEYLAND_RESORT
39
- )
40
- self._refresh_interval: timedelta = timedelta(minutes=30)
41
-
42
- self._cb_data: Optional[dict] = None
43
- self._cb_data_pull_time: Optional[datetime] = datetime.now(tz=self._tz.value)
44
- if not lazy_load:
45
- self.refresh()
24
+ super().__init__(channel_id, lazy_load=lazy_load)
46
25
 
47
26
  def __repr__(self):
48
27
  return f"{self.__class__.__name__}(channel_id='{self.channel_id}')"
49
28
 
50
- def refresh(self) -> None:
51
- """Pulls initial data if none exists or if it is older than the refresh interval"""
52
- if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
53
- self._cb_data = self.get_channel_data(self.channel_id)
54
- self._cb_data_pull_time = datetime.now(tz=self._tz.value)
55
-
56
- @property
57
- def last_update(self) -> datetime:
58
- """The last time the data was updated.
59
-
60
- Returns:
61
- (datetime): The last time the entity's data was updated, or None if no such data exists.
62
- """
63
- if self._cb_data is None:
64
- self._cb_data = self.get_channel_data(self.channel_id)
65
-
66
- try:
67
- dt = isoparse(self._cb_data["lastUpdate"])
68
- dt = dt.replace(tzinfo=self._tz.value)
69
- return dt
70
- except (KeyError, TypeError, ValueError):
71
- logger.debug("No last updated found for %s", self.channel_id)
72
- return self._cb_data_pull_time
73
-
74
29
  @disney_property()
75
30
  def name(self) -> Optional[str]:
76
31
  """The name of the entity.
@@ -2,18 +2,15 @@ import logging
2
2
  import os
3
3
  import typing
4
4
  from datetime import datetime, time, timedelta
5
- from enum import Enum
6
5
  from zoneinfo import ZoneInfo
7
6
 
8
7
  import httpx
9
- from dateutil.parser import isoparse
10
8
 
11
9
  from mousetools.api.menu import Menus
12
- from mousetools.channels import CouchbaseChannel
10
+ from mousetools.channels import CouchbaseChannel, CouchbaseChildChannel
13
11
  from mousetools.channels.calendar import CalendarChildChannel
14
12
  from mousetools.channels.enums import (
15
13
  CouchbaseChannels,
16
- DestinationTimezones,
17
14
  DLRCouchbaseChannels,
18
15
  WDWCouchbaseChannels,
19
16
  )
@@ -21,12 +18,11 @@ from mousetools.channels.facilitystatus import FacilityStatusChildChannel
21
18
  from mousetools.channels.forecastedwaittimes import ForecastedWaitTimesChildChannel
22
19
  from mousetools.decorators import disney_property
23
20
  from mousetools.enums import DestinationShort, EntityType
24
- from mousetools.mixins.couchbase import CouchbaseMixin
25
21
 
26
22
  logger = logging.getLogger(__name__)
27
23
 
28
24
 
29
- class FacilityChildChannel(CouchbaseMixin):
25
+ class FacilityChildChannel(CouchbaseChildChannel):
30
26
  """Base class for all Facility Child Channels. Subclasses like Attraction, Restaurant, Entertainment, etc. extend this class.
31
27
 
32
28
  Example:
@@ -51,35 +47,12 @@ class FacilityChildChannel(CouchbaseMixin):
51
47
  channel_id (str): Facility Channel ID
52
48
  lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
53
49
  """
54
- if isinstance(channel_id, Enum):
55
- channel_id = channel_id.value
50
+ super().__init__(channel_id, lazy_load=lazy_load)
56
51
 
57
- self.channel_id: str = channel_id
58
- self.entity_id: str = channel_id.rsplit(".", 1)[-1]
59
-
60
- self._destination_short: DestinationShort = self.channel_id.split(".")[0]
61
- self._tz: DestinationTimezones = (
62
- DestinationTimezones.WALT_DISNEY_WORLD
63
- if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
64
- else DestinationTimezones.DISNEYLAND_RESORT
65
- )
66
- self._refresh_interval: timedelta = timedelta(
67
- hours=12
68
- ) # properties are rarely updated, no need to spam the API
69
-
70
- self._cb_data: typing.Optional[dict] = None
71
- self._cb_data_pull_time: datetime = datetime.now(tz=self._tz.value)
72
- if not lazy_load:
73
- self.refresh()
52
+ self._refresh_interval = timedelta(weeks=1) # properties are rarely updated
74
53
 
75
54
  self._today_calendar: typing.Optional[CalendarChildChannel] = None
76
55
 
77
- def refresh(self) -> None:
78
- """Pulls initial data if none exists or if it is older than the refresh interval"""
79
- if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
80
- self._cb_data = self.get_channel_data(self.channel_id)
81
- self._cb_data_pull_time = datetime.now(tz=self._tz.value)
82
-
83
56
  def pull_today_calendar(self) -> None:
84
57
  """Pulls today's calendar.
85
58
 
@@ -392,30 +365,6 @@ class FacilityChildChannel(CouchbaseMixin):
392
365
 
393
366
  return FacilityStatusChildChannel(facility_status_channel_id)
394
367
 
395
- @property
396
- def last_update(self) -> datetime:
397
- """
398
- The last time the data was updated.
399
-
400
- Returns:
401
- (datetime): The last time the entity's data was updated, or None if no such data exists.
402
- """
403
- if self._cb_data is None:
404
- self._cb_data = self.get_channel_data(self.channel_id)
405
-
406
- try:
407
- tz = (
408
- DestinationTimezones.WALT_DISNEY_WORLD
409
- if "wdw" in self.channel_id
410
- else DestinationTimezones.DISNEYLAND_RESORT
411
- )
412
- dt = isoparse(self._cb_data["lastUpdate"])
413
- dt = dt.replace(tzinfo=tz.value)
414
- return dt
415
- except (KeyError, TypeError, ValueError):
416
- logger.debug("No last updated found for %s", self.channel_id)
417
- return self._cb_data_pull_time
418
-
419
368
  @disney_property()
420
369
  def sponsor_name(self) -> typing.Optional[str]:
421
370
  """The sponsor name of the entity.
@@ -4,42 +4,24 @@ from datetime import datetime, timedelta
4
4
 
5
5
  from dateutil.parser import isoparse
6
6
 
7
- from mousetools.channels import CouchbaseChannel
8
- from mousetools.channels.enums import CouchbaseChannels, DestinationTimezones
7
+ from mousetools.channels import CouchbaseChannel, CouchbaseChildChannel
8
+ from mousetools.channels.enums import CouchbaseChannels
9
9
  from mousetools.decorators import disney_property
10
- from mousetools.enums import DestinationShort, EntityType
11
- from mousetools.mixins.couchbase import CouchbaseMixin
10
+ from mousetools.enums import EntityType
12
11
 
13
12
  logger = logging.getLogger(__name__)
14
13
 
15
14
 
16
- class FacilityStatusChildChannel(CouchbaseMixin):
15
+ class FacilityStatusChildChannel(CouchbaseChildChannel):
17
16
  def __init__(self, channel_id: str, lazy_load: bool = True):
18
17
  """
19
18
  Args:
20
19
  channel_id (typing.Union[WDWCouchbaseChannels, DLRCouchbaseChannels]): Channel ID from the enum
21
20
  lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
22
21
  """
23
- self.channel_id = channel_id
24
-
25
- self._destination_short: DestinationShort = channel_id.split(".")[0]
26
- self._tz: DestinationTimezones = (
27
- DestinationTimezones.WALT_DISNEY_WORLD
28
- if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
29
- else DestinationTimezones.DISNEYLAND_RESORT
30
- )
31
- self._refresh_interval: timedelta = timedelta(minutes=10)
32
- self._cb_data: typing.Optional[dict] = None
33
- self._cb_data_pull_time: datetime = datetime.now(tz=self._tz.value)
34
-
35
- if not lazy_load:
36
- self.refresh()
22
+ super().__init__(channel_id, lazy_load=lazy_load)
37
23
 
38
- def refresh(self) -> None:
39
- """Pulls initial data if none exists or if it is older than the refresh interval"""
40
- if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
41
- self._cb_data = self.get_channel_data(self.channel_id)
42
- self._cb_data_pull_time = datetime.now(tz=self._tz.value)
24
+ self._refresh_interval: timedelta = timedelta(minutes=10)
43
25
 
44
26
  def get_status(self) -> typing.Optional[str]:
45
27
  """Get the operating status of the facility
@@ -69,24 +51,6 @@ class FacilityStatusChildChannel(CouchbaseMixin):
69
51
  logger.debug("No wait time found for %s", self.channel_id)
70
52
  return None
71
53
 
72
- @property
73
- def last_update(self) -> datetime:
74
- """The last time the data was updated.
75
-
76
- Returns:
77
- (datetime): The last time the entity's data was updated, or None if no such data exists.
78
- """
79
- if self._cb_data is None:
80
- self._cb_data = self.get_channel_data(self.channel_id)
81
-
82
- try:
83
- dt = isoparse(self._cb_data["lastUpdate"])
84
- dt = dt.replace(tzinfo=self._tz.value)
85
- return dt
86
- except (KeyError, TypeError, ValueError):
87
- logger.debug("No last updated found for %s", self.channel_id)
88
- return self._cb_data_pull_time
89
-
90
54
  @disney_property()
91
55
  def fast_pass_available(self) -> typing.Optional[bool]:
92
56
  return self._cb_data["fastPassAvailable"]
@@ -157,6 +121,25 @@ class FacilityStatusChannel(CouchbaseChannel):
157
121
  channels.append(FacilityStatusChildChannel(i["id"]))
158
122
  return channels
159
123
 
124
+ def get_all_statuses(self) -> dict[str, dict[str, typing.Any]]:
125
+ """Get all statuses for the channel
126
+
127
+ Returns:
128
+ (dict[str, dict[str, typing.Any]]): A dictionary of all statuses for the channel
129
+ """
130
+ logger.info("Getting all children statuses for %s", self.channel_id)
131
+ children = self.get_children_channels()
132
+ logger.debug("Got %d children for %s", len(children), self.channel_id)
133
+
134
+ statuses = {}
135
+ for child in children:
136
+ tmp = {}
137
+ tmp["status"] = child.get_status()
138
+ tmp["wait_time"] = child.get_wait_time()
139
+ statuses[child.entity_id] = tmp
140
+
141
+ return statuses
142
+
160
143
  def get_children_attractions(self) -> list[AttractionFacilityStatusChild]: # type: ignore
161
144
  """Get AttractionFacilityStatusChild Children of a Facility
162
145
 
@@ -1,19 +1,14 @@
1
1
  import logging
2
- import typing
3
- from datetime import datetime, timedelta
4
- from enum import Enum
5
2
 
6
3
  from dateutil.parser import isoparse
7
4
 
8
- from mousetools.channels import CouchbaseChannel
9
- from mousetools.channels.enums import CouchbaseChannels, DestinationTimezones
10
- from mousetools.enums import DestinationShort
11
- from mousetools.mixins.couchbase import CouchbaseMixin
5
+ from mousetools.channels import CouchbaseChannel, CouchbaseChildChannel
6
+ from mousetools.channels.enums import CouchbaseChannels
12
7
 
13
8
  logger = logging.getLogger(__name__)
14
9
 
15
10
 
16
- class ForecastedWaitTimesChildChannel(CouchbaseMixin):
11
+ class ForecastedWaitTimesChildChannel(CouchbaseChildChannel):
17
12
  def __init__(
18
13
  self,
19
14
  channel_id: str,
@@ -24,52 +19,11 @@ class ForecastedWaitTimesChildChannel(CouchbaseMixin):
24
19
  channel_id (str): Forecasted Wait Times Channel ID
25
20
  lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
26
21
  """
27
- if isinstance(channel_id, Enum):
28
- channel_id = channel_id.value
29
-
30
- self.channel_id: str = channel_id
31
- self.entity_id: str = channel_id.rsplit(".", 1)[-1]
32
-
33
- self._destination_short: DestinationShort = self.channel_id.split(".")[0]
34
- self._tz: DestinationTimezones = (
35
- DestinationTimezones.WALT_DISNEY_WORLD
36
- if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
37
- else DestinationTimezones.DISNEYLAND_RESORT
38
- )
39
- self._refresh_interval: timedelta = timedelta(minutes=30)
40
-
41
- self._cb_data: typing.Optional[dict] = None
42
- self._cb_data_pull_time: datetime = datetime.now(tz=self._tz.value)
43
- if not lazy_load:
44
- self.refresh()
22
+ super().__init__(channel_id, lazy_load=lazy_load)
45
23
 
46
24
  def __repr__(self):
47
25
  return f"{self.__class__.__name__}(channel_id='{self.channel_id}')"
48
26
 
49
- def refresh(self) -> None:
50
- """Pulls initial data if none exists or if it is older than the refresh interval"""
51
- if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
52
- self._cb_data = self.get_channel_data(self.channel_id)
53
- self._cb_data_pull_time = datetime.now(tz=self._tz.value)
54
-
55
- @property
56
- def last_update(self) -> datetime:
57
- """The last time the data was updated.
58
-
59
- Returns:
60
- (datetime): The last time the entity's data was updated, or None if no such data exists.
61
- """
62
- if self._cb_data is None:
63
- self._cb_data = self.get_channel_data(self.channel_id)
64
-
65
- try:
66
- dt = isoparse(self._cb_data["lastUpdate"])
67
- dt = dt.replace(tzinfo=self._tz.value)
68
- return dt
69
- except (KeyError, TypeError, ValueError):
70
- logger.debug("No last updated found for %s", self.channel_id)
71
- return self._cb_data_pull_time
72
-
73
27
  def get_forecast(self) -> list[dict]:
74
28
  """Returns a list of forecasted wait times sorted by timestamp
75
29
 
@@ -18,6 +18,7 @@ def disney_property(
18
18
  try:
19
19
  return func(self)
20
20
  except (KeyError, TypeError, ValueError):
21
+ logger.debug("No data found for disney property %s, returning default value", func.__name__)
21
22
  return default_value
22
23
 
23
24
  wrapper.__annotations__ = func.__annotations__
@@ -34,6 +35,7 @@ def json_property(func: Callable) -> property:
34
35
  try:
35
36
  return func(self)
36
37
  except (KeyError, TypeError, ValueError):
38
+ logger.debug("No data found for json property %s", func.__name__)
37
39
  return None
38
40
 
39
41
  return wrapper
@@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
11
11
 
12
12
 
13
13
  class CouchbaseMixin:
14
- _couchbase_url = "https://" + auth_obj._environments["syncGatewayUrl"].split("//")[1]
14
+ _couchbase_url = "https://" + auth_obj._environments["syncGatewayUrl"].split("//")[1].rstrip("/")
15
15
  _headers: dict = auth_obj.get_couchbase_headers()
16
16
 
17
17
  def get_channel_data(self, channel_name: str) -> typing.Union[dict, None]:
@@ -35,8 +35,9 @@ class CouchbaseMixin:
35
35
  "json": True,
36
36
  }
37
37
 
38
+ logger.info("Sending request for channel data: %s", channel_name)
38
39
  response = httpx.post(self._couchbase_url + "/_bulk_get", json=payload, headers=self._headers)
39
- logger.debug("Response status: %s", response.status_code)
40
+ logger.debug("Response status for %s: %s", channel_name, response.status_code)
40
41
  data = None
41
42
  try:
42
43
  response.raise_for_status()
@@ -71,7 +72,9 @@ class CouchbaseMixin:
71
72
  if since:
72
73
  payload["since"] = since
73
74
 
75
+ logger.info("Sending request for channel changes: %s", channel_name)
74
76
  response = httpx.post(self._couchbase_url + "/_changes", json=payload, headers=self._headers)
77
+ logger.debug("Response status for %s: %s", channel_name, response.status_code)
75
78
  data = response.raise_for_status().json()
76
79
  except httpx.HTTPError as err:
77
80
  logger.error("Request failed: %s", err)
@@ -31,7 +31,7 @@ class DisneyAPIMixin:
31
31
  response.raise_for_status()
32
32
  data = response.json()
33
33
  except (httpx.HTTPError, json.decoder.JSONDecodeError) as err:
34
- logger.warning("Request failed. Error: %s", err)
34
+ logger.error("Request failed. Error: %s", err)
35
35
  return None
36
36
 
37
37
  return data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mousetools
3
- Version: 4.2.0
3
+ Version: 4.3.1
4
4
  Summary: An unofficial Python wrapper for Disney's API
5
5
  Author: Scott Caratozzolo
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mousetools"
3
- version = "4.2.0"
3
+ version = "4.3.1"
4
4
  authors = [{name = "Scott Caratozzolo"}]
5
5
  description = "An unofficial Python wrapper for Disney's API"
6
6
  readme = "README.md"
@@ -1,62 +0,0 @@
1
- import typing
2
- from datetime import datetime, timedelta
3
- from enum import Enum
4
-
5
- from mousetools.channels.enums import DestinationTimezones, DLRCouchbaseChannels, WDWCouchbaseChannels
6
- from mousetools.enums import DestinationShort, EntityType
7
- from mousetools.mixins.couchbase import CouchbaseMixin
8
-
9
-
10
- class CouchbaseChannel(CouchbaseMixin):
11
- def __init__(self, channel_id: typing.Union[WDWCouchbaseChannels, DLRCouchbaseChannels], lazy_load: bool = True):
12
- """
13
- Args:
14
- channel_id (typing.Union[WDWCouchbaseChannels, DLRCouchbaseChannels]): Channel ID from the enum
15
- lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
16
- """
17
- if isinstance(channel_id, Enum):
18
- channel_id = channel_id.value
19
- self.channel_id = channel_id
20
-
21
- self._destination_short: DestinationShort = channel_id.split(".")[0]
22
- self._tz: DestinationTimezones = (
23
- DestinationTimezones.WALT_DISNEY_WORLD
24
- if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
25
- else DestinationTimezones.DISNEYLAND_RESORT
26
- )
27
-
28
- self._cb_data = None
29
- self._refresh_interval = timedelta(minutes=10)
30
- self.last_update = datetime.now(tz=self._tz.value)
31
-
32
- if not lazy_load:
33
- self.refresh()
34
-
35
- def refresh(self) -> None:
36
- """Pulls initial data if none exists or if it is older than the refresh interval"""
37
- if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
38
- self._cb_data = self.get_channel_changes(self.channel_id)
39
- self.last_update = datetime.now(tz=self._tz.value)
40
-
41
- def get_children_channel_ids(self) -> list[str]:
42
- """Returns a list of child channel ids for the channel."""
43
- self.refresh()
44
-
45
- return [i["id"] for i in self._cb_data["results"]]
46
-
47
- def _get_children_objects_helper(self, class_obj, entity_type: EntityType) -> list:
48
- self.refresh()
49
- channels = []
50
- check_for = f"entitytype={entity_type.value}".lower()
51
- for i in self._cb_data["results"]:
52
- if check_for == i["id"].rsplit(";")[-1].lower():
53
- channels.append(class_obj(i["id"]))
54
- return channels
55
-
56
- def __repr__(self):
57
- return f"{self.__class__.__name__}(channel_id='{self.channel_id}')"
58
-
59
- def __eq__(self, other) -> bool:
60
- if isinstance(other, CouchbaseChannel):
61
- return self.channel_id == other.channel_id
62
- return False
File without changes
File without changes
File without changes