Skip to content

✨ GoingToCamp Provider πŸ•#99

Merged
juftin merged 16 commits intojuftin:release/0.8.0from
acaloiaro:add-going-to-camp-support
Dec 22, 2022
Merged

✨ GoingToCamp Provider πŸ•#99
juftin merged 16 commits intojuftin:release/0.8.0from
acaloiaro:add-going-to-camp-support

Conversation

@acaloiaro
Copy link
Copy Markdown
Contributor

@acaloiaro acaloiaro commented Jul 19, 2022

This PR adds Going to Camp as a camply provider.

Addresses #94

Copy link
Copy Markdown
Contributor Author

@acaloiaro acaloiaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few items not yet noted by TODO in the code.

Comment thread camply/cli.py Outdated
Comment thread camply/cli.py Outdated
Comment thread camply/providers/going_to_camp/going_to_camp_provider.py Outdated
Comment thread camply/providers/going_to_camp/going_to_camp_provider.py Outdated
Comment thread camply/providers/going_to_camp/going_to_camp_provider.py Outdated
Comment thread camply/search/search_going_to_camp.py Outdated
search_window: Union[SearchWindow, List[SearchWindow]],
recreation_area: Optional[Union[List[int], int]] = None,
campsites: Optional[Union[List[int], int]] = None,
weekends_only: bool = False,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Support weekends only?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BaseCampingSearch base class should handle all of this heavy lifting.

Here's how it works inside with Recreation.Gov in the final get_all_campsites abstractmethod:

campsite_df = self.campsites_to_df(campsites=found_campsites)
campsite_df_validated = self._filter_date_overlap(campsites=campsite_df)
compiled_campsite_df = self._consolidate_campsites(
campsite_df=campsite_df_validated, nights=self.nights
)
equipment_filtered_campsites = self.filter_campsites_to_equipment(
campsites=compiled_campsite_df
)
compiled_campsites = self.df_to_campsites(
campsite_df=equipment_filtered_campsites
)

The idea is that found_campsites array is a list of AvailableCampsite objects which may or may not fit the search criteria. Every AvailableCampsite has a booking nights length of 1 and the _filter_date_overlap will take care of filtering to match dates (including weekends) and _consolidate_campsites will handle combining consecutive nights together for the same campsite/campground ID when the user specifies that nights > 1.

Yellowstone was a bit different than this since it allowed you to forward the nights param directly to the API to only return bookings matching that.

Comment thread camply/search/search_going_to_camp.py Outdated
Comment thread camply/search/search_going_to_camp.py Outdated
Comment thread camply/search/search_going_to_camp.py Outdated
@juftin juftin added the camply provider This is related to a new camply provider label Jul 29, 2022
@acaloiaro acaloiaro force-pushed the add-going-to-camp-support branch from 6e9b07e to dc38b65 Compare August 1, 2022 00:17
@acaloiaro
Copy link
Copy Markdown
Contributor Author

The current state of this PR Is that, due to each rec area having its own naming conventions for equipment, equipment matching needs to be improved.

@acaloiaro acaloiaro force-pushed the add-going-to-camp-support branch from dc38b65 to 75b1392 Compare August 1, 2022 00:20
Comment thread camply/cli.py Outdated
Comment thread camply/cli.py
"not case-sensitive.",
)
equipment_id_argument = click.option(
"--equipment-id",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@juftin What are your thoughts on the added complexity of having both --equipment and --equipment-id?

--equipment-id is the easiest way forward to support Going To Camp's native equipment type support, in lieu of the (equipment, length) tuple that powers Rec.gov and Yellowstone. I'm not in love with having both, and equally not in love with overloading --equipment to support both rec.gov and goingtocamp.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One middle ground alternative might be to break the --equipment API up into --equipment and --equipment-length (default 0, although I think -1 is more fitting sentinel value).

After breaking up the --equipment API it could be overloaded such that --equipment takes an equipment name, i.e. trailer, tent, etc -- or an equipment ID, for providers that support specific equipment out of the box.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to breaking it up. My idea with --equipment accepting a tuple of equipment-name and equipment-length is that it would allow you to specify for multiple types of equipment, with different lengths:

camply campsites \
    --rec-area 2991 \
    --start-date 2022-07-09 \
    --end-date 2022-07-17 \
    --nights 5 \
    --equipment RV 25 \
    --trailer 15

But now that I'm thinking about it - maybe that's not a common scenario and specifying a single equipment name (or id) and a single equipment length is more useful.

Copy link
Copy Markdown
Contributor Author

@acaloiaro acaloiaro Sep 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ‘ I'll take a stab at implementing them as two params and a single equipment-per-search. Seems like a reasonable middle ground.

@juftin
Copy link
Copy Markdown
Owner

juftin commented Aug 23, 2022

Hey @acaloiaro feedback is coming soon, I promise. I'm a little preoccupied with some work and personal things at the moment but I'll be following up here as soon as I can

@juftin juftin added BUMP_MINOR enhancement New feature or request labels Aug 31, 2022
@acaloiaro acaloiaro changed the title WIP: Add going to camp support Add going to camp support Sep 11, 2022
@acaloiaro acaloiaro force-pushed the add-going-to-camp-support branch from 0b2a1b7 to 5730500 Compare September 11, 2022 14:50
@acaloiaro
Copy link
Copy Markdown
Contributor Author

@juftin This PR is ready for a proper review.

Notable changes since you may have last seen it

  • --equipment and --equipment-length are separate.
  • I moved --provider on to the top-level of the CLI. Maybe this is too much of a breaking change. Happy to revert it if you think it doesn't make sense.

@acaloiaro acaloiaro marked this pull request as ready for review September 11, 2022 15:23
Comment thread camply/cli.py Outdated
@click.option(
"--debug/--no-debug", default=False, help="Enable extra debugging output."
)
@click.option(
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ‘ πŸ‘ @acaloiaro thank you so much - I'm starting to look at this on my end. I've got some big life events this week (🀡 πŸ’ πŸ‘°β€β™€οΈ ) and so review will be delayed.

One question on the --provider change:

  • I love moving --provider to the top level of the CLI. One question though, does this mean that passing it in after --campsites will break? I really like options like --profile in the AWS CLI where you can just throw it in wherever. I'm wondering if Click gives us any kind of flexibility on those universal options like --provider

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In its current implementation, I think it does break that behavior, but let me see if click will allow the alternate behavior at the same time.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW I still need to add some tests and manually test more things, like notifications and configuration file support, before this PR will actually be ready for consideration. I just wanted to get another set of eyes on it for now.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @acaloiaro following up here. I've actually moved --provider and --debug to the top level and lower level CLI namespaces via some Click Contexts on Release 0.7.0.

I've also added a Discord Server - it's pretty much empty but I wanted to make sure there's an easy place to collaborate for features like yours. Is there anything I can do to help you along on this right now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! I'm taking a look at this PR again now and will try to get it polished up.

@acaloiaro acaloiaro force-pushed the add-going-to-camp-support branch from 5730500 to 5baa965 Compare November 28, 2022 14:55
@acaloiaro acaloiaro force-pushed the add-going-to-camp-support branch from 4e79ec3 to 1bbb8a6 Compare November 28, 2022 20:15
@acaloiaro acaloiaro force-pushed the add-going-to-camp-support branch from 1bbb8a6 to 11111c4 Compare November 28, 2022 20:22
@acaloiaro acaloiaro force-pushed the add-going-to-camp-support branch from 0108ad7 to 711c098 Compare November 29, 2022 01:43
Comment on lines +25 to +35
@validator("start_date")
def start_date_must_be_in_future(self, v):
"""
Validate that start_date is in the future
"""
current_date = datetime.datetime.now().date()
print(v, current_date)
if v < current_date:
raise ValueError("must be in the future")

return v
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validators use cls instead of self - kind of confusing but that's what's throwing the error here: https://github.com/juftin/camply/actions/runs/3576943050/jobs/6015349570#step:8:427

I like the idea of validating the start date at the SearchWindow level - currently I coerce the SearchWindow into being compliant with the current date here:

def _get_search_days(self) -> List[datetime]:
"""
Retrieve Specific Days to Search For
Returns
-------
search_days: List[datetime]
Datetime days to search for reservations
"""
current_date = datetime.now().date()
search_nights = set()
for window in self.search_window:
generated_dates = {
date for date in window.get_date_range() if date >= current_date
}
search_nights.update(generated_dates)
if self.weekends_only is True:
logger.info("Limiting Search of Campgrounds to Weekend Availabilities")
search_nights = {x for x in search_nights if x.weekday() in [4, 5]}
if len(search_nights) > 0:
logger.info(
f"{len(search_nights)} booking nights selected for search, "
f"ranging from {min(search_nights)} to {max(search_nights)}"
)
else:
logger.info(SearchConfig.ERROR_MESSAGE)
raise RuntimeError(SearchConfig.ERROR_MESSAGE)
return list(sorted(search_nights))

If we need to set this data validation at the SearchWindow level these validators should coerce the dates into >= current date instead of raising an Error.

I kind of like the idea of being able to provide a start date in the past as long as the end date is in the future without raising an error. What do you think?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that makes sense; I'd prefer it to be more tolerant of bad input.

Comment on lines +143 to +147
timestamp_range = []
if type(date) is Timestamp:
timestamp_range = date_range(start=date.to_pydatetime(), periods=periods)
else:
timestamp_range = date_range(start=date, periods=periods)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice - I wish I could easily remember exactly where my datetime objects get converted into Timestamps and then become difficult to deal with.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally I remember intending to track that down and factoring this code out. I vaguely recall this having something to do with dates coming from a config file? Does that ring any bells?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are a couple places where I convert an array of AvailableCampsite objects into a pandas DataFrame, do some manipulation, and then transform them back into an array of AvailableCampsite - somehow tunring them into Timestamp along the way. This might be the big offender here:

@staticmethod
def df_to_campsites(campsite_df: DataFrame) -> List[AvailableCampsite]:
"""
Convert Campsite DataFrame to array of AvailableCampsite objects
Parameters
----------
campsite_df: DataFrame
Returns
-------
List[AvailableCampsite]
"""
composed_campsite_array = list()
composed_campsite_data_array = campsite_df.to_dict(orient="records")
for campsite_record in composed_campsite_data_array:
composed_campsite_array.append(AvailableCampsite(**campsite_record))
return composed_campsite_array

Comment thread camply/cli.py
Comment on lines +163 to +177
@camply_command_line.command()
@rec_area_argument
@provider_argument
@click.pass_obj
def equipment_types(
context: CamplyContext,
rec_area: Optional[int] = None,
provider: str = DEFAULT_CAMPLY_PROVIDER,
) -> None:
"""
Retrieve a list of equipment supported by the current provider/recreaton area

Equipment are camping equipment that can be used at a campsite. Different providers
and recreation areas have different types of equipment for which reservations can be made.
"""
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all looking very clean - it's a great implementation. I've got a couple questions on here but I'm close to getting this ready for merge.

There's currently a GitHub Actions Secrets error that occurs when I merge directly to main from an outside repo - let's target a different branch on juftin/camply and then I'll go to main from there.

I'd like to get some usage of GoingToCamp documented - including how to use the new equipment-types command. I've got a placeholder here: #156. If it's helpful we can merge this PR into that branch or merge both PRs into a new branch.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, let's make #156 the target for this branch to address both issues. And I'll take a look at #156 to see about adding more equipment-type documentation.

Comment thread camply/search/search_going_to_camp.py Outdated
Comment thread camply/search/search_going_to_camp.py Outdated
Comment thread camply/search/search_going_to_camp.py Outdated
sites = self.campsite_finder.list_site_availability(
campground,
self.search_window[0].start_date,
self.search_window[0].end_date,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This smells. It looks like we should be iterating over all search windows. I wasn't aware that multiple search windows were a valid use case.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are also a few of properties set on the BaseCampingSearch instance that might be useful:

self.search_days: List[datetime] = self._get_search_days()
self.search_months: List[datetime] = self._get_search_months()
self.nights = self._validate_consecutive_nights(nights=nights)

  • self.search_dates = An array of all Possible Dates from Among Your Search Window that are today or in the future
  • self.search_months = An array of all of the months that comprise the search window expressed as a first of the month dt object
  • self.search_nights = The number of nights to search - this is also validated so if you tell camply that you want to search for weekends only but you want to stay for 6 nights, it's adjusted to 5 - or if your search window is 3 days wide but you ask for 4 nights

Comment thread camply/cli.py
Comment on lines +163 to +177
@camply_command_line.command()
@rec_area_argument
@provider_argument
@click.pass_obj
def equipment_types(
context: CamplyContext,
rec_area: Optional[int] = None,
provider: str = DEFAULT_CAMPLY_PROVIDER,
) -> None:
"""
Retrieve a list of equipment supported by the current provider/recreaton area

Equipment are camping equipment that can be used at a campsite. Different providers
and recreation areas have different types of equipment for which reservations can be made.
"""
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, let's make #156 the target for this branch to address both issues. And I'll take a look at #156 to see about adding more equipment-type documentation.

@acaloiaro acaloiaro changed the base branch from main to feature/goingtocamp November 30, 2022 13:54
Comment on lines +109 to +120
# Rec area: Long Point Region
# Campground: Waterford North Conservation Area
# Equipment: 1 tent
camply \
--debug \
--provider goingtocamp \
campsites \
--rec-area 1 \
--start-date 2023-09-01 \
--end-date 2023-09-02 \
--equipment-id -32768 \
--campground -2147483643 \
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, these are great. Thank you so much for the testing contributions.

Copy link
Copy Markdown
Owner

@juftin juftin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel great with all of this - I'll leave it up to you for when you're fully happy with merging.

I'm guessing that currently I only have the permissions to hit those buttons. How should that merge go, any preferences? For me, ideally the final merge into main is single clean commit belonging to you.

@acaloiaro acaloiaro force-pushed the add-going-to-camp-support branch from f7f63b8 to d1092e7 Compare December 2, 2022 16:48
@juftin juftin changed the base branch from feature/goingtocamp to release/0.8.0 December 22, 2022 03:22
@juftin juftin changed the title Add going to camp support ✨ GoingToCamp Provider πŸ• Dec 22, 2022
@juftin
Copy link
Copy Markdown
Owner

juftin commented Dec 22, 2022

I'm going to move forward and get this code staged to go to main. It's going to an intermediate release branch in the meantime where I can finally bring my testing changes up to date with it. πŸš€

@juftin juftin merged commit 396666e into juftin:release/0.8.0 Dec 22, 2022
@juftin juftin mentioned this pull request Dec 22, 2022
4 tasks
@acaloiaro acaloiaro mentioned this pull request Dec 23, 2022
juftin pushed a commit that referenced this pull request Dec 27, 2022
* Add going to camp support

* Fix type hints for _fetch_nested_keys

* Add missing 'provider' argument to 'campsites()'

* Fix invalid equipment parameter name

* Fix provider config for recreation-areas

* flake8 can get some satisfaction

* Add python tests for going to camp search

* Fix recursive resource fetch bug

* Revert back to requipment-id instead of changing the --equipment API

* Check off the final TODO list

* Update camply/search/search_going_to_camp.py

* Update camply/search/search_going_to_camp.py

* Refactor 'self->cls' in validator

* Add multiple search window support to search library

* Coerce start date to 'today' when it's in the past

* Skip all sites with zero capacity
github-actions Bot pushed a commit that referenced this pull request Dec 27, 2022
# [v0.8.0](v0.7.6...v0.8.0) (2022-12-27)

## ✨ New Features
- [`6620f4c`](6620f4c)  GoingToCamp Provider πŸ• (#99) (Issues: [`#99`](#99))
juftin added a commit that referenced this pull request Jan 18, 2023
* 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).

* Flake8 + ABC

* 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

* πŸ› semantic-release

* πŸ”– 0.7.4

# [v0.7.4](v0.7.3...v0.7.4) (2022-12-22)

## πŸ› Bug Fixes
- [`6d7c682`](6d7c682)  semantic-release

* πŸ‘· publish pre-releases

* πŸ“ Docs + GA

* πŸ› Docker Home Directory (#173)

* πŸ”– 0.7.5

# [v0.7.5](v0.7.4...v0.7.5) (2022-12-22)

## πŸ› Bug Fixes
- [`6c92220`](6c92220)  Docker Home Directory (#173) (Issues: [`#173`](#173))

* πŸ› CI/CD Triggers (#174)

* πŸ”– 0.7.6

# [v0.7.6](v0.7.5...v0.7.6) (2022-12-22)

## πŸ› Bug Fixes
- [`92169f8`](92169f8)  CI/CD Triggers (#174) (Issues: [`#174`](#174))

* ✨ GoingToCamp Provider πŸ• (#99)

* Add going to camp support

* Fix type hints for _fetch_nested_keys

* Add missing 'provider' argument to 'campsites()'

* Fix invalid equipment parameter name

* Fix provider config for recreation-areas

* flake8 can get some satisfaction

* Add python tests for going to camp search

* Fix recursive resource fetch bug

* Revert back to requipment-id instead of changing the --equipment API

* Check off the final TODO list

* Update camply/search/search_going_to_camp.py

* Update camply/search/search_going_to_camp.py

* Refactor 'self->cls' in validator

* Add multiple search window support to search library

* Coerce start date to 'today' when it's in the past

* Skip all sites with zero capacity

* βœ… Testing Improvements: Cassettes and CLIRunner (#155)

* Add going to camp support

* Fix type hints for _fetch_nested_keys

* Add missing 'provider' argument to 'campsites()'

* Fix invalid equipment parameter name

* Fix provider config for recreation-areas

* flake8 can get some satisfaction

* Add python tests for going to camp search

* Fix recursive resource fetch bug

* Revert back to requipment-id instead of changing the --equipment API

* Testing Improvements: Cassettes and CLIRunner

* Testing Improvements: Cassettes and CLIRunner

* Testing Improvements: Cassettes and CLIRunner

* Testing Improvements: Cassettes and CLIRunner

* Testing Improvements: Cassettes and CLIRunner

* Check off the final TODO list

* Testing Improvements: Cassettes and CLIRunner

* Testing Improvements: Cassettes and CLIRunner

* Update camply/search/search_going_to_camp.py

* Update camply/search/search_going_to_camp.py

* Refactor 'self->cls' in validator

* Add multiple search window support to search library

* Coerce start date to 'today' when it's in the past

* GoingToCamp + Tests

* 🚧 Merging Latest

Co-authored-by: Adriano Caloiaro <code@adriano.fyi>
Co-authored-by: Adriano <3331648+acaloiaro@users.noreply.github.com>

* πŸ“Œ dependency updates (#172)

* πŸ”’ dependency updates

* πŸ”’ dependency updates

* πŸ”’ dependency updates

* πŸ“ Providers Documentation (#156)

* feature/goingtocamp

* Add a blurb about GoingToCamp provider (#175)

* GoingToCamp Documentation

* GoingToCamp Documentation

* GoingToCamp Documentation

Co-authored-by: Adriano Caloiaro <3331648+acaloiaro@users.noreply.github.com>

* πŸ”– 0.8.0

# [v0.8.0](v0.7.6...v0.8.0) (2022-12-27)

## ✨ New Features
- [`6620f4c`](6620f4c)  GoingToCamp Provider πŸ• (#99) (Issues: [`#99`](#99))

* πŸ“Œ Lock File

* Merge branch 'main' into tours

Co-authored-by: Jingyuan Liang <jingyuanliang@google.com>
Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net>
Co-authored-by: Adriano Caloiaro <3331648+acaloiaro@users.noreply.github.com>
Co-authored-by: Adriano Caloiaro <code@adriano.fyi>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

camply provider This is related to a new camply provider enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants