Skip to content

Fix GoingToCamp KeyError and add attribute filtering support - #398

Open
javamonkey79 wants to merge 2 commits into
juftin:mainfrom
javamonkey79:fix/goingtocamp-keyerror-campground-listing
Open

Fix GoingToCamp KeyError and add attribute filtering support#398
javamonkey79 wants to merge 2 commits into
juftin:mainfrom
javamonkey79:fix/goingtocamp-keyerror-campground-listing

Conversation

@javamonkey79

Copy link
Copy Markdown

Summary

  • Fixes KeyError when listing campgrounds by using rootMapId instead of MapId lookup (builds on PR Fix GoingToCamp provider KeyError and broken site details endpoint #391 by @aniketde)
  • Adds attribute_filters support for GoingToCamp provider to filter campsites by attributes like electrical service
  • Fixes API filterData encoding to use enumValues format and JSON encoding as required by the GoingToCamp API

Changes

  • going_to_camp_provider.py: Use rootMapId, add attribute_filters parameter to list_site_availability(), properly JSON-encode filterData
  • search_going_to_camp.py: Pass attribute_filters through to provider
  • search_model.py: Add attribute_filters field to YAML config model
  • yaml_utils.py: Pass attribute_filters from YAML to provider kwargs

Usage

Via YAML config file:

provider: GoingToCamp
recreation_area: 11
campgrounds: 15
start_date: 2026-04-01
end_date: 2026-04-03
attribute_filters:
  - attributeDefinitionId: -32767
    enumValues: [3]  # 30A electric

Common attribute filters:

  • Electrical Service (-32767): 0=None, 1=15A, 2=20A, 3=30A, 4=50A
  • Service Type (-32768): 3=Standard, 5=Electric, 6=Elec+Water, 7=Full hookup

Test plan

  • Tested searching Washington State Parks (Riverside State Park, Bowl and Pitcher)
  • Verified attribute filtering reduces results correctly (17 RV sites → 3 with 30A electric)

🤖 Generated with Claude Code

aniketde and others added 2 commits March 7, 2026 01:14
The GoingToCamp API has changed its /api/maps endpoint to no longer
provide per-facility mapId lookups, causing a KeyError when listing
campgrounds. This fix:

- Uses rootMapId from /api/resourceLocation response instead of
  looking up mapId from /api/maps (which no longer has it)
- Adds root_map_id field to ResourceLocation model
- Gracefully handles the now-404 /api/resource/details endpoint
  by returning fallback site data so availability searches still work

Fixes juftin#377

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The GoingToCamp API supports filtering by site attributes (like electric
hookups) via the filterData parameter, but it requires:
1. Using 'enumValues' instead of 'values' in the filter objects
2. JSON-encoding the filterData as a string

This change:
- Adds attribute_filters parameter to list_site_availability()
- Properly formats filterData with enumValues and JSON encoding
- Adds attribute_filters to SearchGoingToCamp class
- Adds attribute_filters to YAML config model for use with --yaml-config

Example YAML config for searching electric RV sites:
  provider: GoingToCamp
  recreation_area: 3
  campgrounds: -2147483565
  attribute_filters:
    - attributeDefinitionId: -32767
      enumValues: [3]

Common attribute IDs:
- Electrical Service (-32767): 0=None, 1=15A, 2=20A, 3=30A, 4=50A
- Service Type (-32768): 3=Standard, 5=Electric, 6=Elec+Water, 7=Full
@dmaynard51

Copy link
Copy Markdown

Tested this branch against Parks Canada (rec area 14) and BC Parks (12) on camply 0.34.1 — the crash is fixed and availability works. 🎉

  • Listing (previously KeyError: -2147483646): ✅ Parks Canada returns 77 campgrounds, BC Parks 114, no crash.
  • Availability (previously 400 Bad Request on the MAPDATA call): ✅ returns real data. Example — BC Parks, next 60 days, 1-night:
    • Charlie Lake PP — 10 nights (e.g. 2026-07-16)
    • Beatton PP — 8 nights
    • Beaumont PP — 2 nights

One thing to flag: across 12 different Parks Canada parks (Banff, Jasper, Forillon, Fundy, Grasslands, Kootenay, …) I got 0 availability with no errors. That's plausibly just peak-season-full (national parks book out months ahead), but I couldn't independently confirm a Parks-Canada availability hit, so I can't fully rule out a reservation.pc.gc.ca-specific empty-return. I also saw a handful of transient 404s from the GoingToCamp API on one earlier run that didn't recur — noting it in case it's meaningful.

Net: big improvement — un-breaks the whole provider and availability is confirmed working (BC Parks). Thanks for the fix! 🙏

Repro: find_campgrounds(rec_area_id=[14|12]) + SearchGoingToCamp(recreation_area=[…], campgrounds=[…], nights=1).

@dmaynard51

Copy link
Copy Markdown

Update — I dug into the Parks Canada 0s from my earlier comment and can confirm: PC availability works, and this PR effectively resolves #291 too.

My earlier zeros were just peak-season-full (Banff/Jasper book out months ahead). Testing an off-peak window (Sep–Nov) instead, Parks Canada returns real availability — e.g. Banff – Tunnel Mountain Village 2: 36 nights (first 2026-10-07).

I traced the raw MAPDATA too: Parks Canada nests resources one level deeper than BC — the top map returns 0 direct resourceAvailabilities but populates mapLinkAvailabilities (sub-maps), and the real resources live in those sub-maps. This branch recurses into them correctly and catches the availability == 0 sites. Code-0 availability does appear for PC (36 across 15 campgrounds in the fall window); it just never shows up in a peak-summer window because those parks are genuinely full.

So: 👍 crash fixed, availability confirmed for both BC Parks and Parks Canada — looks like this closes #377, #405, and #291.

@bruno0o

bruno0o commented Jul 28, 2026

Copy link
Copy Markdown

I tested the fix in my side. I works well when there is no availability :

camply --provider goingtocamp campsites --rec-area 14 --campground -2147483539 --start-date 2026-09-19 --end-date 2026-09-20

[2026-07-28 21:03:20] CAMPLY   camply, the campsite finder ⛺️                                                                                                                                         
[2026-07-28 21:03:20] INFO     Using Camply Provider: "GoingToCamp"                                                                                                                                   
[2026-07-28 21:03:20] INFO     1 booking nights selected for search, ranging from 2026-09-19 to 2026-09-19                                                                                            
[2026-07-28 21:03:20] INFO     Retrieving Facility Information for Recreation Area ID: `14`.                                                                                                          
[2026-07-28 21:03:22] INFO     1 Matching Campgrounds Found                                                                                                                                           
[2026-07-28 21:03:22] INFO     ⛰  Parks Canada (#14) - 🏕  Yoho - Monarch (#-2147483539)                                                                                                               
[2026-07-28 21:03:22] INFO     ❌ ❌ ❌ ❌ 0 Reservable Campsites Matching Search Preferences                                                                                                         
[2026-07-28 21:03:22] CAMPLY   Exiting camply 👋                          

But not when there are availabilities:

camply --provider goingtocamp campsites --rec-area 14 --campground -2147483539 --start-date 2026-09-27 --end-date 2026-09-28

[2026-07-28 21:00:53] CAMPLY   camply, the campsite finder ⛺️                                                                                                                                         
[2026-07-28 21:00:53] INFO     Using Camply Provider: "GoingToCamp"                                                                                                                                   
[2026-07-28 21:00:53] INFO     1 booking nights selected for search, ranging from 2026-09-27 to 2026-09-27                                                                                            
[2026-07-28 21:00:53] INFO     Retrieving Facility Information for Recreation Area ID: `14`.                                                                                                          
[2026-07-28 21:00:55] INFO     1 Matching Campgrounds Found                                                                                                                                           
[2026-07-28 21:00:55] INFO     ⛰  Parks Canada (#14) - 🏕  Yoho - Monarch (#-2147483539)                                                                                                               
[2026-07-28 21:00:55] ERROR    Receiving bad data from GoingToCamp API: status_code: 404:   

@dmaynard51

Copy link
Copy Markdown

Nice repro @bruno0o — that failure pattern (fine with 0 availability, 404 only when sites are available) points right at the culprit.

Why it only breaks when there's availability

list_site_availability() is fine — it gets everything from /api/availability/map (_find_matching_resources) and never touches the dead endpoint. The 404 comes from the search layer, which loops the available sites and calls get_site_details() on each one:

for site in sites:                     # only non-empty when availability exists
    site_details = self.campsite_finder.get_site_details(
        self._recreation_area_id, site.resource_id
    )

get_site_details() hits SITE_DETAILS (/api/resource/details), which GoingToCamp removed — so it 404s once there's a site to look up, and never fires when the result set is empty. That's the whole "works with no availability" thing.

Why the current fix doesn't fully land

Two gaps:

  1. _api_request() does logger.error(...) before it raises, so even a caught ConnectionError still prints the scary ERROR Receiving bad data ... status_code: 404 line — once per available site. On a campground with real availability that's a wall of red, which reads as a failure even if it's technically handled.
  2. get_site_details() also calls ATTRIBUTE_DETAILS unguarded, and the SITE_DETAILS fallback discards the real name/capacity.

Suggested approach

I run this same GoingToCamp platform in production against WA State Parks, and I don't call /api/resource/details at all — the availability path only needs what /api/availability/map already returns (pass getDailyAvailability: true and read resourceAvailabilities; availability == 0 = open). So SITE_DETAILS should be treated as best-effort enrichment, never a hard dependency of a search. Once it 404s, short-circuit it so you don't re-hit (and re-log) it for every remaining site:

def _stub_site(self, resource_id):
    return {
        "resourceId": resource_id,
        "localizedValues": [{"name": f"Site {resource_id}"}],
        "minCapacity": 1, "maxCapacity": 1,
        "definedAttributes": [], "site_attributes": {},
    }

def get_site_details(self, rec_area_id: int, resource_id: int):
    # /api/resource/details was removed and 404s whenever a real site is looked up — which is why
    # searches only fail when there IS availability. Availability itself comes from
    # /api/availability/map, so treat details as optional enrichment and degrade gracefully.
    if getattr(self, "_site_details_gone", False):
        return self._stub_site(resource_id)
    if not hasattr(self, "_attribute_details"):
        try:
            self._attribute_details = self._api_request(rec_area_id, "ATTRIBUTE_DETAILS")
        except ConnectionError:
            self._attribute_details = {}
    attribute_details = self._attribute_details
    try:
        site_details = self._api_request(rec_area_id, "SITE_DETAILS", {"resourceId": resource_id})
    except ConnectionError:
        self._site_details_gone = True     # dead endpoint — stop hammering it for every site
        return self._stub_site(resource_id)
    # ... existing definedAttributes parsing unchanged ...

And I'd downgrade the log for a handled endpoint so it doesn't scream ERROR on every site — e.g. an optional soft=True on _api_request that logs at debug and lets the caller decide, or just move the logger.error out of _api_request and log at the call site only when it's actually fatal.

That leaves searches fully working when availability exists (sites come back named Site <id> with empty attributes instead of crashing), and the whole thing goes quiet once it learns the endpoint is gone. Happy to share the _mapdata/_site_days map-recursion logic I use if it's useful for sourcing per-day availability without the extra call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants