MouseTools 4.0.1__tar.gz → 4.0.3__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.0.1 → mousetools-4.0.3}/PKG-INFO +3 -2
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/channels/__init__.py +7 -1
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/channels/enums.py +5 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/channels/facilities/__init__.py +33 -35
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/channels/facilitystatus/__init__.py +11 -8
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/mixins/disney.py +2 -1
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools.egg-info/PKG-INFO +3 -2
- mousetools-4.0.3/mousetools.egg-info/requires.txt +4 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/pyproject.toml +4 -2
- {mousetools-4.0.1 → mousetools-4.0.3}/tests/test_mixins.py +13 -0
- mousetools-4.0.1/mousetools.egg-info/requires.txt +0 -3
- {mousetools-4.0.1 → mousetools-4.0.3}/LICENSE +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/README.md +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/__init__.py +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/api/__init__.py +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/api/menu.py +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/auth.py +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/channels/facilities/enums.py +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/mixins/__init__.py +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/mixins/couchbase.py +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools/resources/environments.bin +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools.egg-info/SOURCES.txt +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools.egg-info/dependency_links.txt +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/mousetools.egg-info/top_level.txt +0 -0
- {mousetools-4.0.1 → mousetools-4.0.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mousetools
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.3
|
|
4
4
|
Summary: An unofficial Python wrapper for Disney's API
|
|
5
5
|
Author: Scott Caratozzolo
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,7 +19,8 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: httpx>=0.27.2
|
|
21
21
|
Requires-Dist: pycryptodome>=3.22.0
|
|
22
|
-
Requires-Dist:
|
|
22
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
23
|
+
Requires-Dist: tzdata>=2025.2
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
25
26
|
# MouseTools
|
|
@@ -35,6 +35,7 @@ class CouchbaseChannel(CouchbaseMixin):
|
|
|
35
35
|
"""Pulls initial data if none exists or if it is older than the refresh interval"""
|
|
36
36
|
if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
|
|
37
37
|
self._cb_data = self.get_channel_changes(self.channel_id)
|
|
38
|
+
self.last_update = datetime.now(tz=self._tz.value)
|
|
38
39
|
|
|
39
40
|
def get_children_channel_ids(self) -> list[str]:
|
|
40
41
|
"""Returns a list of child channel ids for the channel."""
|
|
@@ -43,4 +44,9 @@ class CouchbaseChannel(CouchbaseMixin):
|
|
|
43
44
|
return [i["id"] for i in self._cb_data["results"]]
|
|
44
45
|
|
|
45
46
|
def __repr__(self):
|
|
46
|
-
return f"{self.__class__.__name__}({self.channel_id})"
|
|
47
|
+
return f"{self.__class__.__name__}(channel_id='{self.channel_id}')"
|
|
48
|
+
|
|
49
|
+
def __eq__(self, other) -> bool:
|
|
50
|
+
if isinstance(other, CouchbaseChannel):
|
|
51
|
+
return self.channel_id == other.channel_id
|
|
52
|
+
return False
|
|
@@ -13,6 +13,7 @@ class CouchbaseChannels(str, Enum):
|
|
|
13
13
|
CALENDAR = "calendar"
|
|
14
14
|
FORECASTED_WAIT_TIME = "forecastedwaittimes"
|
|
15
15
|
TODAY = "today"
|
|
16
|
+
CHARACTERS = "characters"
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def get_complete_channel_id(
|
|
@@ -41,6 +42,8 @@ class WDWCouchbaseChannels(str, Enum):
|
|
|
41
42
|
"""Walt Disney World Forecasted Wait Time Channel"""
|
|
42
43
|
TODAY = get_complete_channel_id(DestinationShort.WALT_DISNEY_WORLD, CouchbaseChannels.TODAY, "1_0", False)
|
|
43
44
|
"""Walt Disney World Today Channel"""
|
|
45
|
+
CHARACTERS = get_complete_channel_id(DestinationShort.WALT_DISNEY_WORLD, CouchbaseChannels.CHARACTERS, "1_0", False)
|
|
46
|
+
"""Walt Disney World Characters Channel"""
|
|
44
47
|
|
|
45
48
|
|
|
46
49
|
class DLRCouchbaseChannels(str, Enum):
|
|
@@ -58,6 +61,8 @@ class DLRCouchbaseChannels(str, Enum):
|
|
|
58
61
|
"""Disneyland Resort Forecasted Wait Time Channel"""
|
|
59
62
|
TODAY = get_complete_channel_id(DestinationShort.DISNEYLAND_RESORT, CouchbaseChannels.TODAY, "1_0", False)
|
|
60
63
|
"""Disneyland Resort Today Channel"""
|
|
64
|
+
CHARACTERS = get_complete_channel_id(DestinationShort.DISNEYLAND_RESORT, CouchbaseChannels.CHARACTERS, "1_0", False)
|
|
65
|
+
"""Disneyland Resort Characters Channel"""
|
|
61
66
|
|
|
62
67
|
|
|
63
68
|
class DestinationTimezones(Enum):
|
|
@@ -5,6 +5,8 @@ from datetime import datetime, time, timedelta
|
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from zoneinfo import ZoneInfo
|
|
7
7
|
|
|
8
|
+
from dateutil.parser import isoparse
|
|
9
|
+
|
|
8
10
|
from mousetools.api.menu import Menus
|
|
9
11
|
from mousetools.channels import CouchbaseChannel
|
|
10
12
|
from mousetools.channels.enums import (
|
|
@@ -62,6 +64,7 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
62
64
|
self._refresh_interval: timedelta = timedelta(minutes=10)
|
|
63
65
|
|
|
64
66
|
self._cb_data: typing.Optional[dict] = None
|
|
67
|
+
self._cb_data_pull_time: typing.Optional[datetime] = datetime.now(tz=self._tz.value)
|
|
65
68
|
if not lazy_load:
|
|
66
69
|
self.refresh()
|
|
67
70
|
|
|
@@ -69,6 +72,7 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
69
72
|
"""Pulls initial data if none exists or if it is older than the refresh interval"""
|
|
70
73
|
if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
|
|
71
74
|
self._cb_data = self.get_channel_data(self.channel_id)
|
|
75
|
+
self._cb_data_pull_time = datetime.now(tz=self._tz.value)
|
|
72
76
|
|
|
73
77
|
@property
|
|
74
78
|
def name(self) -> typing.Optional[str]:
|
|
@@ -418,12 +422,12 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
418
422
|
if "wdw" in self.channel_id
|
|
419
423
|
else DestinationTimezones.DISNEYLAND_RESORT
|
|
420
424
|
)
|
|
421
|
-
dt =
|
|
425
|
+
dt = isoparse(self._cb_data["lastUpdate"])
|
|
422
426
|
dt = dt.replace(tzinfo=tz.value)
|
|
423
427
|
return dt
|
|
424
428
|
except KeyError:
|
|
425
429
|
logger.debug("No last updated found for %s", self.channel_id)
|
|
426
|
-
return
|
|
430
|
+
return self._cb_data_pull_time
|
|
427
431
|
|
|
428
432
|
@property
|
|
429
433
|
def sponsor_name(self) -> typing.Optional[str]:
|
|
@@ -440,6 +444,32 @@ class FacilityChildChannel(CouchbaseMixin):
|
|
|
440
444
|
logger.debug("No sponsor name found for %s", self.channel_id)
|
|
441
445
|
return None
|
|
442
446
|
|
|
447
|
+
@property
|
|
448
|
+
def meal_periods(self) -> typing.Optional[list[str]]:
|
|
449
|
+
"""
|
|
450
|
+
The meal periods offered by the restaurant.
|
|
451
|
+
|
|
452
|
+
Returns:
|
|
453
|
+
(typing.Optional[list[str]]): The meal periods offered by the restaurant.
|
|
454
|
+
"""
|
|
455
|
+
if self._cb_data is None:
|
|
456
|
+
self._cb_data = self.get_channel_data(self.channel_id)
|
|
457
|
+
try:
|
|
458
|
+
return self._cb_data["mealPeriods"]
|
|
459
|
+
except KeyError:
|
|
460
|
+
logger.debug("No meal periods found for %s", self.channel_id)
|
|
461
|
+
return None
|
|
462
|
+
|
|
463
|
+
@property
|
|
464
|
+
def menu(self) -> Menus:
|
|
465
|
+
"""
|
|
466
|
+
The menu of the restaurant.
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
(typing.Optional[Menus]): The menu of the restaurant.
|
|
470
|
+
"""
|
|
471
|
+
return Menus(self.channel_id)
|
|
472
|
+
|
|
443
473
|
def __str__(self) -> str:
|
|
444
474
|
return f"{self.entity_type}: {self.name} ({self.channel_id})"
|
|
445
475
|
|
|
@@ -482,39 +512,7 @@ ThemeParkFacilityChild = FacilityChildChannel.get_subclass("ThemeParkFacilityChi
|
|
|
482
512
|
TourFacilityChild = FacilityChildChannel.get_subclass("TourFacilityChild")
|
|
483
513
|
TransportationFacilityChild = FacilityChildChannel.get_subclass("TransportationFacilityChild")
|
|
484
514
|
WaterParkFacilityChild = FacilityChildChannel.get_subclass("WaterParkFacilityChild")
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
class RestaurantFacilityChild(FacilityChildChannel):
|
|
488
|
-
"""Class for RestaurantFacilityChild Entities."""
|
|
489
|
-
|
|
490
|
-
def __init__(self, *args, **kwargs):
|
|
491
|
-
super().__init__(*args, **kwargs)
|
|
492
|
-
|
|
493
|
-
@property
|
|
494
|
-
def meal_periods(self) -> typing.Optional[list[str]]:
|
|
495
|
-
"""
|
|
496
|
-
The meal periods offered by the restaurant.
|
|
497
|
-
|
|
498
|
-
Returns:
|
|
499
|
-
(typing.Optional[list[str]]): The meal periods offered by the restaurant.
|
|
500
|
-
"""
|
|
501
|
-
if self._cb_data is None:
|
|
502
|
-
self._cb_data = self.get_channel_data(self.channel_id)
|
|
503
|
-
try:
|
|
504
|
-
return self._cb_data["mealPeriods"]
|
|
505
|
-
except KeyError:
|
|
506
|
-
logger.debug("No meal periods found for %s", self.channel_id)
|
|
507
|
-
return None
|
|
508
|
-
|
|
509
|
-
@property
|
|
510
|
-
def menu(self) -> Menus:
|
|
511
|
-
"""
|
|
512
|
-
The menu of the restaurant.
|
|
513
|
-
|
|
514
|
-
Returns:
|
|
515
|
-
(typing.Optional[Menus]): The menu of the restaurant.
|
|
516
|
-
"""
|
|
517
|
-
return Menus(self.channel_id)
|
|
515
|
+
RestaurantFacilityChild = FacilityChildChannel.get_subclass("RestaurantFacilityChild")
|
|
518
516
|
|
|
519
517
|
|
|
520
518
|
class FacilityChannel(CouchbaseChannel):
|
|
@@ -3,6 +3,8 @@ import typing
|
|
|
3
3
|
from collections.abc import Generator
|
|
4
4
|
from datetime import datetime, timedelta
|
|
5
5
|
|
|
6
|
+
from dateutil.parser import isoparse
|
|
7
|
+
|
|
6
8
|
from mousetools.channels import CouchbaseChannel
|
|
7
9
|
from mousetools.channels.enums import CouchbaseChannels, DestinationShort, DestinationTimezones
|
|
8
10
|
from mousetools.mixins.couchbase import CouchbaseMixin
|
|
@@ -28,16 +30,17 @@ class FacilityStatusChildChannel(CouchbaseMixin):
|
|
|
28
30
|
else DestinationTimezones.DISNEYLAND_RESORT
|
|
29
31
|
)
|
|
30
32
|
self._refresh_interval: timedelta = timedelta(minutes=10)
|
|
33
|
+
self._cb_data: typing.Optional[dict] = None
|
|
34
|
+
self._cb_data_pull_time: typing.Optional[datetime] = datetime.now(tz=self._tz.value)
|
|
31
35
|
|
|
32
|
-
if lazy_load:
|
|
33
|
-
self.
|
|
34
|
-
else:
|
|
35
|
-
self._cb_data = self.get_channel_data(self.channel_id)
|
|
36
|
+
if not lazy_load:
|
|
37
|
+
self.refresh()
|
|
36
38
|
|
|
37
39
|
def refresh(self) -> None:
|
|
38
40
|
"""Pulls initial data if none exists or if it is older than the refresh interval"""
|
|
39
41
|
if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
|
|
40
42
|
self._cb_data = self.get_channel_data(self.channel_id)
|
|
43
|
+
self._cb_data_pull_time = datetime.now(tz=self._tz.value)
|
|
41
44
|
|
|
42
45
|
def get_status(self) -> typing.Optional[str]:
|
|
43
46
|
"""Get the operating status of the facility
|
|
@@ -78,12 +81,12 @@ class FacilityStatusChildChannel(CouchbaseMixin):
|
|
|
78
81
|
self._cb_data = self.get_channel_data(self.channel_id)
|
|
79
82
|
|
|
80
83
|
try:
|
|
81
|
-
dt =
|
|
84
|
+
dt = isoparse(self._cb_data["lastUpdate"])
|
|
82
85
|
dt = dt.replace(tzinfo=self._tz.value)
|
|
83
86
|
return dt
|
|
84
87
|
except KeyError:
|
|
85
88
|
logger.debug("No last updated found for %s", self.channel_id)
|
|
86
|
-
return
|
|
89
|
+
return self._cb_data_pull_time
|
|
87
90
|
|
|
88
91
|
@property
|
|
89
92
|
def fast_pass_available(self) -> typing.Optional[bool]:
|
|
@@ -99,7 +102,7 @@ class FacilityStatusChildChannel(CouchbaseMixin):
|
|
|
99
102
|
self.refresh()
|
|
100
103
|
|
|
101
104
|
try:
|
|
102
|
-
dt =
|
|
105
|
+
dt = isoparse(self._cb_data["fastPassEndTime"])
|
|
103
106
|
dt = dt.replace(tzinfo=self._tz.value)
|
|
104
107
|
return dt
|
|
105
108
|
except (KeyError, ValueError):
|
|
@@ -110,7 +113,7 @@ class FacilityStatusChildChannel(CouchbaseMixin):
|
|
|
110
113
|
self.refresh()
|
|
111
114
|
|
|
112
115
|
try:
|
|
113
|
-
dt =
|
|
116
|
+
dt = isoparse(self._cb_data["fastPassStartTime"])
|
|
114
117
|
dt = dt.replace(tzinfo=self._tz.value)
|
|
115
118
|
return dt
|
|
116
119
|
except (KeyError, ValueError):
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import logging
|
|
2
3
|
|
|
3
4
|
import httpx
|
|
@@ -29,7 +30,7 @@ class DisneyAPIMixin:
|
|
|
29
30
|
try:
|
|
30
31
|
response.raise_for_status()
|
|
31
32
|
data = response.json()
|
|
32
|
-
except httpx.HTTPError as err:
|
|
33
|
+
except (httpx.HTTPError, json.decoder.JSONDecodeError) as err:
|
|
33
34
|
logger.warning("Request failed. Error: %s", err)
|
|
34
35
|
return None
|
|
35
36
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mousetools
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.3
|
|
4
4
|
Summary: An unofficial Python wrapper for Disney's API
|
|
5
5
|
Author: Scott Caratozzolo
|
|
6
6
|
License-Expression: MIT
|
|
@@ -19,7 +19,8 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: httpx>=0.27.2
|
|
21
21
|
Requires-Dist: pycryptodome>=3.22.0
|
|
22
|
-
Requires-Dist:
|
|
22
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
23
|
+
Requires-Dist: tzdata>=2025.2
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
25
26
|
# MouseTools
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mousetools"
|
|
3
|
-
version = "4.0.
|
|
3
|
+
version = "4.0.3"
|
|
4
4
|
authors = [{name = "Scott Caratozzolo"}]
|
|
5
5
|
description = "An unofficial Python wrapper for Disney's API"
|
|
6
6
|
readme = "README.md"
|
|
@@ -10,7 +10,8 @@ requires-python = ">=3.9, <4.0"
|
|
|
10
10
|
dependencies = [
|
|
11
11
|
"httpx>=0.27.2",
|
|
12
12
|
"pycryptodome>=3.22.0",
|
|
13
|
-
"
|
|
13
|
+
"python-dateutil>=2.9.0.post0",
|
|
14
|
+
"tzdata>=2025.2",
|
|
14
15
|
]
|
|
15
16
|
classifiers = [
|
|
16
17
|
"Programming Language :: Python :: 3",
|
|
@@ -40,6 +41,7 @@ dev-dependencies = [
|
|
|
40
41
|
"commitizen>=4.7.0",
|
|
41
42
|
"go-task-bin>=3.43.3",
|
|
42
43
|
"icecream>=2.1.4",
|
|
44
|
+
"pytest-mock>=3.14.0",
|
|
43
45
|
]
|
|
44
46
|
|
|
45
47
|
[build-system]
|
|
@@ -13,6 +13,15 @@ def test_couchbase_mixin():
|
|
|
13
13
|
assert channel_changes
|
|
14
14
|
assert len(channel_changes["results"]) > 0
|
|
15
15
|
|
|
16
|
+
channel_data = cb.get_channel_data("wdw.notreal.1_0")
|
|
17
|
+
assert channel_data.get("error", "") == "not_found"
|
|
18
|
+
|
|
19
|
+
cb._couchbase_url = "http://notreal.com"
|
|
20
|
+
channel_data = cb.get_channel_data("wdw.facilities.1_0.en_us.attraction.80010177;entityType=Attraction")
|
|
21
|
+
channel_changes = cb.get_channel_changes("wdw.facilities.1_0.en_us", since=3900000)
|
|
22
|
+
assert channel_data is None
|
|
23
|
+
assert channel_changes is None
|
|
24
|
+
|
|
16
25
|
|
|
17
26
|
def test_disney_mixin():
|
|
18
27
|
disney = DisneyAPIMixin()
|
|
@@ -22,3 +31,7 @@ def test_disney_mixin():
|
|
|
22
31
|
|
|
23
32
|
assert data
|
|
24
33
|
assert "menus" in data
|
|
34
|
+
|
|
35
|
+
data = disney.get_disney_data("http://notreal.com")
|
|
36
|
+
|
|
37
|
+
assert data is None
|
|
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
|