MouseTools 4.0.3__tar.gz → 4.1.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 (30) hide show
  1. {mousetools-4.0.3 → mousetools-4.1.1}/PKG-INFO +24 -2
  2. {mousetools-4.0.3 → mousetools-4.1.1}/README.md +23 -1
  3. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/api/menu.py +121 -140
  4. mousetools-4.1.1/mousetools/api/search.py +58 -0
  5. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/channels/__init__.py +11 -1
  6. mousetools-4.1.1/mousetools/channels/calendar/__init__.py +279 -0
  7. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/channels/enums.py +1 -4
  8. mousetools-4.1.1/mousetools/channels/facilities/__init__.py +766 -0
  9. mousetools-4.1.1/mousetools/channels/facilities/enums.py +32 -0
  10. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/channels/facilitystatus/__init__.py +46 -90
  11. mousetools-4.1.1/mousetools/decorators.py +36 -0
  12. mousetools-4.1.1/mousetools/enums.py +34 -0
  13. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools.egg-info/PKG-INFO +24 -2
  14. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools.egg-info/SOURCES.txt +4 -0
  15. {mousetools-4.0.3 → mousetools-4.1.1}/pyproject.toml +1 -1
  16. mousetools-4.0.3/mousetools/channels/facilities/__init__.py +0 -856
  17. mousetools-4.0.3/mousetools/channels/facilities/enums.py +0 -30
  18. {mousetools-4.0.3 → mousetools-4.1.1}/LICENSE +0 -0
  19. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/__init__.py +0 -0
  20. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/api/__init__.py +0 -0
  21. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/auth.py +0 -0
  22. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/mixins/__init__.py +0 -0
  23. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/mixins/couchbase.py +0 -0
  24. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/mixins/disney.py +0 -0
  25. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools/resources/environments.bin +0 -0
  26. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools.egg-info/dependency_links.txt +0 -0
  27. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools.egg-info/requires.txt +0 -0
  28. {mousetools-4.0.3 → mousetools-4.1.1}/mousetools.egg-info/top_level.txt +0 -0
  29. {mousetools-4.0.3 → mousetools-4.1.1}/setup.cfg +0 -0
  30. {mousetools-4.0.3 → mousetools-4.1.1}/tests/test_mixins.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mousetools
3
- Version: 4.0.3
3
+ Version: 4.1.1
4
4
  Summary: An unofficial Python wrapper for Disney's API
5
5
  Author: Scott Caratozzolo
6
6
  License-Expression: MIT
@@ -25,7 +25,7 @@ Dynamic: license-file
25
25
 
26
26
  # MouseTools
27
27
 
28
- [![PyPI version](https://badge.fury.io/py/MouseTools.svg)](https://badge.fury.io/py/MouseTools) [![Downloads](https://pepy.tech/badge/mousetools)](https://pepy.tech/project/mousetools)
28
+ ![PyPI - Version](https://img.shields.io/pypi/v/mousetools?style=flat-square&logo=python&color=00C106&link=https%3A%2F%2Fpypi.org%2Fproject%2Fmousetools%2F) ![PyPI - Downloads](https://img.shields.io/pypi/dm/mousetools?style=flat-square&color=blue&link=https%3A%2F%2Fpypi.org%2Fproject%2Fmousetools%2F)
29
29
 
30
30
 
31
31
  An unofficial Python wrapper for the Disney API. Data is pulled directly from Disney. This package supports Walt Disney World and Disneyland Resort.
@@ -110,8 +110,30 @@ print(mk.description)
110
110
  # "Enter a Land Where Fantasy Reigns"
111
111
  ```
112
112
 
113
+ There is also a search functionality to help find the channels you want.
114
+ ```python
115
+ from mousetools.api.search import Search
116
+ from mousetools.enums import DestinationShort
117
+
118
+ search = Search(DestinationShort.WALT_DISNEY_WORLD)
119
+ print(search("pirates", max_results=1))
120
+ # [{'channel_id': 'wdw.facilities.1_0.en_us.attraction.80010177;entityType=Attraction',
121
+ # 'entity_id': '80010177;entityType=Attraction',
122
+ # 'name': 'Pirates of the Caribbean',
123
+ # 'search_score': 3507.1604}]
124
+ ```
125
+
113
126
  ### License
114
127
  This project is distributed under the MIT license. For more information see [LICENSE](https://gitlab.com/caratozzoloxyz/public/MouseTools/-/blob/master/LICENSE?ref_type=heads)
115
128
 
116
129
  ### Disclaimer
117
130
  This project is in no way affiliated with The Walt Disney Company and all use of Disney Services is subject to the [Disney Terms of Use](https://disneytermsofuse.com/).
131
+
132
+
133
+ ### About Project
134
+
135
+ <a href='https://ko-fi.com/scaratozzolo' target='_blank'>
136
+ <img src='https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=kofi&logoColor=white&link=https%3A%2F%2Fko-fi.com%2Fscaratozzolo border='0' alt='Support my projects' />
137
+ </a>
138
+
139
+ MouseTools is a passion project I've been working on since 2018 when I was first learning Python. The package has evolved and been rewritten multiple times as I've become a better developer and as the Disney API has changed. There is a lot of time exploring Disney's API and figuring out how all the pieces work before any code is even written. I work on this project when I have time or when I have an idea for another project that could use this data.
@@ -1,6 +1,6 @@
1
1
  # MouseTools
2
2
 
3
- [![PyPI version](https://badge.fury.io/py/MouseTools.svg)](https://badge.fury.io/py/MouseTools) [![Downloads](https://pepy.tech/badge/mousetools)](https://pepy.tech/project/mousetools)
3
+ ![PyPI - Version](https://img.shields.io/pypi/v/mousetools?style=flat-square&logo=python&color=00C106&link=https%3A%2F%2Fpypi.org%2Fproject%2Fmousetools%2F) ![PyPI - Downloads](https://img.shields.io/pypi/dm/mousetools?style=flat-square&color=blue&link=https%3A%2F%2Fpypi.org%2Fproject%2Fmousetools%2F)
4
4
 
5
5
 
6
6
  An unofficial Python wrapper for the Disney API. Data is pulled directly from Disney. This package supports Walt Disney World and Disneyland Resort.
@@ -85,8 +85,30 @@ print(mk.description)
85
85
  # "Enter a Land Where Fantasy Reigns"
86
86
  ```
87
87
 
88
+ There is also a search functionality to help find the channels you want.
89
+ ```python
90
+ from mousetools.api.search import Search
91
+ from mousetools.enums import DestinationShort
92
+
93
+ search = Search(DestinationShort.WALT_DISNEY_WORLD)
94
+ print(search("pirates", max_results=1))
95
+ # [{'channel_id': 'wdw.facilities.1_0.en_us.attraction.80010177;entityType=Attraction',
96
+ # 'entity_id': '80010177;entityType=Attraction',
97
+ # 'name': 'Pirates of the Caribbean',
98
+ # 'search_score': 3507.1604}]
99
+ ```
100
+
88
101
  ### License
89
102
  This project is distributed under the MIT license. For more information see [LICENSE](https://gitlab.com/caratozzoloxyz/public/MouseTools/-/blob/master/LICENSE?ref_type=heads)
90
103
 
91
104
  ### Disclaimer
92
105
  This project is in no way affiliated with The Walt Disney Company and all use of Disney Services is subject to the [Disney Terms of Use](https://disneytermsofuse.com/).
106
+
107
+
108
+ ### About Project
109
+
110
+ <a href='https://ko-fi.com/scaratozzolo' target='_blank'>
111
+ <img src='https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=kofi&logoColor=white&link=https%3A%2F%2Fko-fi.com%2Fscaratozzolo border='0' alt='Support my projects' />
112
+ </a>
113
+
114
+ MouseTools is a passion project I've been working on since 2018 when I was first learning Python. The package has evolved and been rewritten multiple times as I've become a better developer and as the Disney API has changed. There is a lot of time exploring Disney's API and figuring out how all the pieces work before any code is even written. I work on this project when I have time or when I have an idea for another project that could use this data.
@@ -1,6 +1,11 @@
1
1
  import typing
2
+ from datetime import datetime, timedelta
3
+ from zoneinfo import ZoneInfo
4
+
5
+ from dateutil.parser import isoparse
2
6
 
3
7
  from mousetools.auth import auth_obj
8
+ from mousetools.decorators import disney_property, json_property
4
9
  from mousetools.mixins.disney import DisneyAPIMixin
5
10
 
6
11
  # menus -> menu -> menugroups -> menuitems
@@ -14,7 +19,7 @@ class MenuItem:
14
19
  def __repr__(self) -> str:
15
20
  return f"MenuItem(entity_id={self.entity_id}, parent_resturant_id={self._parent_restaurant_id})"
16
21
 
17
- @property
22
+ @json_property
18
23
  def entity_id(self) -> typing.Optional[str]:
19
24
  """
20
25
  The entity id of the menu item.
@@ -22,12 +27,9 @@ class MenuItem:
22
27
  Returns:
23
28
  (typing.Optional[str]): The entity id of the menu item.
24
29
  """
25
- try:
26
- return self._raw_item_data["id"]
27
- except KeyError:
28
- return None
30
+ return self._raw_item_data["id"]
29
31
 
30
- @property
32
+ @json_property
31
33
  def pc_short_name(self) -> typing.Optional[str]:
32
34
  """
33
35
  The short name of the menu item.
@@ -35,12 +37,9 @@ class MenuItem:
35
37
  Returns:
36
38
  (typing.Optional[str]): The short name of the menu item.
37
39
  """
38
- try:
39
- return self._raw_item_data["names"]["PCShort"]
40
- except KeyError:
41
- return None
40
+ return self._raw_item_data["names"]["PCShort"]
42
41
 
43
- @property
42
+ @json_property
44
43
  def pc_long_name(self) -> typing.Optional[str]:
45
44
  """
46
45
  The long name of the menu item.
@@ -48,12 +47,9 @@ class MenuItem:
48
47
  Returns:
49
48
  (typing.Optional[str]): The long name of the menu item.
50
49
  """
51
- try:
52
- return self._raw_item_data["names"]["PCLong"]
53
- except KeyError:
54
- return None
50
+ return self._raw_item_data["names"]["PCLong"]
55
51
 
56
- @property
52
+ @json_property
57
53
  def mobile_short_name(self) -> typing.Optional[str]:
58
54
  """
59
55
  The short name of the menu item.
@@ -61,12 +57,9 @@ class MenuItem:
61
57
  Returns:
62
58
  (typing.Optional[str]): The short name of the menu item.
63
59
  """
64
- try:
65
- return self._raw_item_data["names"]["MobileShort"]
66
- except KeyError:
67
- return None
60
+ return self._raw_item_data["names"]["MobileShort"]
68
61
 
69
- @property
62
+ @json_property
70
63
  def mobile_long_name(self) -> typing.Optional[str]:
71
64
  """
72
65
  The long name of the menu item.
@@ -74,12 +67,9 @@ class MenuItem:
74
67
  Returns:
75
68
  (typing.Optional[str]): The long name of the menu item.
76
69
  """
77
- try:
78
- return self._raw_item_data["names"]["MobileLong"]
79
- except KeyError:
80
- return None
70
+ return self._raw_item_data["names"]["MobileLong"]
81
71
 
82
- @property
72
+ @json_property
83
73
  def mickey_check(self) -> typing.Optional[bool]:
84
74
  """
85
75
  Whether the menu item is mickey check.
@@ -87,12 +77,9 @@ class MenuItem:
87
77
  Returns:
88
78
  (typing.Optional[bool]): Whether the menu item is mickey check.
89
79
  """
90
- try:
91
- return self._raw_item_data["mickeyCheck"]
92
- except KeyError:
93
- return None
80
+ return self._raw_item_data["mickeyCheck"]
94
81
 
95
- @property
82
+ @json_property
96
83
  def pc_long_description(self) -> typing.Optional[str]:
97
84
  """
98
85
  The long description of the menu item.
@@ -100,12 +87,9 @@ class MenuItem:
100
87
  Returns:
101
88
  (typing.Optional[str]): The long description of the menu item.
102
89
  """
103
- try:
104
- return self._raw_item_data["descriptions"]["PCLong"]["text"]
105
- except KeyError:
106
- return None
90
+ return self._raw_item_data["descriptions"]["PCLong"]["text"]
107
91
 
108
- @property
92
+ @json_property
109
93
  def mobile_short_description(self) -> typing.Optional[str]:
110
94
  """
111
95
  The short description of the menu item.
@@ -113,12 +97,9 @@ class MenuItem:
113
97
  Returns:
114
98
  (typing.Optional[str]): The short description of the menu item.
115
99
  """
116
- try:
117
- return self._raw_item_data["descriptions"]["MobileShort"]["text"]
118
- except KeyError:
119
- return None
100
+ return self._raw_item_data["descriptions"]["MobileShort"]["text"]
120
101
 
121
- @property
102
+ @json_property
122
103
  def default_selection(self) -> typing.Optional[bool]:
123
104
  """
124
105
  Whether the menu item is the default selection.
@@ -126,12 +107,9 @@ class MenuItem:
126
107
  Returns:
127
108
  (typing.Optional[bool]): Whether the menu item is the default selection.
128
109
  """
129
- try:
130
- return self._raw_item_data["defaultSelection"]
131
- except KeyError:
132
- return None
110
+ return self._raw_item_data["defaultSelection"]
133
111
 
134
- @property
112
+ @json_property
135
113
  def prices(self) -> typing.Optional[dict]:
136
114
  """
137
115
  The prices of the menu item.
@@ -139,12 +117,9 @@ class MenuItem:
139
117
  Returns:
140
118
  (typing.Optional[dict]): The prices of the menu item.
141
119
  """
142
- try:
143
- return self._raw_item_data["prices"]
144
- except KeyError:
145
- return None
120
+ return self._raw_item_data["prices"]
146
121
 
147
- @property
122
+ @json_property
148
123
  def per_serving_without_tax(self) -> typing.Optional[float]:
149
124
  """
150
125
  The per serving without tax of the menu item.
@@ -152,10 +127,7 @@ class MenuItem:
152
127
  Returns:
153
128
  (typing.Optional[float]): The per serving without tax of the menu item.
154
129
  """
155
- try:
156
- return self._raw_item_data["prices"]["PerServing"]["withoutTax"]
157
- except KeyError:
158
- return None
130
+ return self._raw_item_data["prices"]["PerServing"]["withoutTax"]
159
131
 
160
132
 
161
133
  class MenuGroup:
@@ -166,7 +138,7 @@ class MenuGroup:
166
138
  def __str__(self) -> str:
167
139
  return f"MenuGroup(menu_group_type={self.menu_group_type}, parent_restaurant_id={self._parent_restaurant_id})"
168
140
 
169
- @property
141
+ @json_property
170
142
  def menu_group_type(self) -> typing.Optional[str]:
171
143
  """
172
144
  The type of the menu group.
@@ -174,12 +146,9 @@ class MenuGroup:
174
146
  Returns:
175
147
  (typing.Optional[str]): The type of the menu group.
176
148
  """
177
- try:
178
- return self._raw_menu_group_data["menuGroupType"]
179
- except KeyError:
180
- return None
149
+ return self._raw_menu_group_data["menuGroupType"]
181
150
 
182
- @property
151
+ @json_property
183
152
  def multiple_price_types(self) -> typing.Optional[bool]:
184
153
  """
185
154
  Whether the menu group has multiple price types.
@@ -187,12 +156,9 @@ class MenuGroup:
187
156
  Returns:
188
157
  (typing.Optional[bool]): Whether the menu group has multiple price types.
189
158
  """
190
- try:
191
- return self._raw_menu_group_data["multiplePriceTypes"]
192
- except KeyError:
193
- return None
159
+ return self._raw_menu_group_data["multiplePriceTypes"]
194
160
 
195
- @property
161
+ @json_property
196
162
  def mickey_check_items(self) -> typing.Optional[bool]:
197
163
  """
198
164
  Whether the menu group has mickey check items.
@@ -200,12 +166,9 @@ class MenuGroup:
200
166
  Returns:
201
167
  (typing.Optional[bool]): Whether the menu group has mickey check items.
202
168
  """
203
- try:
204
- return self._raw_menu_group_data["mickeyCheckItems"]
205
- except KeyError:
206
- return None
169
+ return self._raw_menu_group_data["mickeyCheckItems"]
207
170
 
208
- @property
171
+ @json_property
209
172
  def names(self) -> typing.Optional[dict]:
210
173
  """
211
174
  The names of the menu group.
@@ -213,12 +176,9 @@ class MenuGroup:
213
176
  Returns:
214
177
  (typing.Optional[dict]): The names of the menu group.
215
178
  """
216
- try:
217
- return self._raw_menu_group_data["names"]
218
- except KeyError:
219
- return None
179
+ return self._raw_menu_group_data["names"]
220
180
 
221
- @property
181
+ @json_property
222
182
  def pc_short_name(self) -> typing.Optional[str]:
223
183
  """
224
184
  The short name of the menu group.
@@ -226,12 +186,9 @@ class MenuGroup:
226
186
  Returns:
227
187
  (typing.Optional[str]): The short name of the menu group.
228
188
  """
229
- try:
230
- return self._raw_menu_group_data["names"]["PCShort"]
231
- except KeyError:
232
- return None
189
+ return self._raw_menu_group_data["names"]["PCShort"]
233
190
 
234
- @property
191
+ @json_property
235
192
  def pc_long_name(self) -> typing.Optional[str]:
236
193
  """
237
194
  The long name of the menu group.
@@ -239,12 +196,9 @@ class MenuGroup:
239
196
  Returns:
240
197
  (typing.Optional[str]): The long name of the menu group.
241
198
  """
242
- try:
243
- return self._raw_menu_group_data["names"]["PCLong"]
244
- except KeyError:
245
- return None
199
+ return self._raw_menu_group_data["names"]["PCLong"]
246
200
 
247
- @property
201
+ @json_property
248
202
  def mobile_short_name(self) -> typing.Optional[str]:
249
203
  """
250
204
  The short name of the menu group.
@@ -252,12 +206,9 @@ class MenuGroup:
252
206
  Returns:
253
207
  (typing.Optional[str]): The short name of the menu group.
254
208
  """
255
- try:
256
- return self._raw_menu_group_data["names"]["MobileShort"]
257
- except KeyError:
258
- return None
209
+ return self._raw_menu_group_data["names"]["MobileShort"]
259
210
 
260
- @property
211
+ @json_property
261
212
  def mobile_long_name(self) -> typing.Optional[str]:
262
213
  """
263
214
  The long name of the menu group.
@@ -265,10 +216,7 @@ class MenuGroup:
265
216
  Returns:
266
217
  (typing.Optional[str]): The long name of the menu group.
267
218
  """
268
- try:
269
- return self._raw_menu_group_data["names"]["MobileLong"]
270
- except KeyError:
271
- return None
219
+ return self._raw_menu_group_data["names"]["MobileLong"]
272
220
 
273
221
  @property
274
222
  def menu_items(self) -> typing.Optional[list[MenuItem]]:
@@ -292,7 +240,7 @@ class Menu:
292
240
  def __str__(self):
293
241
  return f"Menu(entity_id={self.entity_id}, menu_type={self.menu_type}, parent_restaurant_id={self._parent_restaurant_id})"
294
242
 
295
- @property
243
+ @json_property
296
244
  def entity_id(self) -> typing.Optional[str]:
297
245
  """
298
246
  The entity id of the menu.
@@ -300,12 +248,9 @@ class Menu:
300
248
  Returns:
301
249
  (typing.Optional[str]): The entity id of the menu.
302
250
  """
303
- try:
304
- return self._raw_menu_data["id"]
305
- except KeyError:
306
- return None
251
+ return self._raw_menu_data["id"]
307
252
 
308
- @property
253
+ @json_property
309
254
  def menu_type(self) -> typing.Optional[str]:
310
255
  """
311
256
  The type of the menu.
@@ -313,12 +258,9 @@ class Menu:
313
258
  Returns:
314
259
  (typing.Optional[str]): The type of the menu.
315
260
  """
316
- try:
317
- return self._raw_menu_data["menuType"]
318
- except KeyError:
319
- return None
261
+ return self._raw_menu_data["menuType"]
320
262
 
321
- @property
263
+ @json_property
322
264
  def localized_menu_type(self) -> typing.Optional[str]:
323
265
  """
324
266
  The localized type of the menu.
@@ -326,12 +268,9 @@ class Menu:
326
268
  Returns:
327
269
  (typing.Optional[str]): The localized type of the menu.
328
270
  """
329
- try:
330
- return self._raw_menu_data["localizedMenuType"]
331
- except KeyError:
332
- return None
271
+ return self._raw_menu_data["localizedMenuType"]
333
272
 
334
- @property
273
+ @json_property
335
274
  def experience_type(self) -> typing.Optional[str]:
336
275
  """
337
276
  The experience type of the menu.
@@ -339,12 +278,9 @@ class Menu:
339
278
  Returns:
340
279
  (typing.Optional[str]): The experience type of the menu.
341
280
  """
342
- try:
343
- return self._raw_menu_data["experienceType"]
344
- except KeyError:
345
- return None
281
+ return self._raw_menu_data["experienceType"]
346
282
 
347
- @property
283
+ @json_property
348
284
  def service_style(self) -> typing.Optional[str]:
349
285
  """
350
286
  The service style of the menu.
@@ -352,12 +288,9 @@ class Menu:
352
288
  Returns:
353
289
  (typing.Optional[str]): The service style of the menu.
354
290
  """
355
- try:
356
- return self._raw_menu_data["serviceStyle"]
357
- except KeyError:
358
- return None
291
+ return self._raw_menu_data["serviceStyle"]
359
292
 
360
- @property
293
+ @json_property
361
294
  def primary_cuisine_type(self) -> typing.Optional[str]:
362
295
  """
363
296
  The primary cuisine type of the menu.
@@ -365,12 +298,9 @@ class Menu:
365
298
  Returns:
366
299
  (typing.Optional[str]): The primary cuisine type of the menu.
367
300
  """
368
- try:
369
- return self._raw_menu_data["primaryCuisineType"]
370
- except KeyError:
371
- return None
301
+ return self._raw_menu_data["primaryCuisineType"]
372
302
 
373
- @property
303
+ @json_property
374
304
  def secondary_cuisine_type(self) -> typing.Optional[str]:
375
305
  """
376
306
  The secondary cuisine type of the menu.
@@ -378,10 +308,7 @@ class Menu:
378
308
  Returns:
379
309
  (typing.Optional[str]): The secondary cuisine type of the menu.
380
310
  """
381
- try:
382
- return self._raw_menu_data["secondaryCuisineType"]
383
- except KeyError:
384
- return None
311
+ return self._raw_menu_data["secondaryCuisineType"]
385
312
 
386
313
  @property
387
314
  def menu_groups(self) -> list[MenuGroup]:
@@ -406,10 +333,15 @@ class Menus(DisneyAPIMixin):
406
333
  self.entity_id = restaurant_channel_id.rsplit(".", 1)[-1]
407
334
  self._menu_service_url = f"{self._menu_service_base}/{self.entity_id}"
408
335
 
409
- if lazy_load:
410
- self.disney_data: typing.Optional[dict] = None
411
- else:
412
- self.disney_data = self.get_disney_data(self.entity_id)
336
+ self._refresh_interval: timedelta = timedelta(
337
+ hours=12
338
+ ) # properties are rarely updated, no need to spam the API
339
+ self._tz = ZoneInfo("UTC")
340
+
341
+ self._disney_data: typing.Optional[dict] = None
342
+ self._disney_data_pull_time: typing.Optional[datetime] = datetime.now(tz=self._tz)
343
+ if not lazy_load:
344
+ self.refresh()
413
345
 
414
346
  def __str__(self):
415
347
  return f"Menus(entity_id={self.entity_id})"
@@ -417,7 +349,13 @@ class Menus(DisneyAPIMixin):
417
349
  def __repr__(self):
418
350
  return f"Menus(restaurant_channel_id={self.entity_id})"
419
351
 
420
- @property
352
+ def refresh(self) -> None:
353
+ """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
+ self._disney_data = self.get_disney_data(self._menu_service_url)
356
+ self._disney_data_pull_time = datetime.now(tz=self._tz)
357
+
358
+ @disney_property(default_value=[])
421
359
  def menus(self) -> list[Menu]:
422
360
  """
423
361
  Returns a list of all the menus associated with this entity.
@@ -425,9 +363,52 @@ class Menus(DisneyAPIMixin):
425
363
  Returns:
426
364
  (list[Menu]): List of Menu objects that are associated with this entity.
427
365
  """
428
- if self.disney_data is None:
429
- self.disney_data = self.get_disney_data(self._menu_service_url)
366
+
367
+ return [Menu(i, self.entity_id) for i in self._disney_data["menus"]]
368
+
369
+ @disney_property()
370
+ def facility_name(self) -> typing.Optional[str]:
371
+ """
372
+ The name of the facility.
373
+
374
+ Returns:
375
+ (typing.Optional[str]): The name of the facility.
376
+ """
377
+ return self._disney_data["facilityName"]
378
+
379
+ @disney_property()
380
+ def ancestor_location_park_resort(self) -> typing.Optional[str]:
381
+ """
382
+ The name of the ancestor location.
383
+
384
+ Returns:
385
+ (typing.Optional[str]): The name of the ancestor location.
386
+ """
387
+ return self._disney_data["ancestorLocationParkResort"]
388
+
389
+ @disney_property()
390
+ def ancestor_location_land_area(self) -> typing.Optional[str]:
391
+ """
392
+ The name of the ancestor location.
393
+
394
+ Returns:
395
+ (typing.Optional[str]): The name of the ancestor location.
396
+ """
397
+ return self._disney_data["ancestorLocationLandArea"]
398
+
399
+ @property
400
+ def last_refresh(self) -> typing.Optional[datetime]:
401
+ """
402
+ The last time the menus were refreshed.
403
+
404
+ Returns:
405
+ (typing.Optional[datetime]): The last time the menus were refreshed.
406
+ """
407
+ if self._disney_data is None:
408
+ self._disney_data = self.get_disney_data(self.entity_id)
430
409
  try:
431
- return [Menu(i, self.entity_id) for i in self.disney_data["menus"]]
432
- except KeyError:
433
- return []
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
@@ -0,0 +1,58 @@
1
+ from typing import Optional, Union
2
+
3
+ from mousetools.auth import auth_obj
4
+ from mousetools.channels.enums import DLRCouchbaseChannels, WDWCouchbaseChannels
5
+ from mousetools.enums import DestinationShort
6
+ from mousetools.mixins.disney import DisneyAPIMixin
7
+
8
+
9
+ class Search(DisneyAPIMixin):
10
+ _search_service_base = auth_obj._environments["searchServiceUrl"]
11
+
12
+ def __init__(self, destination: Union[DestinationShort, str]):
13
+ """
14
+ Args:
15
+ destination (Union[DestinationShort, str]): Search the facilities for this destination
16
+ """
17
+ if isinstance(destination, DestinationShort):
18
+ destination = destination.value
19
+ self.destination = destination
20
+ self._facilities_channel = (
21
+ WDWCouchbaseChannels.FACILITIES.value
22
+ if destination == DestinationShort.WALT_DISNEY_WORLD
23
+ else DLRCouchbaseChannels.FACILITIES.value
24
+ )
25
+
26
+ def search(self, query: str, max_results: int = 10) -> Optional[list[dict]]:
27
+ """Send a request to the search service
28
+
29
+ Args:
30
+ query (str): Search query
31
+ max_results (int, optional): Maximum number of results to return in the list. Defaults to 10.
32
+
33
+ Returns:
34
+ (Optional[list[dict]]): List of results
35
+ """
36
+ api_data = self.get_disney_data(
37
+ f"{self._search_service_base}/mobileapi/v1/entities/search?q={query}&brand={self.destination}"
38
+ )
39
+ our_results = []
40
+ for i in api_data.get("results", [])[:max_results]:
41
+ tmp = {}
42
+ tmp["entity_id"] = i["id"]
43
+ tmp["name"] = i["title"][0]
44
+ tmp["search_score"] = i["score"]
45
+ tmp["channel_id"] = f"{self._facilities_channel}.{i['id'].rsplit('=')[-1].lower()}.{i['id']}"
46
+ our_results.append(tmp)
47
+ return our_results
48
+
49
+ def __call__(self, *args, **kwargs) -> Optional[list[dict]]:
50
+ """Call the search function
51
+
52
+ Returns:
53
+ (Optional[list[dict]]): List of results
54
+ """
55
+ return self.search(*args, **kwargs)
56
+
57
+ def __repr__(self):
58
+ return f"Search(destination='{self.destination}')"
@@ -2,7 +2,8 @@ import typing
2
2
  from datetime import datetime, timedelta
3
3
  from enum import Enum
4
4
 
5
- from mousetools.channels.enums import DestinationShort, DestinationTimezones, DLRCouchbaseChannels, WDWCouchbaseChannels
5
+ from mousetools.channels.enums import DestinationTimezones, DLRCouchbaseChannels, WDWCouchbaseChannels
6
+ from mousetools.enums import DestinationShort, EntityType
6
7
  from mousetools.mixins.couchbase import CouchbaseMixin
7
8
 
8
9
 
@@ -43,6 +44,15 @@ class CouchbaseChannel(CouchbaseMixin):
43
44
 
44
45
  return [i["id"] for i in self._cb_data["results"]]
45
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
+
46
56
  def __repr__(self):
47
57
  return f"{self.__class__.__name__}(channel_id='{self.channel_id}')"
48
58