fix(web): compute stats and reputation score from database aggregates (#185) - #377
Conversation
|
@ntttttbl123-blip Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
✅ Deploy Preview for stellar-signet ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
@ntttttbl123-blip is attempting to deploy a commit to the blockchainmaxis-8449's projects Team on Vercel. A member of the Team first needs to authorize it. |
blockchain-maxis
left a comment
There was a problem hiding this comment.
Computing the stats from a database aggregate instead of the paginated 100-row slice is the right fix for #185, and the fallback shape — getProfileStats tries the DB, falls back to computeStats over whatever operations the caller already fetched — means a deployment with no DATABASE_URL behaves exactly as it does today. Threading it through both app/p/[handle]/page.tsx and the profile tRPC procedure covers both readers.
The scoring formula is now written twice, and that's the thing I'd most like changed. safeDbProfileStats has:
const reputation = Math.min(100, Math.min(60, invocations * 6) + Math.min(40, uniqueFunctions * 10));which is character-for-character what computeStats already does at apps/web/lib/profiles.ts:214-217. Two copies of a scoring rule drift — someone tunes the caps in one and the same profile scores differently depending on whether the database happened to be reachable, which is a nastier version of the bug this PR is fixing. Please extract it, e.g. function scoreReputation(invocations: number, uniqueFunctions: number): number, and call it from both. Your new computeStats test asserting 2 * 6 + 2 * 10 would then be pinning the one implementation rather than one of two.
Smaller note: distinct: ['function', 'decodedFunction'] gives distinct pairs, so the new Set(...) after it is what actually produces the unique-function count — the two aren't redundant, but it's worth a comment saying the distinct is a pre-filter and the Set is the real dedupe on the coalesced name, otherwise the next reader will assume one of them is dead.
The other blocker is CI. e2e smoke tests fails on this branch:
> 16 | await expect(page.getByPlaceholder('your-handle')).toBeVisible();
at openClaimForm (apps/web/e2e/claim.spec.ts:16:54)
Being straight with you about what I know: main is green, and this reproduced across all three Playwright retries and again on a full re-run of the job, so it isn't the flake it first looks like. But I could not tie it to your diff — nothing here touches the marketing page or the claim form, and there's no server error in the run, the form just never opens. The one variable I can point at is that this branch is based on 8216232 and the claim flow has moved since (#347 reworked connect-wallet.tsx and claim.spec.ts).
So: rebase onto main and push. If the e2e failure goes with the rebase, it was the stale base and we're done. If it survives, tell me and I'll dig into it myself rather than leave you chasing something that may not be yours.
Resolves the conflict with main's truncation-disclosure work additively: the operations window keeps its `truncated`/`cap` metadata, and the stats are now sourced from `getProfileStats`, which reports whether they are exact database aggregates or derived from that capped window.
blockchain-maxis
left a comment
There was a problem hiding this comment.
Thanks — this is the right fix for #185: the count and the distinct-function query now run in the database over the whole history, so the score stops being a property of the page fetch.
Merged main into the branch as maintainer and resolved the conflict with the truncation-disclosure work that landed in the meantime. Three adjustments on top of your commit:
getProfileStatsnow returns anexactflag. The profile page kept main'struncated/capmetadata for the operations list, but the stats are no longer qualified as lower bounds when they came from a database aggregate — otherwise an exact total rendered as "412+".safeDbProfileStatsreturnsnullinstead of a zeroed result when the profile has no indexed operations. A profile row can exist with the operations still served by Horizon or the curated demo JSON, and a confident0next to a non-empty list is worse than falling back.- The reputation formula is extracted into one
scoreOfhelper shared by the in-memory and database paths, and the OG image and the dashboard were moved ontogetProfileStatstoo, so all three surfaces agree on the same number.
1126426
into
blockchain-maxis:main
Closes #185
Summary of Changes
Computes profile statistics (successful invocations count, distinct functions exercised, and reputation score) using database aggregate queries across the full operation history rather than deriving stats from the paginated first 100 rows.
Key Changes
Verification Status
ode scripts/check-docs.mjs: Passed cleanly.