Skip to content

Feat tide#191

Merged
ryansurf merged 5 commits into
mainfrom
feat-tide
Apr 8, 2026
Merged

Feat tide#191
ryansurf merged 5 commits into
mainfrom
feat-tide

Conversation

@ryansurf
Copy link
Copy Markdown
Owner

@ryansurf ryansurf commented Apr 8, 2026

General:

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?

Code:

  1. Does your submission pass tests?
  2. Have you run the linter/formatter on your code locally before submission?
  3. Have you updated the documentation/README to reflect your changes, as applicable?
  4. Have you added an explanation of what your changes do?
  5. Have you written new tests for your changes, as applicable?

Summary by Sourcery

Improve documentation for cli-surf usage and configuration, and refactor tests to rely on mocked external services and new cache names for more reliable, deterministic behavior.

Enhancements:

  • Rename internal cache instances for ocean, forecast, and hourly forecast data, and adjust usage in the API module to align with the new names.

Documentation:

  • Rewrite and expand README with a clearer overview, table of contents, API argument reference, environment variable descriptions, GPT surf report setup, tech stack, and updated frontend notes.

Tests:

  • Refactor API-related tests to mock external services (geocoding, Open-Meteo, etc.) for deterministic results and add coverage around forecast, UV, ocean data, history functions, and email-related paths.
  • Update tests to work with renamed cache objects and to verify cache clearing behavior where needed.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Apr 8, 2026

Reviewer's Guide

Improves README documentation and developer onboarding while significantly refactoring tests around external APIs to use deterministic mocks and aligning cache names between tests and implementation for the tide/surf data API.

Sequence diagram for tide and surf forecast request with caching and external API

sequenceDiagram
    actor Surfer
    participant FastAPI as FastAPI_server
    participant ApiModule as api_module
    participant ForecastCache as forecast_cache
    participant OpenMeteo as OpenMeteo_API

    Surfer->>FastAPI: GET /?loc=malibu&fc=3
    FastAPI->>ApiModule: forecast(lat,long,decimal,days=3)
    ApiModule->>ForecastCache: check_cache(lat,long,decimal,days)
    alt cache_hit
        ForecastCache-->>ApiModule: cached_forecast_data
        ApiModule-->>FastAPI: cached_forecast_data
        FastAPI-->>Surfer: formatted_surf_forecast
    else cache_miss
        ForecastCache-->>ApiModule: miss
        ApiModule->>OpenMeteo: weather_api(url,params)
        OpenMeteo-->>ApiModule: raw_forecast_response
        ApiModule->>ApiModule: parse_and_normalize_forecast
        ApiModule->>ForecastCache: store(lat,long,decimal,days,forecast_data)
        ApiModule-->>FastAPI: forecast_data
        FastAPI-->>Surfer: formatted_surf_forecast
    end
Loading

Class diagram for updated cache objects and API functions in tide/surf module

classDiagram
    class TTLCache {
      +int maxsize
      +int ttl
      +get(key)
      +set(key,value)
    }

    class api_module {
      <<module>>
      +_TTL : int
      +_ocean_cache : TTLCache
      +_uv_cache : TTLCache
      +uv_history_cache : TTLCache
      +ocean_history_cache : TTLCache
      +_wind_temp_cache : TTLCache
      +_rain_cache : TTLCache
      +forecast_cache : TTLCache
      +_hourlyforecast_cache : TTLCache
      +_ocean_lock : Lock
      +get_uv_history(lat,long,decimal,unit) str
      +ocean_information(lat,long,decimal,unit) list~float~
      +ocean_information_history(lat,long,decimal,unit) list~float~
      +get_rain(lat,long) tuple~float,float~
      +forecast(lat,long,decimal,days) dict
      +get_hourly_forecast(lat,long,days,unit) dict
    }

    class Lock {
      +acquire()
      +release()
    }

    api_module "1" o-- "1" TTLCache : uses_for_ocean_data
    api_module "1" o-- "1" TTLCache : uses_for_uv
    api_module "1" o-- "1" TTLCache : uses_for_uv_history
    api_module "1" o-- "1" TTLCache : uses_for_ocean_history
    api_module "1" o-- "1" TTLCache : uses_for_wind_temp
    api_module "1" o-- "1" TTLCache : uses_for_rain
    api_module "1" o-- "1" TTLCache : uses_for_daily_forecast
    api_module "1" o-- "1" TTLCache : uses_for_hourly_forecast
    api_module "1" o-- "1" Lock : synchronizes_cached_calls

    api_module ..> TTLCache : creates_instances
    api_module ..> Lock : uses_for_thread_safety
Loading

File-Level Changes

Change Details Files
Rewrote and reorganized README to better explain features, usage, configuration (env vars), GPT surf reports, tech stack, and optional components.
  • Added a concise product description and feature bullet list for cli-surf.
  • Introduced a structured table of contents with anchors for Usage, Setup, GPT Surf Report, Tech Stack, Contributing, and Contributors.
  • Expanded API usage examples, restructured API arguments into categorized tables, and documented the /help endpoint.
  • Clarified Poetry and Docker setup steps, environment variables (general, email, GPT, DB), and added notes about optional services (email server, MongoDB).
  • Updated and reorganized the frontend and GPT Surf Report sections, including notes on deprecation/maintenance and improved configuration examples.
  • Added a Tech Stack section and cleaned up contributing and footer separators for readability.
README.md
Refactored API tests to mock external services (Nominatim, Open-Meteo, email-related flows) and to validate cache-aware behavior and error handling deterministically.
  • Replaced live get_coordinates test with a Nominatim-mocked version that asserts exact lat/long/name and output types, including fallback behavior when no args or invalid locations are given.
  • Replaced live Open-Meteo dependent tests (get_uv, ocean_information, forecast, uv/ocean history) with mocks over _create_openmeteo_client, explicitly controlling return structures to assert expected outputs, lengths, and types.
  • Added comprehensive tests for get_uv_history and ocean_information_history covering basic functionality, invalid coordinate error propagation, specific response formats, and behavior when the global testing flag is disabled.
  • Added a highly stubbed test_gather_data that patches all dependent functions (ocean_information, get_uv, hourly forecast, wind/temp, rain, forecast, history functions, forecast_to_json) to verify integration without making network calls.
  • Ensured tests clear relevant caches before calls to avoid cross-test contamination and to assert behavior with fresh cache state.
tests/test_api.py
Aligned cache object names between implementation and tests and fixed minor internal naming inconsistencies in the API module.
  • Renamed _ocean_history_cache to ocean_history_cache and updated its usage in the ocean_information_history cached decorator.
  • Renamed _forecast_cache to forecast_cache and _hourly_forecast_cache to _hourlyforecast_cache, then updated the corresponding cached decorators for forecast and get_hourly_forecast.
  • Removed a stray blank line in get_uv_history’s try block for minor cleanup.
src/api.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/api.py 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • The rename of _forecast_cache/_ocean_history_cache to public forecast_cache/ocean_history_cache changes their visibility; if the goal is only to clear them in tests, consider keeping them private and exposing a small reset helper or using cachetools.cached.cache_clear() in tests to avoid widening the public surface.
  • The _hourlyforecast_cache name is inconsistent with the other cache variables and with get_hourly_forecast; consider renaming it to _hourly_forecast_cache for readability and to avoid confusion.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The rename of `_forecast_cache`/`_ocean_history_cache` to public `forecast_cache`/`ocean_history_cache` changes their visibility; if the goal is only to clear them in tests, consider keeping them private and exposing a small reset helper or using `cachetools.cached.cache_clear()` in tests to avoid widening the public surface.
- The `_hourlyforecast_cache` name is inconsistent with the other cache variables and with `get_hourly_forecast`; consider renaming it to `_hourly_forecast_cache` for readability and to avoid confusion.

## Individual Comments

### Comment 1
<location path="src/api.py" line_range="35-38" />
<code_context>
-_forecast_cache = TTLCache(maxsize=300, ttl=_TTL)
-_hourly_forecast_cache = TTLCache(maxsize=300, ttl=_TTL)
+forecast_cache = TTLCache(maxsize=300, ttl=_TTL)
+_hourlyforecast_cache = TTLCache(maxsize=300, ttl=_TTL)
 _ocean_lock = Lock()

</code_context>
<issue_to_address>
**suggestion (typo):** Align `_hourlyforecast_cache` naming with the other cache variables for clarity.

The new `_hourlyforecast_cache` name removes the underscore between `hourly` and `forecast`, diverging from the previous `_hourly_forecast_cache` and the style of the other caches. This looks more like a typo than an intentional change. Please either keep `_hourly_forecast_cache` or use `hourly_forecast_cache` if it’s meant to be public, to keep naming consistent and readable.

Suggested implementation:

```python
forecast_cache = TTLCache(maxsize=300, ttl=_TTL)
_hourly_forecast_cache = TTLCache(maxsize=300, ttl=_TTL)

```

If `_hourlyforecast_cache` is referenced elsewhere in `src/api.py` or other modules, those references should also be renamed to `_hourly_forecast_cache` to match this change and avoid `NameError`s.
</issue_to_address>

### Comment 2
<location path="tests/test_api.py" line_range="109" />
<code_context>
+    assert isinstance(result[2], str)
+
+
+@patch("src.api._create_openmeteo_client")
+def test_get_uv(mock_create_client):
+    UV_INDEX = 5.0
+    mock_variable = MagicMock()
+    mock_variable.Value.return_value = UV_INDEX  # real number so round() works
+
+    mock_current = MagicMock()
+    mock_current.Variables.return_value = mock_variable
+
+    mock_response = MagicMock()
+    mock_response.Current.return_value = mock_current
+
+    mock_client = MagicMock()
+    mock_client.weather_api.return_value = [mock_response]
+    mock_create_client.return_value = mock_client
+
+    result = get_uv(31.41, -84.92, 2, "imperial")
+
+    assert result == UV_INDEX
+    assert isinstance(result, float)
+
+
</code_context>
<issue_to_address>
**suggestion (testing):** Clear the get_uv cache in this test to avoid flakiness from cached values

Because `get_uv` uses a TTL cache, this test may reuse a value cached by another test with the same arguments, causing flakiness. To keep tests isolated, clear the cache (e.g., `get_uv.cache_clear()`) before calling it here, consistent with how you clear `get_coordinates` and `uv_history_cache`.

```suggestion
    get_uv.cache_clear()
    result = get_uv(31.41, -84.92, 2, "imperial")
```
</issue_to_address>

### Comment 3
<location path="README.md" line_range="26-35" />
<code_context>

+---
+
+## Table of Contents
+
+- [Usage](#-usage)
+- [Setup](#️-setup)
+  - [Poetry](#how-to-start-locally-with-poetry)
+  - [Docker](#how-to-start-with-docker)
+  - [Environment Variables](#variables)
+  - [Email Server](#email-server)
+  - [MongoDB](#mongodb)
+- [GPT Surf Report](#-gpt-surf-report)
+- [Tech Stack](#-tech-stack)
+- [Contributing](#-contributing)
+- [Contributors](#-contributors)
+
+---
</code_context>
<issue_to_address>
**issue (bug_risk):** Table of Contents anchor links likely do not match the generated heading IDs and may be broken, especially the `#️-setup` anchor.

These anchors (e.g., `#-usage`, `#️-setup`, `#-gpt-surf-report`) likely don’t match the IDs auto-generated from headings like `## 💻 Usage` and `## 🛠️ Setup`. In particular, `#️-setup` has an invisible character after `#`, which will break the link. Please check how your renderer (e.g., GitHub) slugs these headings and update the anchors accordingly (likely `#usage`, `#setup`, `#gpt-surf-report`, etc.), removing the stray character in `#️-setup`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/api.py
Comment on lines +35 to 38
_hourlyforecast_cache = TTLCache(maxsize=300, ttl=_TTL)
_ocean_lock = Lock()


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (typo): Align _hourlyforecast_cache naming with the other cache variables for clarity.

The new _hourlyforecast_cache name removes the underscore between hourly and forecast, diverging from the previous _hourly_forecast_cache and the style of the other caches. This looks more like a typo than an intentional change. Please either keep _hourly_forecast_cache or use hourly_forecast_cache if it’s meant to be public, to keep naming consistent and readable.

Suggested implementation:

forecast_cache = TTLCache(maxsize=300, ttl=_TTL)
_hourly_forecast_cache = TTLCache(maxsize=300, ttl=_TTL)

If _hourlyforecast_cache is referenced elsewhere in src/api.py or other modules, those references should also be renamed to _hourly_forecast_cache to match this change and avoid NameErrors.

Comment thread tests/test_api.py
mock_client.weather_api.return_value = [mock_response]
mock_create_client.return_value = mock_client

result = get_uv(31.41, -84.92, 2, "imperial")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Clear the get_uv cache in this test to avoid flakiness from cached values

Because get_uv uses a TTL cache, this test may reuse a value cached by another test with the same arguments, causing flakiness. To keep tests isolated, clear the cache (e.g., get_uv.cache_clear()) before calling it here, consistent with how you clear get_coordinates and uv_history_cache.

Suggested change
result = get_uv(31.41, -84.92, 2, "imperial")
get_uv.cache_clear()
result = get_uv(31.41, -84.92, 2, "imperial")

Comment thread README.md
Comment on lines +26 to +35
## Table of Contents

- [Usage](#-usage)
- [Setup](#️-setup)
- [Poetry](#how-to-start-locally-with-poetry)
- [Docker](#how-to-start-with-docker)
- [Environment Variables](#variables)
- [Email Server](#email-server)
- [MongoDB](#mongodb)
- [GPT Surf Report](#-gpt-surf-report)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Table of Contents anchor links likely do not match the generated heading IDs and may be broken, especially the #️-setup anchor.

These anchors (e.g., #-usage, #️-setup, #-gpt-surf-report) likely don’t match the IDs auto-generated from headings like ## 💻 Usage and ## 🛠️ Setup. In particular, #️-setup has an invisible character after #, which will break the link. Please check how your renderer (e.g., GitHub) slugs these headings and update the anchors accordingly (likely #usage, #setup, #gpt-surf-report, etc.), removing the stray character in #️-setup.

Copy link
Copy Markdown
Owner Author

@ryansurf ryansurf left a comment

Choose a reason for hiding this comment

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

approve

@ryansurf ryansurf merged commit 4da9b7a into main Apr 8, 2026
10 checks passed
@ryansurf ryansurf deleted the feat-tide branch April 8, 2026 02:11
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.

1 participant