Skip to content

Commit 54000e3

Browse files
✨ RecDotGov Tours + Tickets + Timed Entry (#189)
* ✨ RecDotGov Tours + Timed Entry (#151) * Add support for tours (ticket and timed-entry) as new providers A tour facility is currently modeled as a campground and each individual tour is a campsite. User interface was not updated yet but the logic does the work as expected. * Add RecreationDotGovDailyTicket and RecreationDotGovDailyTimedEntry With this endpoint, available time slots can be extracted. They're attached as equipments on each tour (as campsite). * RecreationDotGov Tours (#1) Flake8 + ABC * Fix Flake8 lint errors Co-authored-by: Justin Flannery <juftin@juftin.com> * ✨ RecDotGov Tours + Timed Entry * 📝 New Provider Docs (#190) * 📝 New Provider Docs * 📝 Provider Docs Co-authored-by: jingyuanliang <jingyuanliang@google.com>
1 parent 23424bc commit 54000e3

40 files changed

Lines changed: 34951 additions & 318 deletions

.github/workflows/lint.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@ jobs:
4040
run: |
4141
echo "Flake8: ${{ steps.flake8.outcome }}"
4242
echo "MyPy: ${{ steps.mypy.outcome }}"
43-
echo "I Should Really Be Enforcing MyPy Errors
43+
echo "I Should Really Be Enforcing MyPy Errors"
4444
exit 1

.pre-commit-config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ repos:
3434
- id: pretty-format-toml
3535
args: [--autofix]
3636

37+
- repo: https://github.com/PyCQA/autoflake
38+
rev: v2.0.0
39+
hooks:
40+
- id: autoflake
41+
args: [--in-place, --expand-star-imports, --remove-all-unused-imports, --remove-duplicate-keys, --remove-unused-variables, --ignore-pass-after-docstring]
42+
3743
# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date
3844
ci:
3945
autoupdate_schedule: weekly

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Camply doesn't support your favorite campsite booking provider yet? Consider
8383
* [RecreationDotGov](docs/providers.md#recreationgov)
8484
* [Yellowstone](docs/providers.md#yellowstone)
8585
* [GoingToCamp](docs/providers.md#goingtocamp)
86+
* [RecDotGov Tours + Tickets + Timed Entry](docs/providers.md#recreationgov-tickets-tours--timed-entry)
8687
- [Command Line Usage](docs/command_line_usage.md)
8788
* [campsites](docs/command_line_usage.md#campsites)
8889
* [recreation-areas](docs/command_line_usage.md#recreation-areas)
@@ -109,6 +110,10 @@ Camply doesn't support your favorite campsite booking provider yet? Consider
109110
+ [Search for Recreation Areas by Query String](docs/command_line_usage.md#search-for-recreation-areas-by-query-string)
110111
+ [Look for Specific Campgrounds Within a Recreation Area](docs/command_line_usage.md#look-for-specific-campgrounds-within-a-recreation-area)
111112
+ [Look for Specific Campgrounds by Query String](docs/command_line_usage.md#look-for-specific-campgrounds-by-query-string)
113+
+ [Searching for Tickets and Timed Entries](docs/ccommand_line_usage.md#searching-for-tickets-and-timed-entries)
114+
- [Tickets + Tours](docs/ccommand_line_usage.md#tickets-tours)
115+
- [Timed Entry](docs/ccommand_line_usage.md#timed-entry)
116+
- [Using the Daily Providers](command_line_usage.md#using-the-daily-providers)
112117
- [Finding Recreation Areas IDs and Campground IDs To Search Without Using the Command Line](docs/command_line_usage.md#finding-recreation-areas-ids-and-campground-ids-to-search-without-using-the-command-line)
113118
- [Object-Oriented Usage (Python)](docs/python.md)
114119
* [Search for a Recreation.gov Campsite](docs/python.md#search-for-a-recreationgov-campsite)

camply/cli.py

Lines changed: 25 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,8 @@ class CamplyContext:
4646
"--provider",
4747
show_default=False,
4848
default=None,
49-
help="Camping Search Provider. Options available are 'Yellowstone', "
50-
"'RecreationDotGov', and 'GoingToCamp'. Defaults to 'RecreationDotGov'"
51-
", not case-sensitive.",
49+
type=click.Choice(CAMPSITE_SEARCH_PROVIDER.keys(), case_sensitive=False),
50+
help="Camping Search Provider. Defaults to 'RecreationDotGov', not case-sensitive.",
5251
)
5352
debug_option = click.option(
5453
"--debug/--no-debug", default=None, help="Enable extra debugging output"
@@ -85,11 +84,11 @@ def _preferred_provider(context: CamplyContext, command_provider: Optional[str])
8584
The preferred provider is returned
8685
"""
8786
if command_provider:
88-
return command_provider.lower()
87+
return command_provider
8988
elif command_provider is None and context.provider:
90-
return context.provider.lower()
89+
return context.provider
9190
else:
92-
return DEFAULT_CAMPLY_PROVIDER.lower()
91+
return DEFAULT_CAMPLY_PROVIDER
9392

9493

9594
@click.group()
@@ -213,38 +212,24 @@ def recreation_areas(
213212
if context.debug is None:
214213
context.debug = debug
215214
_set_up_debug(debug=context.debug)
216-
217-
# Recreation dot gov and yellowstone require --state or --search, but going to
218-
# camp does not, since all of its "rec areas" are a very few.
219-
if all([search is None, state is None, provider != GOING_TO_CAMP]):
220-
logger.error(
221-
"You must add a --search, --state, or --provider parameter "
222-
"to search for Recreation Areas."
223-
)
224-
exit(1)
225-
if all([search is None, state is not None, provider == GOING_TO_CAMP]):
215+
if all(
216+
[search is None, state is not None, provider in [YELLOWSTONE, GOING_TO_CAMP]]
217+
):
218+
# State Filtering Not Supported
226219
logger.error(
227-
"GoingToCamp does not support filtering recreation areas by state. Leave --state blank."
220+
f"{provider} does not support filtering recreation areas by state. Leave --state blank."
228221
)
229222
exit(1)
230-
231-
camp_provider = None
232-
if provider == RECREATION_DOT_GOV:
233-
camp_provider = RecreationDotGov()
234-
elif provider == GOING_TO_CAMP:
235-
camp_provider = GoingToCampProvider()
223+
if provider == GOING_TO_CAMP:
224+
rec_area_finder = GoingToCampProvider()
225+
elif provider.startswith(RECREATION_DOT_GOV):
226+
rec_area_finder = RecreationDotGov()
236227
else:
237-
logger.error(
238-
"The provider you specified does not exist or does notsupport"
239-
"listing recreation areas. See --help for available providers"
240-
)
241-
exit(1)
242-
228+
rec_area_finder = CAMPSITE_SEARCH_PROVIDER[provider]
243229
params = dict()
244230
if state is not None:
245231
params.update(dict(state=state))
246-
247-
camp_provider.find_recreation_areas(search_string=search, **params)
232+
rec_area_finder.find_recreation_areas(search_string=search, **params)
248233

249234

250235
@camply_command_line.command()
@@ -264,7 +249,7 @@ def campgrounds(
264249
rec_area: Optional[int] = None,
265250
campground: Optional[int] = None,
266251
campsite: Optional[int] = None,
267-
provider: str = DEFAULT_CAMPLY_PROVIDER,
252+
provider: Optional[str] = DEFAULT_CAMPLY_PROVIDER,
268253
) -> None:
269254
"""
270255
Search for Campgrounds (inside of Recreation Areas) and list them
@@ -278,7 +263,6 @@ def campgrounds(
278263
if context.debug is None:
279264
context.debug = debug
280265
_set_up_debug(debug=context.debug)
281-
282266
if provider == YELLOWSTONE:
283267
SearchYellowstone.print_campgrounds()
284268
exit(0)
@@ -296,11 +280,10 @@ def campgrounds(
296280
"or --rec-area parameter to search for Campgrounds."
297281
)
298282
exit(1)
299-
300283
if provider == YELLOWSTONE:
301284
SearchYellowstone.print_campgrounds()
302285
exit(0)
303-
if provider == GOING_TO_CAMP:
286+
elif provider == GOING_TO_CAMP:
304287
if len(rec_area) == 0:
305288
logger.error("You must specify at least one --rec-area")
306289
exit(1)
@@ -309,8 +292,8 @@ def campgrounds(
309292
rec_area_id=rec_area_id, campground_id=campground, search_string=search
310293
)
311294
exit(0)
312-
313-
camp_finder = RecreationDotGov()
295+
search_provider_class = CAMPSITE_SEARCH_PROVIDER[provider]
296+
camp_finder = search_provider_class.provider_class()
314297
params = dict()
315298
if state is not None:
316299
params.update(dict(state=state))
@@ -471,7 +454,7 @@ def _validate_campsites(
471454
"""
472455
Validate the campsites portion of the CLI
473456
"""
474-
if provider == RECREATION_DOT_GOV and all(
457+
if provider.startswith(RECREATION_DOT_GOV) and all(
475458
[
476459
len(rec_area) == 0,
477460
len(campground) == 0,
@@ -561,7 +544,6 @@ def campsites(
561544
if context.debug is None:
562545
context.debug = debug
563546
_set_up_debug(debug=context.debug)
564-
565547
notifications = make_list(notifications)
566548
_validate_campsites(
567549
rec_area=rec_area,
@@ -583,7 +565,9 @@ def campsites(
583565
provider, provider_kwargs, search_kwargs = yaml_utils.yaml_file_to_arguments(
584566
file_path=yaml_config
585567
)
586-
provider = provider.lower()
568+
for provider_str in CAMPSITE_SEARCH_PROVIDER.keys():
569+
if provider.lower() == provider_str.lower():
570+
provider = provider_str
587571
else:
588572
search_window = SearchWindow(
589573
start_date=datetime.strptime(start_date, "%Y-%m-%d"),
@@ -610,9 +594,7 @@ def campsites(
610594
notification_provider=notifications,
611595
search_forever=search_forever,
612596
)
613-
provider_class = {
614-
key.lower(): value for key, value in CAMPSITE_SEARCH_PROVIDER.items()
615-
}[provider]
597+
provider_class = CAMPSITE_SEARCH_PROVIDER[provider]
616598
camping_finder = provider_class(**provider_kwargs)
617599
camping_finder.get_matching_campsites(**search_kwargs)
618600

camply/config/api_config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,14 @@ class RIDBConfig:
199199
# FACILITIES_API_PATH FIELDS
200200
FACILITIES_API_PATH: str = "facilities"
201201
CAMPGROUND_FACILITY_FIELD_QUALIFIER: str = "Campground"
202+
TICKET_FACILITY_FIELD_QUALIFIER: str = "Ticket Facility"
203+
TIMED_ENTRY_FACILITY_FIELD_QUALIFIER: str = "Timed Entry"
202204
# RECREATION AREA FIELDS
203205
REC_AREA_API_PATH: str = "recareas"
204206
# CAMPSITE DETAILS
205207
CAMPSITE_API_PATH: str = "campsites"
208+
# TOUR DETAILS
209+
TOUR_API_PATH: str = "tours"
206210

207211

208212
class RecreationBookingConfig:

camply/config/search_config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,10 @@ class EquipmentConfig:
6565
for key, list_of_values in EQUIPMENT_MAPPING.items():
6666
for value in list_of_values:
6767
EQUIPMENT_REVERSE_MAPPING[value] = key
68+
69+
TIMESTAMP_EQUIPMENT = []
70+
for hour in range(0, 24):
71+
hour_str = str(hour).zfill(2)
72+
for minute in range(0, 60):
73+
minute_str = str(minute).zfill(2)
74+
TIMESTAMP_EQUIPMENT.append(hour_str + minute_str)

camply/config/yellowstone_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class YellowstoneConfig(DataColumns):
6969

7070
YELLOWSTONE_RECREATION_AREA_ID: int = 1
7171
YELLOWSTONE_RECREATION_AREA_NAME: str = "Yellowstone"
72+
YELLOWSTONE_RECREATION_AREA_FULL_NAME: str = "Yellowstone National Park"
7273
YELLOWSTONE_RECREATION_AREA_FORMAL_NAME: str = "Yellowstone National Park, USA"
7374
YELLOWSTONE_LOOP_NAME: str = "N/A"
7475
CAMPSITE_AVAILABILITY_STATUS: str = "Available"

0 commit comments

Comments
 (0)