MouseTools 4.4.2__tar.gz → 4.5.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.
- {mousetools-4.4.2 → mousetools-4.5.1}/PKG-INFO +2 -1
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/channels/__init__.py +10 -0
- mousetools-4.5.1/mousetools/channels/manager.py +113 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/mixins/couchbase.py +43 -15
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools.egg-info/PKG-INFO +2 -1
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools.egg-info/SOURCES.txt +2 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools.egg-info/requires.txt +1 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/pyproject.toml +2 -1
- mousetools-4.5.1/tests/test_manager.py +28 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/LICENSE +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/README.md +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/__init__.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/api/__init__.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/api/menu.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/api/search.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/auth.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/channels/calendar/__init__.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/channels/characters/__init__.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/channels/enums.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/channels/facilities/__init__.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/channels/facilities/enums.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/channels/facilitystatus/__init__.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/channels/forecastedwaittimes/__init__.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/channels/today/__init__.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/decorators.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/enums.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/mixins/__init__.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/mixins/disney.py +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools/resources/environments.bin +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools.egg-info/dependency_links.txt +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/mousetools.egg-info/top_level.txt +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/setup.cfg +0 -0
- {mousetools-4.4.2 → mousetools-4.5.1}/tests/test_mixins.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mousetools
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.5.1
|
|
4
4
|
Summary: An unofficial Python wrapper for Disney's API
|
|
5
5
|
Author: Scott Caratozzolo
|
|
6
6
|
License-Expression: MIT
|
|
@@ -17,6 +17,7 @@ Classifier: Operating System :: OS Independent
|
|
|
17
17
|
Requires-Python: <4.0,>=3.9
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
|
+
Requires-Dist: more-itertools>=10.7.0
|
|
20
21
|
Requires-Dist: pycryptodome>=3.22.0
|
|
21
22
|
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
22
23
|
Requires-Dist: requests>=2.32.3
|
|
@@ -47,6 +47,11 @@ class CouchbaseChildChannel(CouchbaseMixin):
|
|
|
47
47
|
self._cb_data = self.get_channel_data(self.channel_id)
|
|
48
48
|
self._cb_data_pull_time = datetime.now(tz=self._tz.value)
|
|
49
49
|
|
|
50
|
+
def _refresh_from_manager(self, cb_data: dict) -> None:
|
|
51
|
+
logger.info("Refreshing new data from manager for %s", self.channel_id)
|
|
52
|
+
self._cb_data = cb_data
|
|
53
|
+
self._cb_data_pull_time = datetime.now(tz=self._tz.value)
|
|
54
|
+
|
|
50
55
|
@disney_property()
|
|
51
56
|
def last_update(self) -> Optional[datetime]:
|
|
52
57
|
"""
|
|
@@ -101,6 +106,11 @@ class CouchbaseChannel(CouchbaseMixin):
|
|
|
101
106
|
self._cb_data = self.get_channel_changes(self.channel_id)
|
|
102
107
|
self._cb_data_pull_time = datetime.now(tz=self._tz.value)
|
|
103
108
|
|
|
109
|
+
def _refresh_from_manager(self, cb_data: dict) -> None:
|
|
110
|
+
logger.info("Refreshing new data from manager for %s", self.channel_id)
|
|
111
|
+
self._cb_data = cb_data
|
|
112
|
+
self._cb_data_pull_time = datetime.now(tz=self._tz.value)
|
|
113
|
+
|
|
104
114
|
def get_children_channel_ids(self) -> list[str]:
|
|
105
115
|
"""Returns a list of child channel ids for the channel."""
|
|
106
116
|
self.refresh()
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional, Union
|
|
3
|
+
|
|
4
|
+
from more_itertools import batched
|
|
5
|
+
|
|
6
|
+
from mousetools.channels import CouchbaseChannel, CouchbaseChildChannel
|
|
7
|
+
from mousetools.mixins.couchbase import CouchbaseMixin
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ChannelManager(CouchbaseMixin):
|
|
13
|
+
def __init__(self, channels: list[Union[CouchbaseChannel, CouchbaseChildChannel]]) -> None:
|
|
14
|
+
self._channel_store = {}
|
|
15
|
+
for channel in channels:
|
|
16
|
+
self._channel_store[channel.channel_id] = channel
|
|
17
|
+
|
|
18
|
+
def add_channel(self, channel: Union[CouchbaseChannel, CouchbaseChildChannel]) -> None:
|
|
19
|
+
"""Adds a channel to the manager
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
channel (Union[CouchbaseChannel, CouchbaseChildChannel]): Channel to add
|
|
23
|
+
"""
|
|
24
|
+
self._channel_store[channel.channel_id] = channel
|
|
25
|
+
|
|
26
|
+
def get_channel(self, channel_id: str) -> Union[CouchbaseChannel, CouchbaseChildChannel]:
|
|
27
|
+
"""Returns a channel from the manager
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
channel_id (str): Channel ID
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Union[CouchbaseChannel, CouchbaseChildChannel]: The channel
|
|
34
|
+
"""
|
|
35
|
+
return self._channel_store[channel_id]
|
|
36
|
+
|
|
37
|
+
def get_all_channels(self) -> list[Union[CouchbaseChannel, CouchbaseChildChannel]]:
|
|
38
|
+
"""Returns a list of all channels in the manager
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
list[Union[CouchbaseChannel, CouchbaseChildChannel]]: List of channels
|
|
42
|
+
"""
|
|
43
|
+
return list(self._channel_store.values())
|
|
44
|
+
|
|
45
|
+
def get_channel_ids(self) -> list[str]:
|
|
46
|
+
"""Returns a list of all channel ids in the manager
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
list[str]: List of channel ids
|
|
50
|
+
"""
|
|
51
|
+
return list(self._channel_store.keys())
|
|
52
|
+
|
|
53
|
+
def remove_channel(
|
|
54
|
+
self, channel_id: str, return_channel: bool = False
|
|
55
|
+
) -> Optional[Union[CouchbaseChannel, CouchbaseChildChannel]]:
|
|
56
|
+
"""Removes a channel from the manager
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
channel_id (str): Channel ID
|
|
60
|
+
return_channel (bool, optional): If True, will return the channel. Defaults to False.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Optional[Union[CouchbaseChannel, CouchbaseChildChannel]]: The channel
|
|
64
|
+
"""
|
|
65
|
+
channel = self._channel_store.pop(channel_id)
|
|
66
|
+
if return_channel:
|
|
67
|
+
return channel
|
|
68
|
+
|
|
69
|
+
def refresh(self) -> None:
|
|
70
|
+
"""Refreshes all channels in the manager
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
ValueError: If the channel is not a child or parent channel
|
|
74
|
+
"""
|
|
75
|
+
children_channels: dict[str, CouchbaseChildChannel] = {}
|
|
76
|
+
parent_channels: dict[str, CouchbaseChannel] = {}
|
|
77
|
+
|
|
78
|
+
for channel_id, channel in self._channel_store.items():
|
|
79
|
+
if isinstance(channel, CouchbaseChildChannel):
|
|
80
|
+
children_channels[channel_id] = channel
|
|
81
|
+
elif isinstance(channel, CouchbaseChannel):
|
|
82
|
+
parent_channels[channel_id] = channel
|
|
83
|
+
else:
|
|
84
|
+
raise ValueError(f"Channel {channel} is not a child or parent channel")
|
|
85
|
+
logger.info(
|
|
86
|
+
"Refreshing %d children channels and %d parent channels", len(children_channels), len(parent_channels)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
self._refresh_children(children_channels)
|
|
90
|
+
|
|
91
|
+
self._refresh_parents(parent_channels)
|
|
92
|
+
|
|
93
|
+
def _refresh_children(self, children_channels: dict[str, CouchbaseChildChannel]) -> None:
|
|
94
|
+
if children_channels:
|
|
95
|
+
logger.debug("Refreshing children channels")
|
|
96
|
+
for _channels in batched(children_channels, 100):
|
|
97
|
+
children_results = self.get_channel_data(_channels)
|
|
98
|
+
for channel_id, result in zip(_channels, children_results):
|
|
99
|
+
children_channels[channel_id]._refresh_from_manager(result)
|
|
100
|
+
|
|
101
|
+
def _refresh_parents(self, parent_channels: dict[str, CouchbaseChannel]) -> None:
|
|
102
|
+
if parent_channels:
|
|
103
|
+
logger.debug("Refreshing parent channels")
|
|
104
|
+
for _channels in batched(parent_channels, 100):
|
|
105
|
+
parent_results = self.get_channel_changes(_channels)
|
|
106
|
+
for channel_id in _channels:
|
|
107
|
+
copy_results = parent_results.copy()
|
|
108
|
+
new_results = []
|
|
109
|
+
for result in copy_results["results"]:
|
|
110
|
+
if channel_id in result["id"]:
|
|
111
|
+
new_results.append(result)
|
|
112
|
+
copy_results["results"] = new_results
|
|
113
|
+
parent_channels[channel_id]._refresh_from_manager(copy_results)
|
|
@@ -14,28 +14,39 @@ class CouchbaseMixin:
|
|
|
14
14
|
_couchbase_url = "https://" + auth_obj._environments["syncGatewayUrl"].split("//")[1].rstrip("/")
|
|
15
15
|
_headers: dict = auth_obj.get_couchbase_headers()
|
|
16
16
|
|
|
17
|
-
def get_channel_data(self, channel_name: str) -> Union[dict, None]:
|
|
17
|
+
def get_channel_data(self, channel_name: Union[str, list[str]]) -> Union[list[dict], dict, None]:
|
|
18
18
|
"""
|
|
19
19
|
Sends a request to the Disney Couchbase API with the given channel ID and returns the data.
|
|
20
20
|
|
|
21
21
|
Args:
|
|
22
|
-
channel_name (str): Couchbase channel to request data from.
|
|
22
|
+
channel_name (Union[str, list[str]]): Couchbase channel(s) to request data from.
|
|
23
23
|
|
|
24
24
|
Returns:
|
|
25
|
-
(dict): The disney data.
|
|
25
|
+
(Union[list[dict], dict, None]): The disney data.
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
+
if isinstance(channel_name, str):
|
|
29
|
+
_channel_names = [channel_name]
|
|
30
|
+
return_list = False
|
|
31
|
+
else:
|
|
32
|
+
_channel_names = channel_name
|
|
33
|
+
return_list = True
|
|
34
|
+
|
|
28
35
|
payload = {
|
|
29
|
-
"docs": [
|
|
30
|
-
{"id": channel_name},
|
|
31
|
-
],
|
|
36
|
+
"docs": [{"id": name} for name in _channel_names],
|
|
32
37
|
"json": True,
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
logger.info(
|
|
40
|
+
logger.info(
|
|
41
|
+
"Sending request for channel data: %s", f"{len(channel_name)} channels" if return_list else _channel_names
|
|
42
|
+
)
|
|
36
43
|
response = requests.post(self._couchbase_url + "/_bulk_get", json=payload, headers=self._headers, timeout=10)
|
|
37
|
-
logger.debug(
|
|
38
|
-
|
|
44
|
+
logger.debug(
|
|
45
|
+
"Response status for %s: %s",
|
|
46
|
+
f"{len(channel_name)} channels" if return_list else _channel_names,
|
|
47
|
+
response.status_code,
|
|
48
|
+
)
|
|
49
|
+
results = []
|
|
39
50
|
try:
|
|
40
51
|
response.raise_for_status()
|
|
41
52
|
raw_data = response.text
|
|
@@ -48,6 +59,7 @@ class CouchbaseMixin:
|
|
|
48
59
|
if line != "" and line[0] != "-":
|
|
49
60
|
try:
|
|
50
61
|
data = json.loads(line)
|
|
62
|
+
results.append(data)
|
|
51
63
|
except Exception:
|
|
52
64
|
logger.debug("Failed to parse line: %s", line)
|
|
53
65
|
continue
|
|
@@ -55,21 +67,30 @@ class CouchbaseMixin:
|
|
|
55
67
|
logger.error("Request failed: %s", err)
|
|
56
68
|
return None
|
|
57
69
|
|
|
58
|
-
|
|
70
|
+
if return_list:
|
|
71
|
+
return results
|
|
72
|
+
else:
|
|
73
|
+
return results[0]
|
|
59
74
|
|
|
60
|
-
def get_channel_changes(
|
|
75
|
+
def get_channel_changes(
|
|
76
|
+
self, channel_name: Union[str, list[str]], since: Union[int, None] = None
|
|
77
|
+
) -> Union[dict, None]:
|
|
61
78
|
"""Sends a request to the Disney Couchbase API with the given channel ID and returns the data.
|
|
62
79
|
|
|
63
80
|
Args:
|
|
64
|
-
channel_name (str): Couchbase channel to request data from.
|
|
81
|
+
channel_name (Union[str, list[str]]): Couchbase channel to request data from.
|
|
65
82
|
since (Union[int, None], optional): Since when to get changes. Defaults to None.
|
|
66
83
|
|
|
67
84
|
Returns:
|
|
68
85
|
Union[dict, None]: The disney data.
|
|
69
86
|
"""
|
|
87
|
+
|
|
88
|
+
if isinstance(channel_name, str):
|
|
89
|
+
channel_name = [channel_name]
|
|
90
|
+
|
|
70
91
|
try:
|
|
71
92
|
payload = {
|
|
72
|
-
"channels": channel_name,
|
|
93
|
+
"channels": ",".join(channel_name),
|
|
73
94
|
"style": "all_docs",
|
|
74
95
|
"filter": "sync_gateway/bychannel",
|
|
75
96
|
"feed": "normal",
|
|
@@ -78,9 +99,16 @@ class CouchbaseMixin:
|
|
|
78
99
|
if since:
|
|
79
100
|
payload["since"] = since
|
|
80
101
|
|
|
81
|
-
logger.info(
|
|
102
|
+
logger.info(
|
|
103
|
+
"Sending request for channel changes: %s",
|
|
104
|
+
f"{len(channel_name)} channels" if len(channel_name) > 1 else channel_name[0],
|
|
105
|
+
)
|
|
82
106
|
response = requests.post(self._couchbase_url + "/_changes", json=payload, headers=self._headers, timeout=10)
|
|
83
|
-
logger.debug(
|
|
107
|
+
logger.debug(
|
|
108
|
+
"Response status for %s: %s",
|
|
109
|
+
f"{len(channel_name)} channels" if len(channel_name) > 1 else channel_name[0],
|
|
110
|
+
response.status_code,
|
|
111
|
+
)
|
|
84
112
|
response.raise_for_status()
|
|
85
113
|
data = response.json()
|
|
86
114
|
except requests.HTTPError as err:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mousetools
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.5.1
|
|
4
4
|
Summary: An unofficial Python wrapper for Disney's API
|
|
5
5
|
Author: Scott Caratozzolo
|
|
6
6
|
License-Expression: MIT
|
|
@@ -17,6 +17,7 @@ Classifier: Operating System :: OS Independent
|
|
|
17
17
|
Requires-Python: <4.0,>=3.9
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
|
+
Requires-Dist: more-itertools>=10.7.0
|
|
20
21
|
Requires-Dist: pycryptodome>=3.22.0
|
|
21
22
|
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
22
23
|
Requires-Dist: requests>=2.32.3
|
|
@@ -15,6 +15,7 @@ mousetools/api/menu.py
|
|
|
15
15
|
mousetools/api/search.py
|
|
16
16
|
mousetools/channels/__init__.py
|
|
17
17
|
mousetools/channels/enums.py
|
|
18
|
+
mousetools/channels/manager.py
|
|
18
19
|
mousetools/channels/calendar/__init__.py
|
|
19
20
|
mousetools/channels/characters/__init__.py
|
|
20
21
|
mousetools/channels/facilities/__init__.py
|
|
@@ -26,4 +27,5 @@ mousetools/mixins/__init__.py
|
|
|
26
27
|
mousetools/mixins/couchbase.py
|
|
27
28
|
mousetools/mixins/disney.py
|
|
28
29
|
mousetools/resources/environments.bin
|
|
30
|
+
tests/test_manager.py
|
|
29
31
|
tests/test_mixins.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mousetools"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.5.1"
|
|
4
4
|
authors = [{name = "Scott Caratozzolo"}]
|
|
5
5
|
description = "An unofficial Python wrapper for Disney's API"
|
|
6
6
|
readme = "README.md"
|
|
@@ -8,6 +8,7 @@ license = "MIT"
|
|
|
8
8
|
license-files = ["LICENSE"]
|
|
9
9
|
requires-python = ">=3.9, <4.0"
|
|
10
10
|
dependencies = [
|
|
11
|
+
"more-itertools>=10.7.0",
|
|
11
12
|
"pycryptodome>=3.22.0",
|
|
12
13
|
"python-dateutil>=2.9.0.post0",
|
|
13
14
|
"requests>=2.32.3",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from mousetools.channels.facilities import FacilityChannel, FacilityChildChannel
|
|
2
|
+
from mousetools.channels.facilities.enums import DLRCouchbaseChannels, WDWCouchbaseChannels
|
|
3
|
+
from mousetools.channels.manager import ChannelManager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_manager():
|
|
7
|
+
channel_manager = ChannelManager([])
|
|
8
|
+
assert channel_manager.get_channel_ids() == []
|
|
9
|
+
|
|
10
|
+
wdw = FacilityChannel(WDWCouchbaseChannels.FACILITIES)
|
|
11
|
+
dlr = FacilityChannel(DLRCouchbaseChannels.FACILITIES)
|
|
12
|
+
channel_manager.add_channel(wdw)
|
|
13
|
+
channel_manager.add_channel(dlr)
|
|
14
|
+
assert channel_manager.get_all_channels() == [wdw, dlr]
|
|
15
|
+
|
|
16
|
+
channel = channel_manager.remove_channel(WDWCouchbaseChannels.FACILITIES.value, return_channel=True)
|
|
17
|
+
assert channel.channel_id == WDWCouchbaseChannels.FACILITIES.value
|
|
18
|
+
assert len(channel_manager.get_all_channels()) == 1
|
|
19
|
+
|
|
20
|
+
channel_manager.add_channel(
|
|
21
|
+
FacilityChildChannel("wdw.facilities.1_0.en_us.attraction.80010177;entityType=Attraction")
|
|
22
|
+
)
|
|
23
|
+
assert dlr._cb_data is None
|
|
24
|
+
channel_manager.refresh()
|
|
25
|
+
assert dlr._cb_data is not None
|
|
26
|
+
|
|
27
|
+
channel = channel_manager.get_channel("wdw.facilities.1_0.en_us.attraction.80010177;entityType=Attraction")
|
|
28
|
+
assert channel.channel_id == "wdw.facilities.1_0.en_us.attraction.80010177;entityType=Attraction"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|