Skip to content

Commit

Permalink
camply 0.1.5 (#18)
Browse files Browse the repository at this point in the history
* version bump -> 0.1.5

* pushbullet example

* Yellowstone Campground Filtering

* fix error

* rmnp test

* single day search

* yellwsnte doc clarification

* PyYaml inclusion

* config strings for Yellowstone

* second yml test

* YML File Path expander

* Changelog update

* Changelog bullet

* debug logging for ubuntu

* let yellowstone timeout

* fix parallel jobs

* 30 second timeout + less frequent testing

* CHANGELOG.md update

* YML Cleanup
  • Loading branch information
juftin authored Jun 3, 2021
1 parent 0071fea commit 55b76fa
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 84 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/docker-image-ci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
name: Docker Image CI

on:
push:
branches: [ "**" ]
pull_request:
branches: [ main ]
branches: [ "**" ]

jobs:

Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/pylint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name: PyLint
on:
push:
branches: [ "**" ]
pull_request:
branches: [ main ]

jobs:
build:
Expand Down
52 changes: 9 additions & 43 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
name: Tests

on:
push:
branches: [ "**" ]
pull_request:
branches: [ main ]
branches: [ "**" ]

jobs:
ubuntu:
pytest:
runs-on: ubuntu-latest
strategy:
fail-fast: false
fail-fast: false
matrix:
python-version: [ 3.6, 3.7, 3.8, 3.9 ]
max-parallel: 1
env:
LOG_LEVEL: DEBUG
RIDB_API_KEY: ${{ secrets.RIDB_API_KEY }}
steps:
- uses: actions/checkout@v2
- name: Set up Python Environment ${{ matrix.python-version }}
Expand Down Expand Up @@ -40,41 +42,5 @@ jobs:
camply campgrounds --search "Fire Tower Lookout" --state CA
camply campsites --rec-area 2991 --start-date 2021-09-15 --end-date 2021-09-16
camply campsites --campground 252037 --start-date 2021-09-15 --end-date 2021-09-16
windows:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
python-version: [ 3.6, 3.7, 3.8, 3.9 ]
steps:
- uses: actions/checkout@v2
- name: Set up Python Environment ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest pylint
pip install .
shell: cmd
- name: "Lint (flake8)"
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
shell: cmd
- name: "Unit Tests (pytest)"
run: |
pytest tests/
shell: cmd
- name: "Unit Tests (commandline)"
run: |
camply recreation-areas --search "Yosemite National Park"
camply campgrounds --rec-area 2991
camply campgrounds --search "Fire Tower Lookout" --state CA
camply campsites --rec-area 2991 --start-date 2021-09-15 --end-date 2021-09-16
camply campsites --campground 252037 --start-date 2021-09-15 --end-date 2021-09-16
shell: cmd
camply campsites --provider yellowstone --start-date 2021-09-10 --end-date 2021-09-15 --campground YLYC:RV
camply campsites --yml-config tests/yml/example_search.yml
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ versioning.

## [Unreleased]

To request new features or bugfixes the
[Camply Feedback for v1.0.0 Discussion](https://github.com/juftin/camply/discussions/12) is the best
place to go.

## [0.1.5] - 2021-06-02

### Added

- Ability to filter on Yellowstone Campgrounds. See
this [discussion](https://github.com/juftin/camply/discussions/15#discussioncomment-783657) for
more detail.

### Fixed

- Allow endpoints to timeout after being unresponsive for 30 seconds

## [0.1.4] - 2021-06-01

### Added
Expand Down Expand Up @@ -60,6 +76,8 @@ versioning.

[unreleased]: https://github.com/juftin/camply/compare/main...integration

[0.1.5]: https://github.com/juftin/camply/compare/v0.1.4...v0.1.5

[0.1.4]: https://github.com/juftin/camply/compare/v0.1.3...v0.1.4

[0.1.3]: https://github.com/juftin/camply/compare/v0.1.2...v0.1.3
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM python:3.8-slim

MAINTAINER Justin Flannery <[email protected]>
LABEL version="0.1.4"
LABEL version="0.1.5"
LABEL description="camply, the campsite finder"

COPY . /tmp/camply
Expand Down
34 changes: 29 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ ___________
+ [Searching for a Campsite by Campground ID](#searching-for-a-campsite-by-campground-id)
+ [Continuously Searching for A Campsite](#continuously-searching-for-a-campsite)
+ [Continue Looking After The First Match Is Found](#continue-looking-after-the-first-match-is-found)
+ [Send a Push Notification](#send-a-push-notification)
+ [Look for Weekend Campsite Availabilities](#look-for-weekend-campsite-availabilities)
+ [Look for a Campsite Inside of Yellowstone](#look-for-a-campsite-inside-of-yellowstone)
+ [Look for a Campsite Across Multiple Recreation areas](#look-for-a-campsite-across-multiple-recreation-areas)
Expand Down Expand Up @@ -145,11 +146,11 @@ and a link to make the booking. Required parameters include `--start-date`, `--e
* `--notifications`: `NOTIFICATIONS`
+ If `--continuous` is activated, types of notifications to receive. Options available are
`email`, `pushover`, `pushbullet`, or `silent`. Defaults to `silent` - which just logs
messages to console. [**_example_](#continuously-searching-for-a-campsite)
messages to console. [**_example_](#send-a-push-notification)
* `--notify-first-try`
+ If `--continuous` is activated, whether to send all non-silent notifications if more than 5
matching campsites are found on the first try. Defaults to false which only sends the first
5. [**_example_](#continuously-searching-for-a-campsite)
matching campsites are found on the first try. Defaults to false which only sends
the first 5. [**_example_](#continuously-searching-for-a-campsite)
* `--search-forever`
+ If `--continuous` is activated, this method continues to search after the first availability
has been found. The one caveat is that it will never notify about the same identical campsite
Expand Down Expand Up @@ -299,6 +300,25 @@ camply campsites \
--search-forever
```

#### Send a Push Notification

camply supports notifications via `Pushbullet`, `Pushover`, and `Email`. Pushbullet is a great
option because it's
a [free and easy service to sign up for](https://www.pushbullet.com/#settings/account)
and it supports notifications across different devices and operating systems. Similar to `Pushover`,
`Pushbullet` requires that you create an account and an API token, and share that token with camply
through a [configuration file](docs/examples/example.camply) (via the `camply configure`
command) or though environment variables (`PUSHBULLET_API_TOKEN`).

```text
camply campsites \
--rec-area 2991 \
--start-date 2021-09-10 \
--end-date 2021-09-20 \
--continuous \
--notifications pushbullet
```

#### Look for Weekend Campsite Availabilities

This below search looks across larger periods of time, but only if a campground is available to book
Expand All @@ -320,8 +340,10 @@ camply campsites \

Yellowstone doesn't use https://recreation.gov to manage its campgrounds, instead it uses its own
proprietary system. In order to search the Yellowstone API for campsites, make sure to pass
the `--provider "yellowstone"` argument. This flag disables `--rec-area` and `--campground`
arguments.
the `--provider "yellowstone"` argument. This flag disables `--rec-area` argument.

To learn more about using `camply` to find campsites at Yellowstone, check out
this [discussion](https://github.com/juftin/camply/discussions/15#discussioncomment-783657).

```text
camply campsites \
Expand Down Expand Up @@ -580,6 +602,8 @@ dependencies:
- [python-dotenv](https://github.com/theskumar/python-dotenv)
- The `python-dotenv` package reads key-value pairs from a `.env` file and can set them as
environment variables - this helps with the `.camply` configuration file.
- [PyYAML](https://pyyaml.org/)
- PyYAML is a YAML parsing library - this helps with the YAML file campsite searches.

___________
___________
Expand Down
2 changes: 1 addition & 1 deletion camply/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
camply __init__ file
"""

__version__ = "0.1.4"
__version__ = "0.1.5"
27 changes: 10 additions & 17 deletions camply/config/yellowstone_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"""

import logging
from typing import List, Tuple
from typing import Dict, List, Tuple

from .data_columns import DataColumns

Expand Down Expand Up @@ -68,25 +68,18 @@ class YellowstoneConfig(DataColumns):

YELLOWSTONE_RECREATION_AREA_ID: int = 1
YELLOWSTONE_RECREATION_AREA_NAME: str = "Yellowstone"
YELLOWSTONE_RECREATION_AREA_FORMAL_NAME: str = "Yellowstone National Park, USA"
YELLOWSTONE_LOOP_NAME: str = "N/A"
CAMPSITE_AVAILABILITY_STATUS: str = "Available"
YELLOWSTONE_CAMPGROUND_NAME_REPLACE: Tuple[str, str] = ("CG Internet Rate", "Campground")

YELLOWSTONE_TIMEZONE: str = "America/Denver"

@staticmethod
def get_polling_interval(interval: int) -> int:
"""
Ensure the Polling Interval never exceeds the minimum set
Returns
-------
int
"""
if interval < YellowstoneConfig.MINIMUM_POLLING_INTERVAL:
logger.warning("Polling interval is too short, setting "
f"to {YellowstoneConfig.MINIMUM_POLLING_INTERVAL} minutes")
return_interval = YellowstoneConfig.MINIMUM_POLLING_INTERVAL
else:
return_interval = interval
return return_interval
# LODGES: https://webapi.xanterra.net/v1/api/property/hotels/yellowstonenationalparklodges
YELLOWSTONE_CAMPGROUNDS: Dict[str, str] = {
"YLYC:RV": "Canyon Campground",
"YLYB:RV": "Bridge Bay Campground",
"YLYG:RV": "Grant Campground",
"YLYM:RV": "Madison Campground",
"YLYF:RV": "Fishing Bridge RV Park"
}
5 changes: 3 additions & 2 deletions camply/providers/recreation_dot_gov/campsite_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def _ridb_get_data(self, path: str, params: Optional[dict] = None) -> Union[dict
"""
api_endpoint = self._ridb_get_endpoint(path=path)
response = requests.get(url=api_endpoint, headers=self._ridb_api_headers,
params=params)
params=params, timeout=30)
try:
assert response.status_code == 200
except AssertionError:
Expand Down Expand Up @@ -482,7 +482,8 @@ def _make_recdotgov_request(self, campground_id: int, month: datetime) -> reques
headers.update(choice(USER_AGENTS))
headers.update(RecreationBookingConfig.API_REFERRERS)
response = requests.get(url=api_endpoint, headers=headers,
params=dict(start_date=formatted_month))
params=dict(start_date=formatted_month),
timeout=30)
assert response.status_code == 200
except AssertionError:
response_error = response.text
Expand Down
2 changes: 1 addition & 1 deletion camply/providers/xanterra/yellowstone_lodging.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def _try_retry_get_data(endpoint: str, params: Optional[dict] = None):
yellowstone_headers.update(YellowstoneConfig.API_REFERRERS)
response = requests.get(url=endpoint,
headers=yellowstone_headers,
params=params)
params=params, timeout=30)
if response.status_code == 200 and response.text.strip() != "":
return loads(response.content)
else:
Expand Down
65 changes: 60 additions & 5 deletions camply/search/search_yellowstone.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@

from datetime import datetime
import logging
from typing import List, Union
from typing import List, Optional, Set, Union

from camply.config import YellowstoneConfig
from camply.containers import AvailableCampsite, SearchWindow
from camply.providers import YellowstoneLodging
from camply.search.base_search import BaseCampingSearch
from camply.search.base_search import BaseCampingSearch, SearchError

logger = logging.getLogger(__name__)

Expand All @@ -25,6 +26,7 @@ class SearchYellowstone(BaseCampingSearch):
# noinspection PyUnusedLocal
def __init__(self, search_window: Union[SearchWindow, List[SearchWindow]],
weekends_only: bool = False,
campgrounds: Optional[Union[List[str], str]] = None,
**kwargs) -> None:
"""
Initialize with Search Parameters
Expand All @@ -33,15 +35,16 @@ def __init__(self, search_window: Union[SearchWindow, List[SearchWindow]],
----------
search_window: Union[SearchWindow, List[SearchWindow]]
Search Window tuple containing start date and End Date
campgrounds: Optional[Union[List[int], int]]
Campground ID or List of Campground IDs
weekends_only: bool
Whether to only search for Camping availabilities on the weekends (Friday /
Saturday nights)
campgrounds: Optional[Union[List[str], str]]
Campground ID or List of Campground IDs
"""
super().__init__(provider=YellowstoneLodging(),
search_window=search_window,
weekends_only=weekends_only)
self.campgrounds = self._make_list(campgrounds)

def get_all_campsites(self) -> List[AvailableCampsite]:
"""
Expand All @@ -52,8 +55,60 @@ def get_all_campsites(self) -> List[AvailableCampsite]:
List[AvailableCampsite]
"""
all_campsites = list()
searchable_campgrounds = self._get_searchable_campgrounds()
this_month = datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
for month in self.search_months:
if month >= this_month:
all_campsites += self.campsite_finder.get_monthly_campsites(month=month)
return all_campsites
matching_campsites = self._filter_campsites_to_campgrounds(
campsites=all_campsites, searchable_campgrounds=searchable_campgrounds)
return matching_campsites

def _get_searchable_campgrounds(self) -> Optional[Set[str]]:
"""
Return the Campgrounds for the Camping Search
Returns
-------
Optional[Set[str]]
"""
if self.campgrounds is None:
return None
supported_campsites = set(YellowstoneConfig.YELLOWSTONE_CAMPGROUNDS.keys())
selected_campsites = set(self.campgrounds)
searchable_campgrounds = supported_campsites.intersection(selected_campsites)
if len(searchable_campgrounds) == 0:
campground_ids = [f"`{key}` ({value})" for key, value in
YellowstoneConfig.YELLOWSTONE_CAMPGROUNDS.items()]
error_message = ("You must supply a YellowstoneNationalParkLodges supported "
"campground ID. Current supported Campground IDs: "
f"{', '.join(campground_ids)}")
logger.error(error_message)
raise SearchError(error_message)
logger.info(f"{len(searchable_campgrounds)} Matching Campgrounds Found")
for campground in searchable_campgrounds:
logger.info(f"⛰ {YellowstoneConfig.YELLOWSTONE_RECREATION_AREA_FORMAL_NAME} "
f"(#{YellowstoneConfig.YELLOWSTONE_RECREATION_AREA_ID}) - 🏕 "
f"{YellowstoneConfig.YELLOWSTONE_CAMPGROUNDS[campground]} ({campground})")
return searchable_campgrounds

def _filter_campsites_to_campgrounds(
self, campsites: List[AvailableCampsite],
searchable_campgrounds: Set[str]) -> List[AvailableCampsite]:
"""
Filter Campsites Down to Matching Campgrounds
Parameters
----------
campsites: List[AvailableCampsite]
searchable_campgrounds: Set[str]
Returns
-------
List[AvailableCampsite]
"""
if self.campgrounds is None:
return campsites
matching_campsites = [campsite for campsite in campsites if
campsite.facility_id in searchable_campgrounds]
return matching_campsites
Loading

0 comments on commit 55b76fa

Please sign in to comment.