Skip to content

Commit

Permalink
✨ Multiple Search Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
juftin committed Mar 25, 2023
1 parent 1fbf802 commit a5eddb1
Show file tree
Hide file tree
Showing 14 changed files with 23,507 additions and 32 deletions.
34 changes: 13 additions & 21 deletions camply/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import logging
import sys
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple, Union

import click
Expand All @@ -25,6 +24,7 @@
)
from camply.search import CAMPSITE_SEARCH_PROVIDER
from camply.utils import configure_camply, log_camply, make_list, yaml_utils
from camply.utils.general_utils import handle_search_windows
from camply.utils.logging_utils import log_sorted_response

logging.Logger.camply = log_camply
Expand Down Expand Up @@ -318,11 +318,13 @@ def campgrounds(
# `campsite` arguments
start_date_argument = click.option(
"--start-date",
multiple=True,
default=None,
help="(YYYY-MM-DD) Start of Search window. You will be arriving this day.",
)
end_date_argument = click.option(
"--end-date",
multiple=True,
default=None,
help="(YYYY-MM-DD) End of Search window. You will be checking out this day.",
)
Expand Down Expand Up @@ -465,8 +467,8 @@ def _validate_campsites(
rec_area: Optional[int],
campground: Optional[int],
campsite: Optional[int],
start_date: Optional[str],
end_date: Optional[str],
start_date: Tuple[str],
end_date: Tuple[str],
provider: str,
yaml_config: Optional[str],
continuous: bool,
Expand All @@ -476,7 +478,7 @@ def _validate_campsites(
search_forever: bool,
search_once: bool,
**kwargs: Dict[str, Any],
) -> bool:
) -> Tuple[bool, List[SearchWindow]]:
"""
Validate the campsites portion of the CLI
Expand Down Expand Up @@ -517,16 +519,10 @@ def _validate_campsites(
"parameters."
)
sys.exit(1)

mandatory_parameters = [start_date, end_date]
mandatory_string_parameters = ["--start-date", "--end-date"]
for field in mandatory_parameters:
if field is None and yaml_config is None:
logger.error(
"Campsite searches require the following mandatory search parameters: "
f"{', '.join(mandatory_string_parameters)}"
)
sys.exit(1)
if yaml_config is None:
search_windows = handle_search_windows(start_date=start_date, end_date=end_date)
else:
search_windows = ()
if search_once is True and (continuous is True or search_forever is not None):
logger.error(
"You cannot specify `--search-once` alongside `--continuous` or `--search-forever`"
Expand All @@ -543,7 +539,7 @@ def _validate_campsites(
]
):
continuous = True
return continuous
return continuous, search_windows


@camply_command_line.command(cls=RichCommand)
Expand Down Expand Up @@ -606,7 +602,7 @@ def campsites(
context.debug = debug
_set_up_debug(debug=context.debug)
notifications = make_list(notifications)
continuous = _validate_campsites(
continuous, search_windows = _validate_campsites(
rec_area=rec_area,
campground=campground,
campsite=campsite,
Expand Down Expand Up @@ -644,12 +640,8 @@ def campsites(
if provider.lower() == provider_str.lower():
provider = provider_str
else:
search_window = SearchWindow(
start_date=datetime.strptime(start_date, "%Y-%m-%d"),
end_date=datetime.strptime(end_date, "%Y-%m-%d"),
)
provider_kwargs = {
"search_window": search_window,
"search_window": search_windows,
"recreation_area": make_list(rec_area),
"campgrounds": make_list(campground),
"campsites": make_list(campsite),
Expand Down
36 changes: 35 additions & 1 deletion camply/utils/general_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
"""

import logging
from typing import Any, Callable, List, Optional
import sys
from datetime import date
from typing import Any, Callable, Iterable, List, Optional, Union

from camply import SearchWindow
from camply.containers.base_container import CamplyModel

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -34,3 +37,34 @@ def make_list(obj, coerce: Optional[Callable] = None) -> Optional[List[Any]]:
return list(obj)
else:
return [coerce(obj) if coerce is not None else obj]


def handle_search_windows(
start_date: Union[Iterable[str], str], end_date: Union[Iterable[str], str]
) -> Union[List[SearchWindow], SearchWindow]:
"""
Handle Multiple Search Windows by the CLI
"""
if isinstance(start_date, (str, date)):
start_date = (start_date,)
assert isinstance(end_date, (str, date))
end_date = (end_date,)
search_windows: List[SearchWindow] = []
for field in [start_date, end_date]:
if field is None or (isinstance(field, (tuple, list)) and len(field) == 0):
logger.error("Campsite searches require a `start_date` and an `end_date`")
sys.exit(1)
if len(start_date) != len(end_date):
logger.error(
"When searching multiple date windows, you must provide the same amount "
"of `--start-dates` as `--end-dates`"
)
sys.exit(1)
for index, date_str in enumerate(start_date):
search_windows.append(
SearchWindow(start_date=date_str, end_date=end_date[index])
)
if len(search_windows) == 1:
return search_windows[0]
else:
return search_windows
11 changes: 5 additions & 6 deletions camply/utils/yaml_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import logging
import os
from datetime import datetime
from pathlib import Path
from re import compile
from typing import Any, Dict, Tuple
Expand All @@ -13,8 +12,8 @@
from yaml import SafeLoader, load

from camply.config import SearchConfig
from camply.containers import SearchWindow
from camply.utils import make_list
from camply.utils.general_utils import handle_search_windows

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -98,8 +97,10 @@ def yaml_file_to_arguments(
yaml_search = read_yaml(path=file_path)
logger.info(f"YAML File Parsed: {Path(file_path).name}")
provider = yaml_search.get("provider", "RecreationDotGov")
start_date = datetime.strptime(str(yaml_search["start_date"]), "%Y-%m-%d")
end_date = datetime.strptime(str(yaml_search["end_date"]), "%Y-%m-%d")
search_window = handle_search_windows(
start_date=yaml_search.get("start_date", []),
end_date=yaml_search.get("end_date", []),
)
nights = int(yaml_search.get("nights", 1))
recreation_area = yaml_search.get("recreation_area", None)
campgrounds = yaml_search.get("campgrounds", None)
Expand All @@ -120,8 +121,6 @@ def yaml_file_to_arguments(
offline_search = yaml_search.get("offline_search", False)
offline_search_path = yaml_search.get("offline_search_path", None)

search_window = SearchWindow(start_date=start_date, end_date=end_date)

provider_kwargs = {
"search_window": search_window,
"recreation_area": recreation_area,
Expand Down
24 changes: 24 additions & 0 deletions docs/command_line_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,30 @@ camply campsites \
always encouraged to perform an initial online search before setting up a `camply` search. To bypass
this behavior and send all notifications, pass the `--notify-first-try` argument.

### Searching Across Multiple Time Windows

There might be an occasion where you're looking to search for a campsite across
multiple date ranges, i.e. any Monday in July. `camply` supports this quite nicely by
passing multiple `--start-date` and `--end-date` options.

!!! note

The important rule about multiple **`--start-date`** and **`--end-date`** options
is that ordering matters. If you provide multiple start dates and multiple end
dates the first end date will correspond to the first start date, and so on
and so forth.

```commandline
camply campsites \
--rec-area 2725 \
--start-date 2023-07-12 \
--end-date 2023-07-13 \
--start-date 2023-07-19 \
--end-date 2023-07-20 \
--start-date 2023-07-26 \
--end-date 2023-07-27
```

### Continue Looking After The First Match Is Found

Sometimes you want to search for all possible matches up until your arrival date. No problem. Add
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/example_search.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ recreation_area: # (LIST OR SINGLE ENTRY)
- 1074 # Sierra National Forest, CA (All Campgrounds)
campgrounds: # ENTIRE FIELD CAN BE OMITTED IF NOT USED - (LIST OR SINGLE ENTRY)
campsites: # OVERRIDES CAMPGROUNDS / RECREATION AREA - (LIST OR SINGLE ENTRY)
start_date: 2023-09-12 # YYYY-MM-DD
end_date: 2023-09-13 # YYYY-MM-DD
start_date: 2023-09-12 # YYYY-MM-DD - (LIST OR SINGLE ENTRY)
end_date: 2023-09-13 # YYYY-MM-DD - (LIST OR SINGLE ENTRY)
weekends: false # FALSE BY DEFAULT
nights: 1 # 1 BY DEFAULT
continuous: true # DEFAULTS TO TRUE
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ becomes available, camply sends you a notification to book your spot!
- [Searching for a Campsite by Campground ID](command_line_usage.md#searching-for-a-campsite-by-campground-id)
- [Searching for a Specific Campsite by ID](command_line_usage.md#searching-for-a-specific-campsite-by-id)
- [Continuously Searching for A Campsite](command_line_usage.md#continuously-searching-for-a-campsite)
- [Searching Across Multiple Time Windows](command_line_usage.md#searching-across-multiple-time-windows)
- [Continue Looking After The First Match Is Found](command_line_usage.md#continue-looking-after-the-first-match-is-found)
- [Send a Push Notification](command_line_usage.md#send-a-push-notification)
- [Send a Text Message](command_line_usage.md#send-a-text-message)
Expand Down
Loading

0 comments on commit a5eddb1

Please sign in to comment.