MouseTools 4.1.1__tar.gz → 4.3.0__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.
- {mousetools-4.1.1 → mousetools-4.3.0}/PKG-INFO +1 -1
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/api/menu.py +3 -3
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/channels/__init__.py +51 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/channels/calendar/__init__.py +4 -49
- mousetools-4.3.0/mousetools/channels/characters/__init__.py +122 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/channels/enums.py +5 -5
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/channels/facilities/__init__.py +86 -83
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/channels/facilitystatus/__init__.py +32 -59
- mousetools-4.3.0/mousetools/channels/forecastedwaittimes/__init__.py +71 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/decorators.py +6 -3
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/mixins/couchbase.py +1 -1
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools.egg-info/PKG-INFO +1 -1
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools.egg-info/SOURCES.txt +2 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/pyproject.toml +2 -1
- {mousetools-4.1.1 → mousetools-4.3.0}/LICENSE +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/README.md +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/__init__.py +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/api/__init__.py +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/api/search.py +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/auth.py +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/channels/facilities/enums.py +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/enums.py +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/mixins/__init__.py +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/mixins/disney.py +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools/resources/environments.bin +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools.egg-info/dependency_links.txt +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools.egg-info/requires.txt +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/mousetools.egg-info/top_level.txt +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/setup.cfg +0 -0
- {mousetools-4.1.1 → mousetools-4.3.0}/tests/test_mixins.py +0 -0
|
@@ -339,7 +339,7 @@ class Menus(DisneyAPIMixin):
|
|
|
339
339
|
self._tz = ZoneInfo("UTC")
|
|
340
340
|
|
|
341
341
|
self._disney_data: typing.Optional[dict] = None
|
|
342
|
-
self._disney_data_pull_time:
|
|
342
|
+
self._disney_data_pull_time: datetime = datetime.now(tz=self._tz)
|
|
343
343
|
if not lazy_load:
|
|
344
344
|
self.refresh()
|
|
345
345
|
|
|
@@ -397,12 +397,12 @@ class Menus(DisneyAPIMixin):
|
|
|
397
397
|
return self._disney_data["ancestorLocationLandArea"]
|
|
398
398
|
|
|
399
399
|
@property
|
|
400
|
-
def last_refresh(self) ->
|
|
400
|
+
def last_refresh(self) -> datetime:
|
|
401
401
|
"""
|
|
402
402
|
The last time the menus were refreshed.
|
|
403
403
|
|
|
404
404
|
Returns:
|
|
405
|
-
(
|
|
405
|
+
(datetime): The last time the menus were refreshed.
|
|
406
406
|
"""
|
|
407
407
|
if self._disney_data is None:
|
|
408
408
|
self._disney_data = self.get_disney_data(self.entity_id)
|
|
@@ -1,11 +1,62 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import typing
|
|
2
3
|
from datetime import datetime, timedelta
|
|
3
4
|
from enum import Enum
|
|
4
5
|
|
|
6
|
+
from dateutil.parser import isoparse
|
|
7
|
+
|
|
5
8
|
from mousetools.channels.enums import DestinationTimezones, DLRCouchbaseChannels, WDWCouchbaseChannels
|
|
6
9
|
from mousetools.enums import DestinationShort, EntityType
|
|
7
10
|
from mousetools.mixins.couchbase import CouchbaseMixin
|
|
8
11
|
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CouchbaseChildChannel(CouchbaseMixin):
|
|
16
|
+
def __init__(self, channel_id: str, lazy_load: bool = True):
|
|
17
|
+
if isinstance(channel_id, Enum):
|
|
18
|
+
channel_id = channel_id.value
|
|
19
|
+
|
|
20
|
+
self.channel_id: str = channel_id
|
|
21
|
+
self.entity_id: str = channel_id.rsplit(".", 1)[-1]
|
|
22
|
+
|
|
23
|
+
self._destination_short: DestinationShort = self.channel_id.split(".")[0]
|
|
24
|
+
self._tz: DestinationTimezones = (
|
|
25
|
+
DestinationTimezones.WALT_DISNEY_WORLD
|
|
26
|
+
if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
|
|
27
|
+
else DestinationTimezones.DISNEYLAND_RESORT
|
|
28
|
+
)
|
|
29
|
+
self._refresh_interval: timedelta = timedelta(minutes=30)
|
|
30
|
+
self._cb_data: typing.Optional[dict] = None
|
|
31
|
+
self._cb_data_pull_time: datetime = datetime.now(tz=self._tz.value)
|
|
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_data(self.channel_id)
|
|
39
|
+
self._cb_data_pull_time = datetime.now(tz=self._tz.value)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def last_update(self) -> datetime:
|
|
43
|
+
"""
|
|
44
|
+
The last time the data was updated.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
(datetime): The last time the entity's data was updated, or None if no such data exists.
|
|
48
|
+
"""
|
|
49
|
+
if self._cb_data is None:
|
|
50
|
+
self._cb_data = self.get_channel_data(self.channel_id)
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
dt = isoparse(self._cb_data["lastUpdate"])
|
|
54
|
+
dt = dt.replace(tzinfo=self._tz.value)
|
|
55
|
+
return dt
|
|
56
|
+
except (KeyError, TypeError, ValueError):
|
|
57
|
+
logger.debug("No last updated found for %s", self.channel_id)
|
|
58
|
+
return self._cb_data_pull_time
|
|
59
|
+
|
|
9
60
|
|
|
10
61
|
class CouchbaseChannel(CouchbaseMixin):
|
|
11
62
|
def __init__(self, channel_id: typing.Union[WDWCouchbaseChannels, DLRCouchbaseChannels], lazy_load: bool = True):
|
|
@@ -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
|
|
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(
|
|
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
|
-
|
|
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: typing.Optional[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) -> typing.Optional[datetime]:
|
|
68
|
-
"""The last time the data was updated.
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
(typing.Optional[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.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from dateutil.parser import isoparse
|
|
5
|
+
|
|
6
|
+
from mousetools.channels import CouchbaseChannel, CouchbaseChildChannel
|
|
7
|
+
from mousetools.channels.enums import CouchbaseChannels
|
|
8
|
+
from mousetools.decorators import disney_property
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CharactersChildChannel(CouchbaseChildChannel):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
channel_id: str,
|
|
17
|
+
lazy_load: bool = True,
|
|
18
|
+
):
|
|
19
|
+
"""
|
|
20
|
+
Args:
|
|
21
|
+
channel_id (str): Forecasted Wait Times Channel ID
|
|
22
|
+
lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
|
|
23
|
+
"""
|
|
24
|
+
super().__init__(channel_id, lazy_load=lazy_load)
|
|
25
|
+
|
|
26
|
+
def __repr__(self):
|
|
27
|
+
return f"{self.__class__.__name__}(channel_id='{self.channel_id}')"
|
|
28
|
+
|
|
29
|
+
@disney_property()
|
|
30
|
+
def name(self) -> Optional[str]:
|
|
31
|
+
"""The name of the entity.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
(Optional[str]): The name of the entity, or None if it was not found.
|
|
35
|
+
"""
|
|
36
|
+
return self._cb_data["name"]
|
|
37
|
+
|
|
38
|
+
@disney_property()
|
|
39
|
+
def character_id(self) -> Optional[str]:
|
|
40
|
+
"""The character ID of the entity. Strips the region from the channel ID.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
(Optional[str]): The character ID of the entity, or None if it was not found.
|
|
44
|
+
"""
|
|
45
|
+
return self._cb_data["characterId"]
|
|
46
|
+
|
|
47
|
+
@disney_property()
|
|
48
|
+
def description(self) -> Optional[str]:
|
|
49
|
+
"""The description of the entity.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
(Optional[str]): The description of the entity, or None if it was not found.
|
|
53
|
+
"""
|
|
54
|
+
return self._cb_data["description"]
|
|
55
|
+
|
|
56
|
+
@disney_property()
|
|
57
|
+
def thumbnail_url(self) -> Optional[str]:
|
|
58
|
+
"""The thumbnail URL of the entity.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
(Optional[str]): The thumbnail URL of the entity, or None if it was not found.
|
|
62
|
+
"""
|
|
63
|
+
return self._cb_data["thumbnailUrl"]
|
|
64
|
+
|
|
65
|
+
@disney_property()
|
|
66
|
+
def banner_url(self) -> Optional[str]:
|
|
67
|
+
"""The banner URL of the entity.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
(Optional[str]): The banner URL of the entity, or None if it was not found.
|
|
71
|
+
"""
|
|
72
|
+
return self._cb_data["bannerUrl"]
|
|
73
|
+
|
|
74
|
+
def get_appearances(self) -> list[dict]:
|
|
75
|
+
"""The current list of appearances of the character is scheduled for.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
(list[dict]): The list of appearances of the entity.
|
|
79
|
+
"""
|
|
80
|
+
self.refresh()
|
|
81
|
+
try:
|
|
82
|
+
if "appearances" not in self._cb_data:
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
_appearances = []
|
|
86
|
+
for i in self._cb_data["appearances"]:
|
|
87
|
+
tmp = {}
|
|
88
|
+
tmp["start_datetime"] = isoparse(i["startDateTime"]).astimezone(self._tz.value)
|
|
89
|
+
tmp["end_datetime"] = isoparse(i["endDateTime"]).astimezone(self._tz.value)
|
|
90
|
+
tmp["start_time_str"] = i["startTime"]
|
|
91
|
+
tmp["end_time_str"] = i["endTime"]
|
|
92
|
+
tmp["coordinates"] = {"latitude": i["latitude"], "longitude": i["longitude"]}
|
|
93
|
+
tmp["facility_id"] = i["facilityId"]
|
|
94
|
+
tmp["location_name"] = i["locationName"]
|
|
95
|
+
tmp["location_id"] = i["locationId"]
|
|
96
|
+
tmp["ancestor_land_id"] = i["ancestorLandId"]
|
|
97
|
+
tmp["ancestor_land_name"] = i["ancestorLandName"]
|
|
98
|
+
_appearances.append(tmp)
|
|
99
|
+
|
|
100
|
+
return sorted(_appearances, key=lambda i: i["start_datetime"])
|
|
101
|
+
|
|
102
|
+
except (KeyError, TypeError, ValueError):
|
|
103
|
+
logger.debug("No forecast found for %s", self.channel_id)
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class CharactersChannel(CouchbaseChannel):
|
|
108
|
+
def __init__(self, *args, **kwargs):
|
|
109
|
+
super().__init__(*args, **kwargs)
|
|
110
|
+
|
|
111
|
+
def get_children_channels(self) -> list[CharactersChildChannel]:
|
|
112
|
+
"""Gets a list of children channels for the channel.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
(list[CharactersChildChannel]): A list of CharactersChildChannel
|
|
116
|
+
"""
|
|
117
|
+
self.refresh()
|
|
118
|
+
channels = []
|
|
119
|
+
for i in self._cb_data["results"]:
|
|
120
|
+
if CouchbaseChannels.CHARACTERS in i["id"]:
|
|
121
|
+
channels.append(CharactersChildChannel(i["id"]))
|
|
122
|
+
return channels
|
|
@@ -8,7 +8,7 @@ class CouchbaseChannels(str, Enum):
|
|
|
8
8
|
FACILITIES = "facilities"
|
|
9
9
|
FACILITY_STATUS = "facilitystatus"
|
|
10
10
|
CALENDAR = "calendar"
|
|
11
|
-
|
|
11
|
+
FORECASTED_WAIT_TIMES = "forecastedwaittimes"
|
|
12
12
|
TODAY = "today"
|
|
13
13
|
CHARACTERS = "characters"
|
|
14
14
|
|
|
@@ -33,8 +33,8 @@ class WDWCouchbaseChannels(str, Enum):
|
|
|
33
33
|
"""Walt Disney World Facility Status Channel"""
|
|
34
34
|
CALENDAR = get_complete_channel_id(DestinationShort.WALT_DISNEY_WORLD, CouchbaseChannels.CALENDAR, "1_0", False)
|
|
35
35
|
"""Walt Disney World Calendar Channel"""
|
|
36
|
-
|
|
37
|
-
DestinationShort.WALT_DISNEY_WORLD, CouchbaseChannels.
|
|
36
|
+
FORECASTED_WAIT_TIMES = get_complete_channel_id(
|
|
37
|
+
DestinationShort.WALT_DISNEY_WORLD, CouchbaseChannels.FORECASTED_WAIT_TIMES, "1_0", True
|
|
38
38
|
)
|
|
39
39
|
"""Walt Disney World Forecasted Wait Time Channel"""
|
|
40
40
|
TODAY = get_complete_channel_id(DestinationShort.WALT_DISNEY_WORLD, CouchbaseChannels.TODAY, "1_0", False)
|
|
@@ -52,8 +52,8 @@ class DLRCouchbaseChannels(str, Enum):
|
|
|
52
52
|
"""Disneyland Resort Facility Status Channel"""
|
|
53
53
|
CALENDAR = get_complete_channel_id(DestinationShort.DISNEYLAND_RESORT, CouchbaseChannels.CALENDAR, "1_0", False)
|
|
54
54
|
"""Disneyland Resort Calendar Channel"""
|
|
55
|
-
|
|
56
|
-
DestinationShort.DISNEYLAND_RESORT, CouchbaseChannels.
|
|
55
|
+
FORECASTED_WAIT_TIMES = get_complete_channel_id(
|
|
56
|
+
DestinationShort.DISNEYLAND_RESORT, CouchbaseChannels.FORECASTED_WAIT_TIMES, "1_0", True
|
|
57
57
|
)
|
|
58
58
|
"""Disneyland Resort Forecasted Wait Time Channel"""
|
|
59
59
|
TODAY = get_complete_channel_id(DestinationShort.DISNEYLAND_RESORT, CouchbaseChannels.TODAY, "1_0", False)
|
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
2
3
|
import typing
|
|
3
4
|
from datetime import datetime, time, timedelta
|
|
4
|
-
from enum import Enum
|
|
5
5
|
from zoneinfo import ZoneInfo
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import httpx
|
|
8
8
|
|
|
9
9
|
from mousetools.api.menu import Menus
|
|
10
|
-
from mousetools.channels import CouchbaseChannel
|
|
10
|
+
from mousetools.channels import CouchbaseChannel, CouchbaseChildChannel
|
|
11
11
|
from mousetools.channels.calendar import CalendarChildChannel
|
|
12
12
|
from mousetools.channels.enums import (
|
|
13
13
|
CouchbaseChannels,
|
|
14
|
-
DestinationTimezones,
|
|
15
14
|
DLRCouchbaseChannels,
|
|
16
15
|
WDWCouchbaseChannels,
|
|
17
16
|
)
|
|
18
17
|
from mousetools.channels.facilitystatus import FacilityStatusChildChannel
|
|
18
|
+
from mousetools.channels.forecastedwaittimes import ForecastedWaitTimesChildChannel
|
|
19
19
|
from mousetools.decorators import disney_property
|
|
20
20
|
from mousetools.enums import DestinationShort, EntityType
|
|
21
|
-
from mousetools.mixins.couchbase import CouchbaseMixin
|
|
22
21
|
|
|
23
22
|
logger = logging.getLogger(__name__)
|
|
24
23
|
|
|
25
24
|
|
|
26
|
-
class FacilityChildChannel(
|
|
25
|
+
class FacilityChildChannel(CouchbaseChildChannel):
|
|
27
26
|
"""Base class for all Facility Child Channels. Subclasses like Attraction, Restaurant, Entertainment, etc. extend this class.
|
|
28
27
|
|
|
29
28
|
Example:
|
|
@@ -38,8 +37,6 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
38
37
|
{'latitude': 28.4160036778, 'longitude': -81.5811902834}
|
|
39
38
|
"""
|
|
40
39
|
|
|
41
|
-
_subclasses: typing.ClassVar[dict[str, type["FacilityChildChannel"]]] = {}
|
|
42
|
-
|
|
43
40
|
def __init__(
|
|
44
41
|
self,
|
|
45
42
|
channel_id: str,
|
|
@@ -50,27 +47,12 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
50
47
|
channel_id (str): Facility Channel ID
|
|
51
48
|
lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
|
|
52
49
|
"""
|
|
53
|
-
|
|
54
|
-
channel_id = channel_id.value
|
|
55
|
-
|
|
56
|
-
self.channel_id: str = channel_id
|
|
57
|
-
self.entity_id: str = channel_id.rsplit(".", 1)[-1]
|
|
50
|
+
super().__init__(channel_id, lazy_load=lazy_load)
|
|
58
51
|
|
|
59
|
-
self._destination_short: DestinationShort = self.channel_id.split(".")[0]
|
|
60
|
-
self._tz: DestinationTimezones = (
|
|
61
|
-
DestinationTimezones.WALT_DISNEY_WORLD
|
|
62
|
-
if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
|
|
63
|
-
else DestinationTimezones.DISNEYLAND_RESORT
|
|
64
|
-
)
|
|
65
52
|
self._refresh_interval: timedelta = timedelta(
|
|
66
53
|
hours=12
|
|
67
54
|
) # properties are rarely updated, no need to spam the API
|
|
68
55
|
|
|
69
|
-
self._cb_data: typing.Optional[dict] = None
|
|
70
|
-
self._cb_data_pull_time: typing.Optional[datetime] = datetime.now(tz=self._tz.value)
|
|
71
|
-
if not lazy_load:
|
|
72
|
-
self.refresh()
|
|
73
|
-
|
|
74
56
|
self._today_calendar: typing.Optional[CalendarChildChannel] = None
|
|
75
57
|
|
|
76
58
|
def refresh(self) -> None:
|
|
@@ -391,30 +373,6 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
391
373
|
|
|
392
374
|
return FacilityStatusChildChannel(facility_status_channel_id)
|
|
393
375
|
|
|
394
|
-
@property
|
|
395
|
-
def last_update(self) -> typing.Optional[datetime]:
|
|
396
|
-
"""
|
|
397
|
-
The last time the data was updated.
|
|
398
|
-
|
|
399
|
-
Returns:
|
|
400
|
-
(typing.Optional[datetime]): The last time the entity's data was updated, or None if no such data exists.
|
|
401
|
-
"""
|
|
402
|
-
if self._cb_data is None:
|
|
403
|
-
self._cb_data = self.get_channel_data(self.channel_id)
|
|
404
|
-
|
|
405
|
-
try:
|
|
406
|
-
tz = (
|
|
407
|
-
DestinationTimezones.WALT_DISNEY_WORLD
|
|
408
|
-
if "wdw" in self.channel_id
|
|
409
|
-
else DestinationTimezones.DISNEYLAND_RESORT
|
|
410
|
-
)
|
|
411
|
-
dt = isoparse(self._cb_data["lastUpdate"])
|
|
412
|
-
dt = dt.replace(tzinfo=tz.value)
|
|
413
|
-
return dt
|
|
414
|
-
except (KeyError, TypeError, ValueError):
|
|
415
|
-
logger.debug("No last updated found for %s", self.channel_id)
|
|
416
|
-
return self._cb_data_pull_time
|
|
417
|
-
|
|
418
376
|
@disney_property()
|
|
419
377
|
def sponsor_name(self) -> typing.Optional[str]:
|
|
420
378
|
"""The sponsor name of the entity.
|
|
@@ -434,16 +392,32 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
434
392
|
"""
|
|
435
393
|
return self._cb_data["mealPeriods"]
|
|
436
394
|
|
|
437
|
-
|
|
438
|
-
def menu(self) -> Menus:
|
|
395
|
+
def get_menu(self) -> Menus:
|
|
439
396
|
"""
|
|
440
397
|
The menu of the restaurant.
|
|
441
398
|
|
|
442
399
|
Returns:
|
|
443
|
-
(
|
|
400
|
+
(Menus): The menu of the restaurant.
|
|
444
401
|
"""
|
|
445
402
|
return Menus(self.channel_id)
|
|
446
403
|
|
|
404
|
+
def get_forecasted_wait_times_channel(self) -> ForecastedWaitTimesChildChannel:
|
|
405
|
+
"""Get the forecasted wait times channel for this entity.
|
|
406
|
+
|
|
407
|
+
Returns:
|
|
408
|
+
(ForecastedWaitTimesChildChannel): The forecasted wait times channel
|
|
409
|
+
|
|
410
|
+
Example:
|
|
411
|
+
```python
|
|
412
|
+
>>> f = FacilityChildChannel("wdw.facilities.1_0.en_us.attraction.80010177;entityType=Attraction", lazy_load=False)
|
|
413
|
+
>>> fwt = f.get_forecasted_wait_times_channel()
|
|
414
|
+
>>> fwt.get_forecast()
|
|
415
|
+
[{'forecasted_wait_minutes': 85, 'bar_graph_percentage': 70, 'accessibility_label': 'High', 'timestamp': datetime.datetime(2025, 5, 27, 9, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))}, ...]
|
|
416
|
+
```
|
|
417
|
+
"""
|
|
418
|
+
channel_id = f"{WDWCouchbaseChannels.FORECASTED_WAIT_TIMES.value if self._destination_short == DestinationShort.WALT_DISNEY_WORLD else DLRCouchbaseChannels.FORECASTED_WAIT_TIMES.value}.{self.entity_id}"
|
|
419
|
+
return ForecastedWaitTimesChildChannel(channel_id)
|
|
420
|
+
|
|
447
421
|
@disney_property()
|
|
448
422
|
def detail_image_url(self) -> typing.Optional[str]:
|
|
449
423
|
"""The detail image url of the entity.
|
|
@@ -503,6 +477,41 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
503
477
|
closed = self._today_calendar.get_closed(self.entity_id)
|
|
504
478
|
return bool(refurb or closed)
|
|
505
479
|
|
|
480
|
+
def get_current_weather(self, api_key: typing.Optional[str] = None) -> dict:
|
|
481
|
+
"""Get the current weather for the facility using the OpenWeatherMap API. https://openweathermap.org
|
|
482
|
+
|
|
483
|
+
Args:
|
|
484
|
+
api_key (typing.Optional[str], optional): The API key to use. Defaults to None. If None, will use the MT_OPEN_WEATHER_API_KEY environment variable.
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
(dict): The current weather for the facility.
|
|
488
|
+
"""
|
|
489
|
+
|
|
490
|
+
weather_key = api_key or os.environ.get("MT_OPEN_WEATHER_API_KEY")
|
|
491
|
+
|
|
492
|
+
if weather_key is None:
|
|
493
|
+
logger.warning("No weather API key found")
|
|
494
|
+
return {}
|
|
495
|
+
|
|
496
|
+
coordinates = self.coordinates
|
|
497
|
+
if coordinates is None:
|
|
498
|
+
return {}
|
|
499
|
+
|
|
500
|
+
weather_url = "https://api.openweathermap.org/data/2.5/weather"
|
|
501
|
+
params = {
|
|
502
|
+
"lat": coordinates["latitude"],
|
|
503
|
+
"lon": coordinates["longitude"],
|
|
504
|
+
"appid": weather_key,
|
|
505
|
+
}
|
|
506
|
+
try:
|
|
507
|
+
response = httpx.get(weather_url, params=params)
|
|
508
|
+
response.raise_for_status()
|
|
509
|
+
data = response.json()
|
|
510
|
+
return data
|
|
511
|
+
except httpx.HTTPError as err:
|
|
512
|
+
logger.error("Request failed: %s", err)
|
|
513
|
+
return {}
|
|
514
|
+
|
|
506
515
|
def __str__(self) -> str:
|
|
507
516
|
return f"{self.entity_type}: {self.name} ({self.channel_id})"
|
|
508
517
|
|
|
@@ -514,38 +523,32 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
514
523
|
return self.channel_id == other.channel_id
|
|
515
524
|
return False
|
|
516
525
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
SpaFacilityChild = FacilityChildChannel.get_subclass("SpaFacilityChild")
|
|
544
|
-
ThemeParkFacilityChild = FacilityChildChannel.get_subclass("ThemeParkFacilityChild")
|
|
545
|
-
TourFacilityChild = FacilityChildChannel.get_subclass("TourFacilityChild")
|
|
546
|
-
TransportationFacilityChild = FacilityChildChannel.get_subclass("TransportationFacilityChild")
|
|
547
|
-
WaterParkFacilityChild = FacilityChildChannel.get_subclass("WaterParkFacilityChild")
|
|
548
|
-
RestaurantFacilityChild = FacilityChildChannel.get_subclass("RestaurantFacilityChild")
|
|
526
|
+
|
|
527
|
+
AttractionFacilityChild = type("AttractionFacilityChild", (FacilityChildChannel,), {})
|
|
528
|
+
AudioTourFacilityChild = type("AudioTourFacilityChild", (FacilityChildChannel,), {})
|
|
529
|
+
BuildingFacilityChild = type("BuildingFacilityChild", (FacilityChildChannel,), {})
|
|
530
|
+
BusStopFacilityChild = type("BusStopFacilityChild", (FacilityChildChannel,), {})
|
|
531
|
+
DestinationFacilityChild = type("DestinationFacilityChild", (FacilityChildChannel,), {})
|
|
532
|
+
DiningEventFacilityChild = type("DiningEventFacilityChild", (FacilityChildChannel,), {})
|
|
533
|
+
DinnerShowFacilityChild = type("DinnerShowFacilityChild", (FacilityChildChannel,), {})
|
|
534
|
+
EntertainmentFacilityChild = type("EntertainmentFacilityChild", (FacilityChildChannel,), {})
|
|
535
|
+
EntertainmentVenueFacilityChild = type("EntertainmentVenueFacilityChild", (FacilityChildChannel,), {})
|
|
536
|
+
WaterParkFacilityChild = type("WaterParkFacilityChild", (FacilityChildChannel,), {})
|
|
537
|
+
GuestServiceFacilityChild = type("GuestServiceFacilityChild", (FacilityChildChannel,), {})
|
|
538
|
+
LandFacilityChild = type("LandFacilityChild", (FacilityChildChannel,), {})
|
|
539
|
+
MerchandiseFacilityChild = type("MerchandiseFacilityChild", (FacilityChildChannel,), {})
|
|
540
|
+
PhotopassFacilityChild = type("PhotopassFacilityChild", (FacilityChildChannel,), {})
|
|
541
|
+
PointOfInterestFacilityChild = type("PointOfInterestFacilityChild", (FacilityChildChannel,), {})
|
|
542
|
+
RecreationFacilityChild = type("RecreationFacilityChild", (FacilityChildChannel,), {})
|
|
543
|
+
RecreationActivityFacilityChild = type("RecreationActivityFacilityChild", (FacilityChildChannel,), {})
|
|
544
|
+
ResortFacilityChild = type("ResortFacilityChild", (FacilityChildChannel,), {})
|
|
545
|
+
ResortAreaFacilityChild = type("ResortAreaFacilityChild", (FacilityChildChannel,), {})
|
|
546
|
+
SpaFacilityChild = type("SpaFacilityChild", (FacilityChildChannel,), {})
|
|
547
|
+
ThemeParkFacilityChild = type("ThemeParkFacilityChild", (FacilityChildChannel,), {})
|
|
548
|
+
TourFacilityChild = type("TourFacilityChild", (FacilityChildChannel,), {})
|
|
549
|
+
TransportationFacilityChild = type("TransportationFacilityChild", (FacilityChildChannel,), {})
|
|
550
|
+
WaterParkFacilityChild = type("WaterParkFacilityChild", (FacilityChildChannel,), {})
|
|
551
|
+
RestaurantFacilityChild = type("RestaurantFacilityChild", (FacilityChildChannel,), {})
|
|
549
552
|
|
|
550
553
|
|
|
551
554
|
class FacilityChannel(CouchbaseChannel):
|
|
@@ -1,47 +1,25 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import typing
|
|
3
|
-
from datetime import datetime
|
|
3
|
+
from datetime import datetime
|
|
4
4
|
|
|
5
5
|
from dateutil.parser import isoparse
|
|
6
6
|
|
|
7
|
-
from mousetools.channels import CouchbaseChannel
|
|
8
|
-
from mousetools.channels.enums import CouchbaseChannels
|
|
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
|
|
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(
|
|
17
|
-
_subclasses: typing.ClassVar[dict[str, type["FacilityStatusChildChannel"]]] = {}
|
|
18
|
-
|
|
15
|
+
class FacilityStatusChildChannel(CouchbaseChildChannel):
|
|
19
16
|
def __init__(self, channel_id: str, lazy_load: bool = True):
|
|
20
17
|
"""
|
|
21
18
|
Args:
|
|
22
19
|
channel_id (typing.Union[WDWCouchbaseChannels, DLRCouchbaseChannels]): Channel ID from the enum
|
|
23
20
|
lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
|
|
24
21
|
"""
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
self._destination_short: DestinationShort = channel_id.split(".")[0]
|
|
28
|
-
self._tz: DestinationTimezones = (
|
|
29
|
-
DestinationTimezones.WALT_DISNEY_WORLD
|
|
30
|
-
if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
|
|
31
|
-
else DestinationTimezones.DISNEYLAND_RESORT
|
|
32
|
-
)
|
|
33
|
-
self._refresh_interval: timedelta = timedelta(minutes=10)
|
|
34
|
-
self._cb_data: typing.Optional[dict] = None
|
|
35
|
-
self._cb_data_pull_time: typing.Optional[datetime] = datetime.now(tz=self._tz.value)
|
|
36
|
-
|
|
37
|
-
if not lazy_load:
|
|
38
|
-
self.refresh()
|
|
39
|
-
|
|
40
|
-
def refresh(self) -> None:
|
|
41
|
-
"""Pulls initial data if none exists or if it is older than the refresh interval"""
|
|
42
|
-
if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
|
|
43
|
-
self._cb_data = self.get_channel_data(self.channel_id)
|
|
44
|
-
self._cb_data_pull_time = datetime.now(tz=self._tz.value)
|
|
22
|
+
super().__init__(channel_id, lazy_load=lazy_load)
|
|
45
23
|
|
|
46
24
|
def get_status(self) -> typing.Optional[str]:
|
|
47
25
|
"""Get the operating status of the facility
|
|
@@ -71,24 +49,6 @@ class FacilityStatusChildChannel(CouchbaseMixin):
|
|
|
71
49
|
logger.debug("No wait time found for %s", self.channel_id)
|
|
72
50
|
return None
|
|
73
51
|
|
|
74
|
-
@property
|
|
75
|
-
def last_update(self) -> typing.Optional[datetime]:
|
|
76
|
-
"""The last time the data was updated.
|
|
77
|
-
|
|
78
|
-
Returns:
|
|
79
|
-
(typing.Optional[datetime]): The last time the entity's data was updated, or None if no such data exists.
|
|
80
|
-
"""
|
|
81
|
-
if self._cb_data is None:
|
|
82
|
-
self._cb_data = self.get_channel_data(self.channel_id)
|
|
83
|
-
|
|
84
|
-
try:
|
|
85
|
-
dt = isoparse(self._cb_data["lastUpdate"])
|
|
86
|
-
dt = dt.replace(tzinfo=self._tz.value)
|
|
87
|
-
return dt
|
|
88
|
-
except (KeyError, TypeError, ValueError):
|
|
89
|
-
logger.debug("No last updated found for %s", self.channel_id)
|
|
90
|
-
return self._cb_data_pull_time
|
|
91
|
-
|
|
92
52
|
@disney_property()
|
|
93
53
|
def fast_pass_available(self) -> typing.Optional[bool]:
|
|
94
54
|
return self._cb_data["fastPassAvailable"]
|
|
@@ -132,20 +92,14 @@ class FacilityStatusChildChannel(CouchbaseMixin):
|
|
|
132
92
|
return self.channel_id == other.channel_id
|
|
133
93
|
return False
|
|
134
94
|
|
|
135
|
-
@classmethod
|
|
136
|
-
def get_subclass(cls, type_name: str) -> type["FacilityStatusChildChannel"]:
|
|
137
|
-
if type_name not in cls._subclasses:
|
|
138
|
-
cls._subclasses[type_name] = type(type_name, (cls,), {"__doc__": f"Class for {type_name} Entities."})
|
|
139
|
-
return cls._subclasses[type_name]
|
|
140
95
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
WaterParkFacilityStatusChild = FacilityStatusChildChannel.get_subclass("WaterParkFacilityStatusChild")
|
|
96
|
+
AttractionFacilityStatusChild = type("AttractionFacilityStatusChild", (FacilityStatusChildChannel,), {})
|
|
97
|
+
EntertainmentFacilityStatusChild = type("EntertainmentFacilityStatusChild", (FacilityStatusChildChannel,), {})
|
|
98
|
+
EntertainmentVenueFacilityStatusChild = type("EntertainmentVenueFacilityStatusChild", (FacilityStatusChildChannel,), {})
|
|
99
|
+
LandFacilityStatusChild = type("LandFacilityStatusChild", (FacilityStatusChildChannel,), {})
|
|
100
|
+
RestaurantFacilityStatusChild = type("RestaurantFacilityStatusChild", (FacilityStatusChildChannel,), {})
|
|
101
|
+
ThemeParkFacilityStatusChild = type("ThemeParkFacilityStatusChild", (FacilityStatusChildChannel,), {})
|
|
102
|
+
WaterParkFacilityStatusChild = type("WaterParkFacilityStatusChild", (FacilityStatusChildChannel,), {})
|
|
149
103
|
|
|
150
104
|
|
|
151
105
|
class FacilityStatusChannel(CouchbaseChannel):
|
|
@@ -165,6 +119,25 @@ class FacilityStatusChannel(CouchbaseChannel):
|
|
|
165
119
|
channels.append(FacilityStatusChildChannel(i["id"]))
|
|
166
120
|
return channels
|
|
167
121
|
|
|
122
|
+
def get_all_statuses(self) -> dict[str, dict[str, typing.Any]]:
|
|
123
|
+
"""Get all statuses for the channel
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
(dict[str, dict[str, typing.Any]]): A dictionary of all statuses for the channel
|
|
127
|
+
"""
|
|
128
|
+
logger.info("Getting all children statuses for %s", self.channel_id)
|
|
129
|
+
children = self.get_children_channels()
|
|
130
|
+
logger.debug("Got %d children for %s", len(children), self.channel_id)
|
|
131
|
+
|
|
132
|
+
statuses = {}
|
|
133
|
+
for child in children:
|
|
134
|
+
tmp = {}
|
|
135
|
+
tmp["status"] = child.get_status()
|
|
136
|
+
tmp["wait_time"] = child.get_wait_time()
|
|
137
|
+
statuses[child.entity_id] = tmp
|
|
138
|
+
|
|
139
|
+
return statuses
|
|
140
|
+
|
|
168
141
|
def get_children_attractions(self) -> list[AttractionFacilityStatusChild]: # type: ignore
|
|
169
142
|
"""Get AttractionFacilityStatusChild Children of a Facility
|
|
170
143
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from dateutil.parser import isoparse
|
|
4
|
+
|
|
5
|
+
from mousetools.channels import CouchbaseChannel, CouchbaseChildChannel
|
|
6
|
+
from mousetools.channels.enums import CouchbaseChannels
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ForecastedWaitTimesChildChannel(CouchbaseChildChannel):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
channel_id: str,
|
|
15
|
+
lazy_load: bool = True,
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Args:
|
|
19
|
+
channel_id (str): Forecasted Wait Times Channel ID
|
|
20
|
+
lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
|
|
21
|
+
"""
|
|
22
|
+
super().__init__(channel_id, lazy_load=lazy_load)
|
|
23
|
+
|
|
24
|
+
def __repr__(self):
|
|
25
|
+
return f"{self.__class__.__name__}(channel_id='{self.channel_id}')"
|
|
26
|
+
|
|
27
|
+
def get_forecast(self) -> list[dict]:
|
|
28
|
+
"""Returns a list of forecasted wait times sorted by timestamp
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
(list[dict]): A list of dictionaries. Each dictionary contains forecasted wait minutes, bar graph percentage, accessibility label, and timestamp
|
|
32
|
+
"""
|
|
33
|
+
self.refresh()
|
|
34
|
+
try:
|
|
35
|
+
if "forecasts" not in self._cb_data:
|
|
36
|
+
return []
|
|
37
|
+
|
|
38
|
+
forecasts = []
|
|
39
|
+
for forecast in self._cb_data["forecasts"]:
|
|
40
|
+
tmp = {}
|
|
41
|
+
dt = isoparse(forecast["timestamp"])
|
|
42
|
+
dt = dt.astimezone(self._tz.value)
|
|
43
|
+
tmp["forecasted_wait_minutes"] = forecast["forecastedWaitMinutes"]
|
|
44
|
+
tmp["bar_graph_percentage"] = forecast["percentage"]
|
|
45
|
+
tmp["accessibility_label"] = forecast["accessibility12h"].split(" ", 1)[-1]
|
|
46
|
+
tmp["timestamp"] = dt
|
|
47
|
+
forecasts.append(tmp)
|
|
48
|
+
|
|
49
|
+
return sorted(forecasts, key=lambda i: i["timestamp"])
|
|
50
|
+
|
|
51
|
+
except (KeyError, TypeError, ValueError):
|
|
52
|
+
logger.debug("No forecast found for %s", self.channel_id)
|
|
53
|
+
return []
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ForecastedWaitTimesChannel(CouchbaseChannel):
|
|
57
|
+
def __init__(self, *args, **kwargs):
|
|
58
|
+
super().__init__(*args, **kwargs)
|
|
59
|
+
|
|
60
|
+
def get_children_channels(self) -> list[ForecastedWaitTimesChildChannel]:
|
|
61
|
+
"""Gets a list of children channels for the channel.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
(list[ForecastedWaitTimesChildChannel]): A list of ForecastedWaitTimesChildChannel
|
|
65
|
+
"""
|
|
66
|
+
self.refresh()
|
|
67
|
+
channels = []
|
|
68
|
+
for i in self._cb_data["results"]:
|
|
69
|
+
if CouchbaseChannels.FORECASTED_WAIT_TIMES in i["id"]:
|
|
70
|
+
channels.append(ForecastedWaitTimesChildChannel(i["id"]))
|
|
71
|
+
return channels
|
|
@@ -7,9 +7,10 @@ logger = logging.getLogger(__name__)
|
|
|
7
7
|
T = TypeVar("T")
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def disney_property(
|
|
10
|
+
def disney_property(
|
|
11
|
+
refresh: bool = True, default_value: Optional[T] = None
|
|
12
|
+
) -> Callable[[Callable[[Any], Optional[T]]], property]:
|
|
11
13
|
def decorator(func: Callable) -> property:
|
|
12
|
-
@property
|
|
13
14
|
@functools.wraps(func)
|
|
14
15
|
def wrapper(self) -> Optional[T]:
|
|
15
16
|
if refresh:
|
|
@@ -19,7 +20,9 @@ def disney_property(refresh: bool = True, default_value: Optional[T] = None) ->
|
|
|
19
20
|
except (KeyError, TypeError, ValueError):
|
|
20
21
|
return default_value
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
wrapper.__annotations__ = func.__annotations__
|
|
24
|
+
wrapper.__doc__ = func.__doc__
|
|
25
|
+
return property(wrapper)
|
|
23
26
|
|
|
24
27
|
return decorator
|
|
25
28
|
|
|
@@ -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]:
|
|
@@ -16,9 +16,11 @@ mousetools/api/search.py
|
|
|
16
16
|
mousetools/channels/__init__.py
|
|
17
17
|
mousetools/channels/enums.py
|
|
18
18
|
mousetools/channels/calendar/__init__.py
|
|
19
|
+
mousetools/channels/characters/__init__.py
|
|
19
20
|
mousetools/channels/facilities/__init__.py
|
|
20
21
|
mousetools/channels/facilities/enums.py
|
|
21
22
|
mousetools/channels/facilitystatus/__init__.py
|
|
23
|
+
mousetools/channels/forecastedwaittimes/__init__.py
|
|
22
24
|
mousetools/mixins/__init__.py
|
|
23
25
|
mousetools/mixins/couchbase.py
|
|
24
26
|
mousetools/mixins/disney.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mousetools"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.3.0"
|
|
4
4
|
authors = [{name = "Scott Caratozzolo"}]
|
|
5
5
|
description = "An unofficial Python wrapper for Disney's API"
|
|
6
6
|
readme = "README.md"
|
|
@@ -42,6 +42,7 @@ dev-dependencies = [
|
|
|
42
42
|
"go-task-bin>=3.43.3",
|
|
43
43
|
"icecream>=2.1.4",
|
|
44
44
|
"pytest-mock>=3.14.0",
|
|
45
|
+
"pytest-httpx>=0.35.0",
|
|
45
46
|
]
|
|
46
47
|
|
|
47
48
|
[build-system]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|