Skip to content

Efficient fetch entity issue counts - #1129

Merged
pooleycodes merged 9 commits into
mainfrom
efficient-fetchEntityIssueCounts
Nov 13, 2025
Merged

Efficient fetch entity issue counts#1129
pooleycodes merged 9 commits into
mainfrom
efficient-fetchEntityIssueCounts

Conversation

@pooleycodes

@pooleycodes pooleycodes commented Nov 12, 2025

Copy link
Copy Markdown
Contributor

Description

fetchEntityIssueCounts seems to be very slow, a query to Datasette Fetch Entity Issue Counts will show how slow it is.

This function has been moved to the performance db with this lookup New Datasette Query. This seems to be a lot faster and currently seems to work as expected with this change having checked multiple development pages for LPA's to the production equivalent.

Also parallel execution and removal of datasetErrorStatus (not needed) has been done to speed up the LPA Dashboard.

What type of PR is this? (check all applicable)

  • Refactor
  • Feature
  • Bug Fix
  • Optimization
  • Documentation Update

Related Tickets & Documents

Added/updated tests?

We encourage you to keep the code coverage percentage at 80% and above.

  • Yes
  • No, and this is why: Please replace this line with details on why tests have not been included
  • I need help with writing tests

All tests check this lookup works correctly already.

QA sign off

  • Code has been checked and approved
  • Design has been checked and approved
  • Product and business logic has been checked and proved

Summary by CodeRabbit

  • New Features

    • Added "expected" and "prospective" dataset categories on organisation overview pages so organisations can see additional dataset guidance alongside statutory datasets.
  • Improvements

    • Updated dataset status messages for clarity: "authoritative dataset(s) provided", "error(s) accessing URLs", and "dataset(s) can be improved".
    • Overview data loading reworked for faster, more reliable issue-counts and resource retrieval.
  • Tests

    • Local acceptance tests now run with the UI visible for easier debugging and verification.

@coderabbitai

coderabbitai Bot commented Nov 12, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR replaces the dataset-error status fetch with a performance-DB-based entity issue counts fetch, restructures the LPA overview middleware into parallel execution groups, introduces dataset categories "expected" and "prospective" (alongside "statutory"), updates schemas, views and tests to use the new categories, and adds a performance service method and a Playwright UI flag in package.json.

Changes

Cohort / File(s) Summary
Configuration
package.json
Updated test:acceptance:local script to include the --ui flag for Playwright local testing.
Common middleware
src/middleware/common.middleware.js
Minor formatting cleanup around the fetchEntityIssueCounts SQL; no semantics changed.
LPA Overview middleware
src/middleware/lpa-overview.middleware.js
Replaced dataset-error fetch with fetchEntityIssueCountsPerformanceDb, reorganised middleware into two parallel groups, removed dataset filter from fetchProvisions (now fetches all org provisions), and extended dataset categorisation to include expected and prospective.
Performance API service
src/services/performanceDbApi.js
Added public method fetchEntityIssueCounts(datasetId) to query aggregated entity issue counts from the performance DB.
Schemas
src/routes/schemas.js
Added optional expected and prospective dataset arrays to OrgOverviewPage schema.
View template
src/views/organisations/overview.html
Replaced datasets.other notices with datasets.expected and datasets.prospective sections; updated status wording and added statutory/expected explanatory blocks with conditional ODP membership rendering.
Acceptance tests
test/acceptance/dataset_overview.test.js
Updated selectors and assertions to use datasetsExpected (replacing previous mandatory/ODP block) and adjusted expected counts and heading text.
Unit tests — Middleware
test/unit/middleware/lpa-overview.middleware.test.js
Updated tests to use datasets.expected (renamed from other), added conditional rendering assertions for datasetsExpected based on ODP membership.
Unit tests — Views
test/unit/views/organisations/lpaOverviewPage.test.js
Replaced other with expected, added prospective, updated expected rendered texts and dataset aggregation in view tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Middleware as LPA Overview Middleware
    participant DB as Performance DB

    rect rgb(200,220,240)
        Note over Middleware: Parallel Group 1
        par fetchOrgInfo
            Middleware->>DB: fetch org info
        and fetchResources
            Middleware->>DB: fetch resources
        and fetchEndpointSummary
            Middleware->>DB: fetch endpoint summary
        and fetchEntityIssueCountsPerformanceDb
            Middleware->>DB: fetch entity issue counts
        and fetchProvisions
            Middleware->>DB: fetch all provisions
        end
    end

    rect rgb(220,240,200)
        Note over Middleware: Parallel Group 2 (depends on Group 1)
        par fetchEntryIssueCounts
            Middleware->>DB: fetch entry issue counts (uses resources)
        and fetchEntityCounts
            Middleware->>DB: fetch entity counts (uses org info)
        end
    end

    rect rgb(240,220,200)
        Note over Middleware: Template preparation
        Middleware->>Middleware: prepareOverviewTemplateParams
        Note over Middleware: Categorise datasets as statutory, expected, prospective
    end

    Middleware-->>Client: render overview with new dataset categories
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–60 minutes

  • Areas needing extra attention:
    • src/middleware/lpa-overview.middleware.js — verify parallel execution ordering, data dependencies, and that provisions are used correctly without the dataset filter.
    • src/services/performanceDbApi.js and src/middleware/common.middleware.js — confirm SQL correctness and that entity issue counts map to template fields expected by views/tests.
    • Views and tests — ensure labels, pluralisation and conditional ODP rendering match schema and middleware outputs.

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • DilwoarH
  • rosado
  • GeorgeGoodall-GovUk

Poem

🐰 I hopped through code with nimble feet,
Grouped fetches run in parallel — neat,
Expected, prospective now in view,
Errors counted, dashboards too,
A little rabbit cheers this feat! 🎉

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Efficient fetch entity issue counts' directly describes the main optimization: moving fetchEntityIssueCounts to the performance database for efficiency gains.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch efficient-fetchEntityIssueCounts

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21c8105 and 38f71e7.

📒 Files selected for processing (1)
  • src/middleware/common.middleware.js (0 hunks)
💤 Files with no reviewable changes (1)
  • src/middleware/common.middleware.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: run-tests / test

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.

@github-actions

github-actions Bot commented Nov 12, 2025

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 66.4% 6354 / 9569
🔵 Statements 66.4% 6354 / 9569
🔵 Functions 64.72% 266 / 411
🔵 Branches 80.63% 866 / 1074
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/middleware/common.middleware.js 65.96% 93.51% 39.28% 65.96% 29-38, 42, 58-59, 61-62, 74-76, 80, 96-103, 162-175, 181-203, 299-312, 371, 380-382, 412-414, 462-503, 543-547, 552-555, 567-571, 599-613, 620-644, 650-658, 662-678, 747-749, 822-829, 843-898, 929-930, 933-936, 939-952, 977-982, 992-995
src/middleware/lpa-overview.middleware.js 79.81% 78.78% 50% 79.81% 22, 30-31, 38-39, 79-81, 93-95, 101-103, 124-135, 152-154, 164-166, 213-214, 267-275, 289, 291, 322-335, 338-350
src/routes/schemas.js 100% 100% 100% 100%
src/services/performanceDbApi.js 64.9% 85.41% 28.57% 64.9% 38-39, 62-63, 83-84, 100-128, 171-173, 246-250, 255-266, 333-339, 350-364, 374-383, 394-414, 418-426, 435-447, 458-486
Generated in workflow #1261 for commit 38f71e7 by the Vitest Coverage Report Action

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/middleware/common.middleware.js (1)

645-646: Swap console.log for structured logging
We should not print SQL straight to stdout in production middleware; it bypasses our logging framework and makes log parsing noisy. Please route this through logger.debug (or drop it entirely) so we stay consistent with the rest of the service logging.

-    console.log('sqlQuery', sqlQuery)
+    logger.debug('fetchEntityIssueCounts query', { type: types.App, sqlQuery })
src/views/organisations/overview.html (3)

116-116: Pluralisation approach is inconsistent across status items.

Lines 116 and 121 use inline ternary expressions for pluralisation, whilst line 126 uses the pluralise filter. Both are functional, but using the same approach consistently would improve maintainability.

Consider applying the pluralise filter uniformly to all three status items:

-        authoritative dataset{{ "s" if (datasetsWithEndpoints != 1) }} provided
+        authoritative {{ "dataset" | pluralise(datasetsWithEndpoints) }} provided
-        error{{ "s" if (datasetsWithErrors != 1) }} accessing URLs
+        {{ "error" | pluralise(datasetsWithErrors) }} accessing URLs

Also applies to: 121-121, 126-126


147-157: Structure is sound with clear conditional logic.

The datasetsExpected section appropriately guards visibility behind both datasets.expected.length > 0 and isODPMember, ensuring it only displays when relevant. The data-testid is correctly set for testing.

Minor: fix spacing in the template variable on line 149:

-      <h2 class="govuk-heading-m">Datasets {{ organisation.name}} are expected to provide</h2>
+      <h2 class="govuk-heading-m">Datasets {{ organisation.name }} are expected to provide</h2>

159-169: Structure is sound; prospective section appropriately unrestricted.

The datasetsProspective section correctly shows only when prospective datasets exist, without ODP membership restriction. The data-testid is correctly set.

Minor: fix spacing in the template variable on line 161 (matching datasetsExpected):

-      <h2 class="govuk-heading-m">Datasets {{ organisation.name}} can provide</h2>
+      <h2 class="govuk-heading-m">Datasets {{ organisation.name }} can provide</h2>

Also note that line 163 omits the govuk-!-margin-bottom-8 class present on the statutory and expected sections. If this is intentional (prospective is the final section), that's fine; otherwise, consider adding it for consistency.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf6e89e and 21c8105.

📒 Files selected for processing (9)
  • package.json (1 hunks)
  • src/middleware/common.middleware.js (2 hunks)
  • src/middleware/lpa-overview.middleware.js (3 hunks)
  • src/routes/schemas.js (1 hunks)
  • src/services/performanceDbApi.js (1 hunks)
  • src/views/organisations/overview.html (4 hunks)
  • test/acceptance/dataset_overview.test.js (2 hunks)
  • test/unit/middleware/lpa-overview.middleware.test.js (3 hunks)
  • test/unit/views/organisations/lpaOverviewPage.test.js (3 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2024-11-14T17:31:37.924Z
Learnt from: rosado
Repo: digital-land/submit PR: 657
File: src/services/performanceDbApi.js:290-293
Timestamp: 2024-11-14T17:31:37.924Z
Learning: In the `getEntityCounts` function in `src/services/performanceDbApi.js`, errors are already logged through the `.catch(...)` handlers on each promise in the `requests` array.

Applied to files:

  • src/services/performanceDbApi.js
  • src/middleware/lpa-overview.middleware.js
  • src/middleware/common.middleware.js
📚 Learning: 2024-11-14T16:38:49.883Z
Learnt from: rosado
Repo: digital-land/submit PR: 657
File: test/unit/middleware/issueDetails.middleware.test.js:43-43
Timestamp: 2024-11-14T16:38:49.883Z
Learning: In `test/unit/middleware/issueDetails.middleware.test.js`, template params are verified with a schema, so it's acceptable for the test expectations to use primitive values while the test input uses an object for `issueEntitiesCount`.

Applied to files:

  • src/services/performanceDbApi.js
  • src/middleware/lpa-overview.middleware.js
  • test/unit/middleware/lpa-overview.middleware.test.js
  • src/middleware/common.middleware.js
📚 Learning: 2024-10-21T08:57:17.338Z
Learnt from: GeorgeGoodall-GovUk
Repo: digital-land/submit PR: 510
File: test/unit/middleware/datasetOverview.middleware.test.js:1-3
Timestamp: 2024-10-21T08:57:17.338Z
Learning: Tests for `pullOutDatasetSpecification` have been moved to `common.middleware.test`.

Applied to files:

  • test/acceptance/dataset_overview.test.js
  • test/unit/middleware/lpa-overview.middleware.test.js
  • test/unit/views/organisations/lpaOverviewPage.test.js
📚 Learning: 2024-11-12T10:54:09.485Z
Learnt from: GeorgeGoodall-GovUk
Repo: digital-land/submit PR: 609
File: test/unit/noErrorsPage.test.js:68-68
Timestamp: 2024-11-12T10:54:09.485Z
Learning: In `test/unit/noErrorsPage.test.js`, avoid using `prettifyColumnName` for column headers because the table can contain spec fields, as requested by Alex.

Applied to files:

  • test/unit/views/organisations/lpaOverviewPage.test.js
📚 Learning: 2024-11-28T16:29:30.209Z
Learnt from: GeorgeGoodall-GovUk
Repo: digital-land/submit PR: 691
File: src/middleware/dataview.middleware.js:24-24
Timestamp: 2024-11-28T16:29:30.209Z
Learning: In `src/middleware/dataview.middleware.js`, when querying the database, interpolating variables directly into SQL queries is acceptable as the variables are sourced from the database.

Applied to files:

  • src/middleware/common.middleware.js
🧬 Code graph analysis (2)
src/middleware/lpa-overview.middleware.js (2)
src/middleware/middleware.builders.js (4)
  • fetchMany (297-299)
  • FetchOptions (32-41)
  • FetchOptions (32-41)
  • parallel (418-420)
src/middleware/common.middleware.js (8)
  • fetchOrgInfo (78-83)
  • fetchOrgInfo (78-83)
  • fetchResources (160-178)
  • fetchResources (160-178)
  • fetchEndpointSummary (822-835)
  • fetchEndpointSummary (822-835)
  • fetchEntryIssueCounts (662-683)
  • fetchEntryIssueCounts (662-683)
test/unit/middleware/lpa-overview.middleware.test.js (1)
src/middleware/lpa-overview.middleware.js (7)
  • prepareOverviewTemplateParams (256-310)
  • req (75-75)
  • req (124-124)
  • req (149-149)
  • req (207-207)
  • req (322-322)
  • req (338-338)
🔇 Additional comments (3)
src/views/organisations/overview.html (3)

94-104: New deadline notice loops follow established pattern.

The loops for datasets.expected and datasets.prospective mirror the existing datasets.statutory structure and correctly check for dataset.notice before rendering. This maintains consistency across the template.

However, verify that all middleware/route handlers passing context to this template define the datasets.expected and datasets.prospective properties, even when empty, to prevent Nunjucks from treating them as undefined.


138-138: Explanatory text provides helpful context.

The new paragraph clearly communicates the statutory requirement and value proposition to users. Class reuse maintains design consistency.


94-104: Context variables are properly defined and handled across all rendering paths.

The verification confirms that prepareOverviewTemplateParams in src/middleware/lpa-overview.middleware.js correctly manages these context variables:

  • datasets.expected and datasets.prospective are created via _.groupBy() and only included when matching datasets exist (provision_reason-based grouping), which is intentional design—the template safely handles undefined properties via Nunjucks iteration
  • isODPMember is always explicitly set as a boolean
  • All rendering paths (test and production) route through prepareOverviewTemplateParams, ensuring consistent context preparation
  • No alternative rendering paths exist that bypass this middleware

The implementation is correct and the template sections will display/hide based on the presence of actual data.

Comment on lines +256 to +266
SELECT
dataset,
issue_type,
field,
COUNT(*) AS count
FROM endpoint_dataset_issue_type_summary
WHERE organisation = '${datasetId}'
AND severity = 'error'
AND responsibility = 'external'
AND (resource_end_date = '' OR resource_end_date IS NULL)
GROUP BY dataset, issue_type, field`

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.

⚠️ Potential issue | 🔴 Critical

Use the precomputed count_issues values
endpoint_dataset_issue_type_summary already stores the actual issue totals in count_issues (see lpaOverviewQuery in this same file). Switching to COUNT(*) only returns “number of summary rows”, so any dataset with 5 unresolved issues now comes back as count = 1, which breaks the dashboard stats. Please aggregate with SUM(count_issues) instead so we preserve the real totals.

-    COUNT(*) AS count 
+    SUM(count_issues) AS count 
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SELECT
dataset,
issue_type,
field,
COUNT(*) AS count
FROM endpoint_dataset_issue_type_summary
WHERE organisation = '${datasetId}'
AND severity = 'error'
AND responsibility = 'external'
AND (resource_end_date = '' OR resource_end_date IS NULL)
GROUP BY dataset, issue_type, field`
SELECT
dataset,
issue_type,
field,
SUM(count_issues) AS count
FROM endpoint_dataset_issue_type_summary
WHERE organisation = '${datasetId}'
AND severity = 'error'
AND responsibility = 'external'
AND (resource_end_date = '' OR resource_end_date IS NULL)
GROUP BY dataset, issue_type, field`
🤖 Prompt for AI Agents
In src/services/performanceDbApi.js around lines 256 to 266, the query uses
COUNT(*) which returns number of summary rows instead of the precomputed issue
totals; replace COUNT(*) AS count with SUM(count_issues) AS count (or similar
alias) so the query aggregates the stored count_issues values and returns actual
issue totals while keeping the same WHERE and GROUP BY clauses.

@pooleycodes
pooleycodes merged commit c3797a1 into main Nov 13, 2025
8 of 10 checks passed
@pooleycodes
pooleycodes deleted the efficient-fetchEntityIssueCounts branch November 13, 2025 15:59
@coderabbitai coderabbitai Bot mentioned this pull request Jun 23, 2026
8 tasks
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.

Faster LPA Dashboard Loading

2 participants