Skip to content

Commit

Permalink
camply 0.1.3 (#16)
Browse files Browse the repository at this point in the history
* 0.1.3

* Reworking Some Campground Searching Logic + CI Docker

* CI Fix

* Bugfix - Facilities Without Address (#14)

* CI Fix, *

* docker version CI

* declare variable env.

* only latest

* CI refactor

* CI issues

* Publish on Release CI

* only send emails for new campsites

* issue templates

* CHANGELOG.md

Co-authored-by: Grantland Chew <grantlandchew@gmail.com>
  • Loading branch information
juftin and grantland authored May 26, 2021
1 parent 0dfeb24 commit aec4c6b
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 81 deletions.
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Have a question about how to use camply?
url: https://github.com/juftin/camply/discussions
about: Check out camply discussions. There's lots of useful stuff there.
49 changes: 49 additions & 0 deletions .github/workflows/camply-publishing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: camply Publishing

on:
release:
types: [ published ]

jobs:

build:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v2

- name: Set up QEMU
uses: docker/setup-qemu-action@v1

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Declare Version Variable
run: |
cd ${GITHUB_WORKSPACE}
VERSION=$(grep -o '__version__ = .*' camply/__init__.py | sed "s/__version__ = //g" | sed 's/"//g')
echo CAMPLY_LATEST_VERSION=${VERSION} >> $GITHUB_ENV
- name: Docker Image Building and Publishing
id: docker_build
uses: docker/build-push-action@v2
with:
push: True
tags: juftin/camply:latest,juftin/camply:${{ env.CAMPLY_LATEST_VERSION }}

- name: Build and Publish to PyPi
run: |
python -m pip install --upgrade pip
python -m pip install poetry
poetry update
cd ${GITHUB_WORKSPACE} && poetry install
cd ${GITHUB_WORKSPACE} && poetry build
cd ${GITHUB_WORKSPACE} && poetry publish --username ${PYPI_USERNAME} --password ${PYPI_PASSWORD}
36 changes: 34 additions & 2 deletions .github/workflows/docker-image-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,38 @@ jobs:
runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v2
- name: Build the Docker image
run: docker build . --file Dockerfile --tag juftin/camply:$(date +%s)

- name: Set up QEMU
uses: docker/setup-qemu-action@v1

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Declare Version Variable
run: |
cd ${GITHUB_WORKSPACE}
VERSION=$(grep -o '__version__ = .*' camply/__init__.py | sed "s/__version__ = //g" | sed 's/"//g')
echo CAMPLY_LATEST_VERSION=${VERSION} >> $GITHUB_ENV
- name: Docker Image Building
id: docker_build
uses: docker/build-push-action@v2
with:
push: False
tags: juftin/camply:latest,juftin/camply:${{ env.CAMPLY_LATEST_VERSION }}

- name: Build the Poetry Package
run: |
python -m pip install --upgrade pip
python -m pip install poetry
poetry update
cd ${GITHUB_WORKSPACE} && poetry install
cd ${GITHUB_WORKSPACE} && poetry build
14 changes: 11 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ versioning.

## [Unreleased]

## [0.1.3] - 2021-05-25

### Added

- Camply is brand new, awaiting feedback before making changes.
- CI/CD for publishing camply to Docker Hub and PyPi

### Fixed

- Any bugs that surface will quickly have fixes quickly deployed before version
`1.0.0` is released.
- Addressed situations where campsites without addresses information were throwing a KeyError. (big
thanks to @grantland)
- This resolution presents an
interesting [discussion](https://github.com/juftin/camply/pull/14#issuecomment-848302948)
on whether campsites that have `Enabled` and `Reservable` = `False` should be searchable.
- Empty Email notifications issue resolved

## [0.1.2] - 2021-05-24

Expand Down Expand Up @@ -43,6 +49,8 @@ versioning.

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

[0.1.3]: https://github.com/juftin/camply/compare/v0.1.2...v0.1.3

[0.1.2]: https://github.com/juftin/camply/compare/v0.1.1...v0.1.2

[0.1.1]: https://github.com/juftin/camply/compare/v0.1.0...v0.1.1
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 <juftin@juftin.com>
LABEL version="0.1.2"
LABEL version="0.1.3"
LABEL description="camply, the campsite finder"

COPY . /tmp/camply
Expand Down
5 changes: 3 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ All! (and by all I mean just `0.1.x` for now)

## Reporting a Vulnerability

To report a vulnerability please use the [issues](https://github.com/juftin/camply/issues) page. You
can expect follow up on the issue thread.
To report a vulnerability please
contact [juftin@juftin.com](mailto:juftin@juftin.com?subject=[GitHub]%20camply%Security%20Issue).
You can expect the creation of a new issue thread for valid security vulnerabilities.
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.2"
__version__ = "0.1.3"
4 changes: 2 additions & 2 deletions camply/config/cli_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ class CommandLineValidation:
ERROR_NO_ARGUMENT_FOUND: str = "You must provide an argument to the Camply CLI"
ERROR_MESSAGE_RECREATION_AREA: str = ("You must add a --search or --state parameter to search "
"for Recreation Areas.")
ERROR_MESSAGE_CAMPGROUNDS: str = ("You must add a --search, --state, or --rec-area "
" parameter to search for Campgrounds.")
ERROR_MESSAGE_CAMPGROUNDS: str = ("You must add a --search, --state, --campground, or "
"--rec-area parameter to search for Campgrounds.")
ERROR_MESSAGE_REC_DOT_GOV: str = ("To search for Recreation.gov Campsites you must provide "
"either the --rec-area or the --campground parameters.")
ERROR_MESSAGE_CAMPSITES: str = ("Campsite searches require the following mandatory search "
Expand Down
3 changes: 2 additions & 1 deletion camply/notifications/email_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,5 @@ def send_campsites(campsites: List[AvailableCampsite], **kwargs) -> None:
composed_message = "\n".join(fields) + "\n\n"
master_email_body_list.append(composed_message)
master_email_body = "\n".join(master_email_body_list)
EmailNotifications.send_message(message=master_email_body)
if len(campsites) > 0:
EmailNotifications.send_message(message=master_email_body)
112 changes: 78 additions & 34 deletions camply/providers/recreation_dot_gov/campsite_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,10 @@ def find_recreation_areas(self, search_string: str = None, **kwargs) -> List[dic

def find_campgrounds(self, search_string: str = None,
rec_area_id: Optional[List[int]] = None,
campground_id: Optional[List[int]] = None, **kwargs) -> List[dict]:
campground_id: Optional[List[int]] = None, **kwargs) -> \
List[CampgroundFacility]:
"""
Find Matching Campsites Based on Search String
Find Bookable Campgrounds Given a Set of Search Criteria
Parameters
----------
Expand All @@ -109,42 +110,30 @@ def find_campgrounds(self, search_string: str = None,
Returns
-------
filtered_responses: List[dict]
facilities: List[CampgroundFacility]
Array of Matching Campsites
"""

if campground_id not in [None, list()]:
return self._ridb_get_data(path=f"{RIDBConfig.FACILITIES_API_PATH}/{campground_id}",
params=dict(full=True))
if rec_area_id is not None:
facilities = self._find_facilities_from_campgrounds(campground_id=campground_id)
elif rec_area_id is not None:
facilities = list()
for recreation_area in rec_area_id:
facilities += self.find_facilities_per_recreation_area(rec_area_id=recreation_area)
return facilities
facilities += self.find_facilities_per_recreation_area(
rec_area_id=recreation_area)
else:
state_arg = kwargs.get("state", None)
if state_arg is not None:
kwargs.update({"state": state_arg.upper()})
if search_string in ["", None] and state_arg is None:
raise RuntimeError("You must provide a search query or state to find campsites")
facilities_response = self._ridb_get_paginate(path=RIDBConfig.FACILITIES_API_PATH,
params=dict(query=search_string,
activity="CAMPING",
full="true",
**kwargs))
filtered_responses = self._filter_facilities_responses(responses=facilities_response)
logger.info(f"{len(filtered_responses)} Matching Campgrounds Found")

logging_messages = list()
for facility in filtered_responses:
_, campground_facility = self.process_facilities_responses(facility=facility)
if campground_facility is not None:
logging_messages.append(campground_facility)
self.log_sorted_response(response_array=logging_messages)
return filtered_responses

def find_facilities_per_recreation_area(self, rec_area_id: int = None, **kwargs) -> List[dict]:
facilities = self._find_facilities_from_search(search=search_string, **kwargs)
return facilities

def find_facilities_per_recreation_area(self, rec_area_id: int = None, **kwargs) -> \
List[CampgroundFacility]:
"""
Find Matching Campsites Based on Search String
Find Matching Campsites Based from Recreation Area
Parameters
----------
Expand All @@ -153,21 +142,76 @@ def find_facilities_per_recreation_area(self, rec_area_id: int = None, **kwargs)
Returns
-------
filtered_responses: List[dict]
campgrounds: List[CampgroundFacility]
Array of Matching Campsites
"""
logger.info(f"Retrieving Facility Information for Recreation Area ID: `{rec_area_id}`.")
api_path = f"{RIDBConfig.REC_AREA_API_PATH}/{rec_area_id}/{RIDBConfig.FACILITIES_API_PATH}"
api_response = self._ridb_get_paginate(path=api_path, params=dict(full="true", **kwargs))
filtered_facilities = self._filter_facilities_responses(responses=api_response)
logging_messages = list()
logger.info(f"{len(filtered_facilities)} camping facilities found")
campgrounds = list()
logger.info(f"{len(filtered_facilities)} Matching Campgrounds Found")
for facility in filtered_facilities:
_, campground_facility = self.process_facilities_responses(facility=facility)
if campground_facility is not None:
logging_messages.append(campground_facility)
self.log_sorted_response(response_array=logging_messages)
return filtered_facilities
campgrounds.append(campground_facility)
self.log_sorted_response(response_array=campgrounds)
return campgrounds

def _find_facilities_from_campgrounds(self, campground_id: Union[int, List[int]]) -> \
List[CampgroundFacility]:
"""
Find Matching Campsites from Campground ID
Parameters
----------
campground_id: Union[int, List[int]]
ID of the Campsite
Returns
-------
filtered_responses: List[CampgroundFacility]
Array of Matching Campsites
"""
campgrounds = list()
for campground_identifier in campground_id:
facility_data = self._ridb_get_data(
path=f"{RIDBConfig.FACILITIES_API_PATH}/{campground_identifier}",
params=dict(full=True))
filtered_facility = self._filter_facilities_responses(responses=[facility_data])
_, campground_facility = self.process_facilities_responses(
facility=filtered_facility[0])
if campground_facility is not None:
campgrounds.append(campground_facility)
logger.info(f"{len(campgrounds)} Matching Campgrounds Found")
self.log_sorted_response(response_array=campgrounds)
return campgrounds

def _find_facilities_from_search(self, search: str, **kwargs) -> List[dict]:
"""
Find Matching Campgrounds Based on Search String
Parameters
----------
search: str
Search String
Returns
-------
campgrounds: List[dict]
Array of Matching Campsites
"""
facilities_response = self._ridb_get_paginate(path=RIDBConfig.FACILITIES_API_PATH,
params=dict(query=search, activity="CAMPING",
full="true", **kwargs))
filtered_responses = self._filter_facilities_responses(responses=facilities_response)
logger.info(f"{len(filtered_responses)} Matching Campgrounds Found")
campgrounds = list()
for facility in filtered_responses:
_, campground_facility = self.process_facilities_responses(facility=facility)
if campground_facility is not None:
campgrounds.append(campground_facility)
self.log_sorted_response(response_array=campgrounds)
return campgrounds

@classmethod
def _ridb_get_endpoint(cls, path: str) -> str:
Expand Down Expand Up @@ -310,7 +354,7 @@ def process_facilities_responses(cls, facility: dict) -> \
try:
facility_state = facility[RIDBConfig.FACILITY_ADDRESS][0][
RIDBConfig.FACILITY_LOCATION_STATE].upper()
except IndexError:
except (KeyError, IndexError):
facility_state = "USA"
try:
recreation_area = facility[RIDBConfig.CAMPGROUND_RECREATION_AREA][0][
Expand All @@ -323,7 +367,7 @@ def process_facilities_responses(cls, facility: dict) -> \
facility_id=facility_id,
recreation_area_id=recreation_area_id)
return facility, campground_facility
except IndexError:
except (KeyError, IndexError):
return facility, None

@classmethod
Expand Down
Loading

0 comments on commit aec4c6b

Please sign in to comment.