From 6a4606155a902faf146f2aae198a1b1882832343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Rialland?= Date: Thu, 8 Sep 2022 19:28:33 +0200 Subject: [PATCH] Add script fetch api stats from plausible --- site/scripts/fetch-api-stats.js | 52 +++++++++++++++++++++++++++++++ site/scripts/fetch-stats.js | 46 +++++++++++++++++---------- site/source/pages/Stats/Stats.tsx | 46 ++++++++++++++++----------- site/source/pages/Stats/types.ts | 12 +++++-- 4 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 site/scripts/fetch-api-stats.js diff --git a/site/scripts/fetch-api-stats.js b/site/scripts/fetch-api-stats.js new file mode 100644 index 000000000..75228dd22 --- /dev/null +++ b/site/scripts/fetch-api-stats.js @@ -0,0 +1,52 @@ +import 'dotenv/config.js' +import 'isomorphic-fetch' + +const fetchApiStats = async (page, start, end, interval) => { + const url = + 'https://plausible.io/api/v1/stats/timeseries?' + + Object.entries({ + site_id: 'mon-entreprise.urssaf.fr/api', + period: 'custom', + date: start + ',' + end, + interval, + metrics: 'pageviews,visitors', + filters: 'event:page==' + page, + }) + .map(([k, v]) => encodeURIComponent(k) + '=' + encodeURIComponent(v)) + .join('&') + + const result = await fetch(url, { + headers: new Headers({ + Authorization: `Bearer ${process.env.PLAUSIBLE_API_KEY}`, + }), + }) + + return result.json() +} + +export const apiStats = async (start, end, interval) => { + const names = ['evaluate', 'rules', 'rule'] + const data = Object.values( + ( + await Promise.all([ + fetchApiStats('/api/v1/evaluate', start, end, interval), + fetchApiStats('/api/v1/rules', start, end, interval), + fetchApiStats('/api/v1/rules/*', start, end, interval), + ]) + ) + .flatMap(({ results }, i) => + results.map(({ date, pageviews }) => ({ date, [names[i]]: pageviews })) + ) + .reduce( + (acc, el) => ( + acc[el.date] + ? (acc[el.date] = { ...acc[el.date], ...el }) + : (acc[el.date] = el), + acc + ), + {} + ) + ) + + return data +} diff --git a/site/scripts/fetch-stats.js b/site/scripts/fetch-stats.js index d6ce9ee37..41bbc10ba 100644 --- a/site/scripts/fetch-stats.js +++ b/site/scripts/fetch-stats.js @@ -3,6 +3,7 @@ import 'isomorphic-fetch' import fs from 'fs' import path from 'path' import { fileURLToPath } from 'url' +import { apiStats } from './fetch-api-stats.js' import { createDataDir, writeInDataDir } from './utils.js' const matomoSiteVisitsHistory = JSON.parse( @@ -180,7 +181,9 @@ async function fetchDailyVisits() { (await fetchApi(buildSiteQuery(last60days, 'D')))[0].Rows ) - return { pages, site } + const { start, end } = last60days + + return { pages, site, api: await apiStats(start, end, 'date') } } async function fetchMonthlyVisits() { @@ -198,7 +201,9 @@ async function fetchMonthlyVisits() { ), ] - return { pages, site } + const { start, end } = last36Months + + return { pages, site, api: await apiStats(start, end, 'month') } } async function fetchUserAnswersStats() { @@ -292,20 +297,29 @@ async function main() { ) return } - const visitesJours = await fetchDailyVisits() - const visitesMois = await fetchMonthlyVisits() - const satisfaction = uniformiseData( - flattenPage(await fetchApi(buildSatisfactionQuery())) - ).map((page) => { - // eslint-disable-next-line no-unused-vars - const { date, ...satisfactionPage } = { - month: new Date(new Date(page.date).setDate(1)), - ...page, + const [ + visitesJours, + visitesMois, + rawSatisfaction, + retoursUtilisateurs, + nbAnswersLast30days, + ] = await Promise.all([ + fetchDailyVisits(), + fetchMonthlyVisits(), + fetchApi(buildSatisfactionQuery()), + fetchUserFeedbackIssues(), + fetchUserAnswersStats(), + ]) + const satisfaction = uniformiseData(flattenPage(await rawSatisfaction)).map( + (page) => { + // eslint-disable-next-line no-unused-vars + const { date, ...satisfactionPage } = { + month: new Date(new Date(page.date).setDate(1)), + ...page, + } + return satisfactionPage } - return satisfactionPage - }) - const retoursUtilisateurs = await fetchUserFeedbackIssues() - const nbAnswersLast30days = await fetchUserAnswersStats() + ) writeInDataDir('stats.json', { visitesJours, visitesMois, @@ -314,7 +328,7 @@ async function main() { nbAnswersLast30days, }) } catch (e) { - console.log(e) + console.error(e) } } main() diff --git a/site/source/pages/Stats/Stats.tsx b/site/source/pages/Stats/Stats.tsx index fbfc0e9b4..1a9727d2c 100644 --- a/site/source/pages/Stats/Stats.tsx +++ b/site/source/pages/Stats/Stats.tsx @@ -26,7 +26,7 @@ import { Page, PageChapter2, PageSatisfaction, StatsStruct } from './types' import { formatDay, formatMonth } from './utils' type Period = 'mois' | 'jours' -type Chapter2 = PageChapter2 | 'PAM' +type Chapter2 = PageChapter2 | 'PAM' | 'api-rest' type Pageish = Page | PageSatisfaction @@ -105,11 +105,6 @@ const computeTotals = ( : data.map((d) => d.nombre).reduce((a, b) => a + b, 0) } -interface BrushStartEndIndex { - startIndex?: number - endIndex?: number -} - interface StatsDetailProps { stats: StatsStruct } @@ -140,6 +135,9 @@ const StatsDetail = ({ stats }: StatsDetailProps) => { if (!chapter2) { return rawData.site } + if (chapter2 === 'api-rest') { + return rawData.api.map(({ date, ...nombre }) => ({ date, nombre })) + } return filterByChapter2(rawData.pages as Pageish[], chapter2) }, [period, chapter2]) @@ -233,17 +231,21 @@ const StatsDetail = ({ stats }: StatsDetailProps) => { endIndex={endDateIndex} /> -

- Cumuls pour la période{' '} - {period === 'jours' - ? `du ${formatDay(slicedVisits[0].date)} au ${formatDay( - slicedVisits[slicedVisits.length - 1].date - )}` - : `de ${formatMonth(slicedVisits[0].date)}` + - (slicedVisits.length > 1 - ? ` à ${formatMonth(slicedVisits[slicedVisits.length - 1].date)}` - : '')} -

+ {slicedVisits.length > 0 && ( +

+ Cumuls pour la période{' '} + {period === 'jours' + ? `du ${formatDay(slicedVisits[0].date)} au ${formatDay( + slicedVisits[slicedVisits.length - 1].date + )}` + : `de ${formatMonth(slicedVisits[0].date)}` + + (slicedVisits.length > 1 + ? ` à ${formatMonth( + slicedVisits[slicedVisits.length - 1].date + )}` + : '')} +

+ )} { props.onChange( - typeof val === 'string' && val.length + typeof val === 'string' && val.length && !isNaN(parseInt(val)) ? getChapter2(simulateurs[parseInt(val)]) + : val === 'api-rest' + ? val : '' ) }} @@ -398,7 +402,11 @@ function SimulateursChoice(props: { {[ - Tout le site +  Tout le site + , + + +  API REST , ...simulateurs.map((s, i) => ( diff --git a/site/source/pages/Stats/types.ts b/site/source/pages/Stats/types.ts index 22c017b02..ca81df51e 100644 --- a/site/source/pages/Stats/types.ts +++ b/site/source/pages/Stats/types.ts @@ -39,16 +39,24 @@ export enum SatisfactionLevel { TrèsBien = 'très bien', } -export interface Visites { +interface Visites { pages: Page[] site: Site[] + api: API[] } -export interface Site { +interface Site { date: string nombre: number } +interface API { + date: string + evaluate: number + rules: number + rule: number +} + export enum PageChapter2 { AideDeclarationIndependant = 'aide_declaration_independant', ArtisteAuteur = 'artiste_auteur',