Skip to content

Add BLN3 score calculation with caching#608

Merged
SvenVw merged 9 commits into
developmentfrom
FDM574
May 13, 2026
Merged

Add BLN3 score calculation with caching#608
SvenVw merged 9 commits into
developmentfrom
FDM574

Conversation

@SvenVw
Copy link
Copy Markdown
Collaborator

@SvenVw SvenVw commented May 7, 2026

Summary by CodeRabbit

  • New Features

    • BLN3 scoring added: automated field input collection, BLN3 score request, and cached score retrieval with auth and timeout handling; new BLN3 data types exposed.
  • Tests

    • Comprehensive tests for input collection, request/response handling, cache semantics, and error/timeout cases.

Review Change Stack

Closes #574

…raw NMI API call to `POST /maatwerk/bln3/score/field`), `getBln3Score` (cached wrapper via `withCalculationCache`), and `collectInputForBln3Score` (assembles field inputs from fdm-core: lat/lon from field centroid, soil analysis parameters, cultivations mapped from BRP catalogue codes, and adopted BLN measures). Types exported: `Bln3Score`, `Bln3ScoreInputs`, `Bln3ScoreCollectedInputs`, `Bln3IndicatorResult`, `Bln3AggregationResult`.
@SvenVw SvenVw self-assigned this May 7, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

📝 Walkthrough

Walkthrough

Adds BLN3 score module: TypeScript types, collectInputForBln3Score (concurrent FDM reads and transformations), requestBln3Score (POST to NMI with 30s timeout and validation), getBln3Score (withCalculationCache wrapper), tests, re-exports, and a Changesets entry.

Changes

BLN3 Score Calculation Feature

Layer / File(s) Summary
BLN3 Type Contracts
fdm-calculator/src/bln3/types.d.ts
TypeScript type declarations for BLN3 inputs, indicators, aggregations, collected-input shape, score response, and NMI API envelope.
Input collection from FDM (impl & tests)
fdm-calculator/src/bln3/input.ts, fdm-calculator/src/bln3/input.test.ts
collectInputForBln3Score concurrently fetches field/soil/cultivations/measures; derives a_lon/a_lat, numeric a_* soil features, maps nl_ cultivations and bln_ measures with timeframe fallbacks, omits empty sections, and wraps errors; tests validate mappings, selection, omissions, timeframe forwarding, and error wrapping.
NMI API request, caching, and tests
fdm-calculator/src/bln3/index.ts, fdm-calculator/src/bln3/index.test.ts
requestBln3Score validates nmiApiKey, POSTs to the NMI BLN3 score endpoint with Bearer auth and a 30s timeout, validates/mapping response and errors; getBln3Score wraps it with withCalculationCache (nmiApiKey redacted); tests cover request shape, success mapping, HTTP/API errors, timeout handling, and cache hit/miss/logging semantics.
Public API Surface & Release
fdm-calculator/src/index.ts, .changeset/loud-mirrors-kneel.md
Re-exports collectInputForBln3Score, getBln3Score, requestBln3Score, and BLN3 types; Changesets entry added for minor version bump documenting the BLN3 module.

Sequence Diagram

sequenceDiagram
  participant App as fdm-app
  participant Calc as fdm-calculator
  participant FdmCore as FDM Core
  participant Cache as Calculation Cache
  participant NmiApi as NMI API
  App->>Calc: getBln3Score(field_id, nmiApiKey)
  Calc->>Cache: lookup hash(requestBln3Score + inputs with nmiApiKey redacted)
  alt Cache Hit
    Cache-->>App: Bln3Score
  else Cache Miss
    Calc->>FdmCore: getField, getSoilAnalyses, getCultivations, getMeasures
    FdmCore-->>Calc: field + soil + cultivations + measures
    Calc->>Calc: collectInputForBln3Score transforms data
    Calc->>NmiApi: POST /maatwerk/bln3/score/field (Bearer auth)
    NmiApi-->>Calc: {data: {indicator: [...]}}
    Calc->>Cache: store result
    Cache-->>App: Bln3Score
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • nmi-agro/fdm#606: Related changes to measures/catalogue APIs used by collectInputForBln3Score.

Suggested labels

fdm-calculator, enhancement, branch:development

Suggested reviewers

  • BoraIneviNMI
  • gerardhros

Poem

🐰 I hopped through fields to fetch the ground,
Collected soils and measures all around.
I asked the API, waited with care,
Cached the answer to share and to spare.
Now indicators bloom — hop, skip, and bound!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed All acceptance criteria from issue #574 are met: requestBln3Score calls the NMI API with proper auth and error handling, getBln3Score wraps it with withCalculationCache with nmiApiKey redaction, types are defined, functions are exported, and comprehensive unit tests cover cache behavior and error cases.
Out of Scope Changes check ✅ Passed All changes align with issue #574 objectives: BLN3 score module, types, caching infrastructure, and unit tests. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title Check ✅ Passed Title check skipped as CodeRabbit has written the PR title.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch FDM574

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@SvenVw SvenVw changed the base branch from development to FDM573 May 7, 2026 09:27
@sentry
Copy link
Copy Markdown

sentry Bot commented May 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@SvenVw SvenVw changed the base branch from FDM573 to development May 12, 2026 07:35
@SvenVw SvenVw changed the title FDM574 @coderabbitai May 12, 2026
@SvenVw SvenVw linked an issue May 12, 2026 that may be closed by this pull request
6 tasks
@SvenVw SvenVw marked this pull request as ready for review May 12, 2026 07:36
@SvenVw
Copy link
Copy Markdown
Collaborator Author

SvenVw commented May 12, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot changed the title @coderabbitai Add BLN3 score calculation with caching May 12, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fdm-calculator/src/bln3/index.test.ts`:
- Around line 129-134: Add unit tests for getBln3Score that exercise actual
cache semantics rather than just type: mock or stub the underlying
requestBln3Score and the cache layer used by withCalculationCache to assert (1)
a cache miss calls requestBln3Score and then stores the result (miss→store), (2)
a subsequent call with the same key returns the cached value and does not call
requestBln3Score (hit without refetch), and (3) simulate cache read/write
failures to verify error-path handling and that appropriate logging occurs;
reference getBln3Score, requestBln3Score and withCalculationCache when locating
code to mock and assert behavior, and mirror patterns used in
fdm-core/src/calculator.test.ts for cache assertions and logging checks.

In `@fdm-calculator/src/bln3/index.ts`:
- Around line 28-38: The POST fetch in bln3 (the call assigning to `response`)
must use an AbortController to enforce a timeout: create an AbortController,
pass its `signal` into the fetch options, start a timer (e.g., with setTimeout)
to call controller.abort() after a configurable ms, and clear the timer once the
response is received; update the surrounding logic that awaits `response` to
handle an aborted request (treat AbortError or aborted fetch as a timeout error)
so the worker doesn't hang. Use the same timeout pattern used in
`dyna.ts`/`nsupply.ts` and reference the `response` variable and the fetch call
in `fdm-calculator/src/bln3/index.ts` when applying the change.
- Around line 47-52: The API response mapping currently assumes semantic
success; before returning from the function that parses the NMI response (where
result: Bln3ScoreResponse is created and you map result.data.indicator →
indicators), validate result.success is true and handle the failure path: if
result.success is false, throw an error (or return a rejected promise) including
any error/message fields from the response for observability instead of
proceeding to map result.data; ensure you reference and check result.success and
use result.data or result.error in the error payload so the calling code can
distinguish HTTP success from API-level failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0235bbb9-1f20-4ed7-a4ed-51f918378b16

📥 Commits

Reviewing files that changed from the base of the PR and between a67df6a and bc16545.

📒 Files selected for processing (7)
  • .changeset/loud-mirrors-kneel.md
  • fdm-calculator/src/bln3/collect.test.ts
  • fdm-calculator/src/bln3/collect.ts
  • fdm-calculator/src/bln3/index.test.ts
  • fdm-calculator/src/bln3/index.ts
  • fdm-calculator/src/bln3/types.d.ts
  • fdm-calculator/src/index.ts

Comment thread fdm-calculator/src/bln3/index.test.ts
Comment thread fdm-calculator/src/bln3/index.ts Outdated
Comment thread fdm-calculator/src/bln3/index.ts Outdated
@SvenVw SvenVw requested a review from BoraIneviNMI May 12, 2026 09:10
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI left a comment

Choose a reason for hiding this comment

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

I found a couple things but it is up to you to change them. I can just approve if you aren't going to change anything.

@@ -0,0 +1,106 @@
import {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Make sure you want to call this file "collect.ts" and not "input.ts" or something.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in ccee20d

a_lon: 5.2,
}

describe("requestBln3Score", () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You didn't test the case where fetch throws an error.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 960d951

@SvenVw SvenVw requested a review from BoraIneviNMI May 13, 2026 07:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
fdm-calculator/src/bln3/index.test.ts (1)

147-152: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add real cache-behavior tests for getBln3Score (miss/hit + error logging).

Lines 147-152 only validate export shape; they do not cover the required cache semantics from the PR objective (miss→store, hit without refetch, and cache error logging paths).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fdm-calculator/src/bln3/index.test.ts` around lines 147 - 152, Add tests in
index.test.ts that exercise real cache semantics for getBln3Score: write one
test that simulates a cache miss by mocking the underlying request function
(requestBln3Score) to return a value and asserting the value is stored (call
twice and assert requestBln3Score called only once after store), another test
for a cache hit where you pre-seed the cache (or mock the cache layer used by
withCalculationCache) and assert getBln3Score returns cached value without
invoking requestBln3Score, and a test that forces the cache layer to
throw/return an error and asserts the error path logs via the logger (spy on the
logger used by withCalculationCache). Use the existing symbols getBln3Score,
requestBln3Score and withCalculationCache to locate and mock the pieces and
ensure you verify call counts and logger calls for miss/hit/error scenarios.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fdm-calculator/src/bln3/index.test.ts`:
- Around line 46-145: Add a test in index.test.ts that exercises
requestBln3Score's semantic API-failure branch by mocking fetch to resolve with
ok: true and json() returning { success: false, error_message: "semantic failure
message" } (use baseInputs/Bln3ScoreInputs) and assert that
requestBln3Score(...) rejects with that exact error_message and that fetch was
called once; use vi.mocked(fetch).mockResolvedValueOnce(...) similar to other
tests and mirror the pattern used in the non-ok and AbortError tests.

In `@fdm-calculator/src/bln3/index.ts`:
- Around line 52-61: The code assumes result.data exists after checking
result.success; before returning indicators and aggregations, validate that
result.data is present and has the expected fields (e.g., result.data.indicator
and result.data.aggregations) and throw a clear Error if not (instead of letting
a TypeError occur). In the function handling the Bln3ScoreResponse, guard
against malformed payloads by checking result.data and the shapes of
result.data.indicator/aggregations (or use result.data?.indicator with an
explicit null/undefined check) and return only when valid, otherwise throw a
descriptive error mentioning the missing payload fields.

---

Duplicate comments:
In `@fdm-calculator/src/bln3/index.test.ts`:
- Around line 147-152: Add tests in index.test.ts that exercise real cache
semantics for getBln3Score: write one test that simulates a cache miss by
mocking the underlying request function (requestBln3Score) to return a value and
asserting the value is stored (call twice and assert requestBln3Score called
only once after store), another test for a cache hit where you pre-seed the
cache (or mock the cache layer used by withCalculationCache) and assert
getBln3Score returns cached value without invoking requestBln3Score, and a test
that forces the cache layer to throw/return an error and asserts the error path
logs via the logger (spy on the logger used by withCalculationCache). Use the
existing symbols getBln3Score, requestBln3Score and withCalculationCache to
locate and mock the pieces and ensure you verify call counts and logger calls
for miss/hit/error scenarios.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0e365d78-09d2-4bfb-933f-9a51ddafa39b

📥 Commits

Reviewing files that changed from the base of the PR and between bc16545 and 960d951.

📒 Files selected for processing (4)
  • fdm-calculator/src/bln3/index.test.ts
  • fdm-calculator/src/bln3/index.ts
  • fdm-calculator/src/bln3/input.test.ts
  • fdm-calculator/src/bln3/input.ts

Comment thread fdm-calculator/src/bln3/index.test.ts
Comment thread fdm-calculator/src/bln3/index.ts
Comment thread fdm-calculator/src/bln3/index.test.ts Fixed
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fdm-calculator/src/bln3/index.test.ts`:
- Around line 100-112: Add a new unit test alongside the existing one to cover
the case where the API response includes aggregations: mock fetch to return
mockBln3ScoreResponse with a data.aggregations field populated, call
requestBln3Score(baseInputs), and assert the returned Bln3Score equals {
indicators: mockBln3ScoreResponse.data.indicator, aggregations:
mockBln3ScoreResponse.data.aggregations } to verify the mapping done in
requestBln3Score (see where aggregations are read in index.ts).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e5aaa156-6b19-40ec-b466-dbeb7eb82bb4

📥 Commits

Reviewing files that changed from the base of the PR and between 960d951 and a9c414d.

📒 Files selected for processing (2)
  • fdm-calculator/src/bln3/index.test.ts
  • fdm-calculator/src/bln3/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • fdm-calculator/src/bln3/index.ts

Comment thread fdm-calculator/src/bln3/index.test.ts
Copy link
Copy Markdown
Collaborator

@BoraIneviNMI BoraIneviNMI left a comment

Choose a reason for hiding this comment

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

Looks good now. Thank you for making the changes.

@SvenVw SvenVw merged commit 9ee8f2e into development May 13, 2026
13 checks passed
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.

Add BLN3 score calculation with caching

2 participants