diff --git a/README.md b/README.md index 9be909a..1bf403b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ Base URL: http://stats.filspark.com/ http://stats.filspark.com/miners/retrieval-success-rate/summary +- `GET /miner/:id/retrieval-success-rate/summary?from=&to=` + + http://stats.filspark.com/miner/f0814049/retrieval-success-rate/summary + - `GET /miner/:id/deals/eligible/summary` http://stats.filspark.com/miner/f0814049/deals/eligible/summary diff --git a/stats/lib/handler.js b/stats/lib/handler.js index 71a05ce..6ef912e 100644 --- a/stats/lib/handler.js +++ b/stats/lib/handler.js @@ -13,7 +13,8 @@ import { fetchParticipantRewardTransfers, fetchRetrievalSuccessRate, fetchDealSummary, - fetchDailyRetrievalResultCodes + fetchDailyRetrievalResultCodes, + fetchDailyMinerRSRSummary } from './stats-fetchers.js' import { handlePlatformRoutes } from './platform-routes.js' @@ -107,6 +108,8 @@ const handler = async (req, res, pgPools, SPARK_API_BASE_URL) => { await respond(fetchMinersRSRSummary) } else if (req.method === 'GET' && url === '/retrieval-result-codes/daily') { await respond(fetchDailyRetrievalResultCodes) + } else if (req.method === 'GET' && segs[0] === 'miner' && segs[1] && segs[2] === 'retrieval-success-rate' && segs[3] === 'summary') { + await respond(fetchDailyMinerRSRSummary, segs[1]) } else if (req.method === 'GET' && segs[0] === 'miner' && segs[1] && segs[2] === 'deals' && segs[3] === 'eligible' && segs[4] === 'summary') { redirectToSparkApi(req, res, SPARK_API_BASE_URL) } else if (req.method === 'GET' && segs[0] === 'client' && segs[1] && segs[2] === 'deals' && segs[3] === 'eligible' && segs[4] === 'summary') { diff --git a/stats/lib/stats-fetchers.js b/stats/lib/stats-fetchers.js index 6ca1038..3e2f2cc 100644 --- a/stats/lib/stats-fetchers.js +++ b/stats/lib/stats-fetchers.js @@ -201,6 +201,7 @@ export const fetchParticipantRewardTransfers = async (pgPools, { from, to }, add } /** + * Fetches the retrieval stats summary for all miners for given date range. * @param {PgPools} pgPools * @param {import('./typings.js').DateRangeFilter} filter */ @@ -223,6 +224,33 @@ export const fetchMinersRSRSummary = async (pgPools, filter) => { return stats } +/** + * Fetches the retrieval stats summary for a single miner for given date range. + * @param {PgPools} pgPools + * @param {import('./typings.js').DateRangeFilter} filter + * @param {string} minerId + */ +export const fetchDailyMinerRSRSummary = async (pgPools, { from, to }, minerId) => { + const { rows } = await pgPools.evaluate.query(` + SELECT day::TEXT, SUM(total) as total, SUM(successful) as successful + FROM retrieval_stats + WHERE miner_id = $1 AND day >= $2 AND day <= $3 + GROUP BY day + ORDER BY day + `, [ + minerId, + from, + to + ]) + const stats = rows.map(r => ({ + day: r.day, + total: r.total, + successful: r.successful, + success_rate: r.total > 0 ? r.successful / r.total : null + })) + return stats +} + export const fetchDailyRetrievalResultCodes = async (pgPools, filter) => { const { rows } = await pgPools.stats.query(` SELECT day::TEXT, code, rate diff --git a/stats/test/handler.test.js b/stats/test/handler.test.js index 7d809b6..74610f9 100644 --- a/stats/test/handler.test.js +++ b/stats/test/handler.test.js @@ -674,6 +674,40 @@ describe('HTTP request handler', () => { assert.strictEqual(res.headers.get('access-control-allow-origin'), 'http://localhost:3000') }) }) + + describe('GET /miner/{id}/retrieval-success-rate/summary', () => { + it('lists daily retrieval stats summary for specified miner in given date range', async () => { + // before the range + await givenRetrievalStats(pgPools.evaluate, { day: '2024-01-09', minerId: 'f1one', total: 10, successful: 1 }) + await givenRetrievalStats(pgPools.evaluate, { day: '2024-01-09', minerId: 'f1two', total: 100, successful: 20 }) + // in the range + await givenRetrievalStats(pgPools.evaluate, { day: '2024-01-20', minerId: 'f1one', total: 20, successful: 1 }) + await givenRetrievalStats(pgPools.evaluate, { day: '2024-01-20', minerId: 'f1two', total: 200, successful: 60 }) + await givenRetrievalStats(pgPools.evaluate, { day: '2024-01-10', minerId: 'f1one', total: 10, successful: 1 }) + await givenRetrievalStats(pgPools.evaluate, { day: '2024-01-10', minerId: 'f1two', total: 100, successful: 50 }) + // after the range + await givenRetrievalStats(pgPools.evaluate, { day: '2024-01-21', minerId: 'f1one', total: 30, successful: 1 }) + await givenRetrievalStats(pgPools.evaluate, { day: '2024-01-21', minerId: 'f1two', total: 300, successful: 60 }) + + const res = await fetch( + new URL( + '/miner/f1one/retrieval-success-rate/summary?from=2024-01-10&to=2024-01-20', + baseUrl + ), { + redirect: 'manual' + } + ) + await assertResponseStatus(res, 200) + + const stats = /** @type {{ day: string, success_rate: number }[]} */( + await res.json() + ) + assert.deepStrictEqual(stats, [ + { day: '2024-01-10', success_rate: 1 / 10, total: '10', successful: '1' }, + { day: '2024-01-20', success_rate: 1 / 20, total: '20', successful: '1' } + ]) + }) + }) }) /**