fix: stats on API rest
parent
9ac81d5957
commit
759b7c4979
|
@ -52,7 +52,6 @@ jobs:
|
|||
ZAMMAD_API_SECRET_KEY: ${{ secrets.ZAMMAD_API_SECRET_KEY }}
|
||||
ATINTERNET_API_SECRET_KEY: ${{ secrets.ATINTERNET_API_SECRET_KEY }}
|
||||
ATINTERNET_API_ACCESS_KEY: ${{ secrets.ATINTERNET_API_ACCESS_KEY }}
|
||||
PLAUSIBLE_API_KEY: ${{ secrets.PLAUSIBLE_API_KEY }}
|
||||
CRISP_API_IDENTIFIER: ${{ secrets.CRISP_API_IDENTIFIER }}
|
||||
CRISP_API_KEY: ${{ secrets.CRISP_API_KEY }}
|
||||
CRISP_WEBSITE_ID: ${{ secrets.CRISP_WEBSITE_ID }}
|
||||
|
@ -108,7 +107,6 @@ jobs:
|
|||
ZAMMAD_API_SECRET_KEY: ${{ secrets.ZAMMAD_API_SECRET_KEY }}
|
||||
ATINTERNET_API_SECRET_KEY: ${{ secrets.ATINTERNET_API_SECRET_KEY }}
|
||||
ATINTERNET_API_ACCESS_KEY: ${{ secrets.ATINTERNET_API_ACCESS_KEY }}
|
||||
PLAUSIBLE_API_KEY: ${{ secrets.PLAUSIBLE_API_KEY }}
|
||||
CRISP_API_IDENTIFIER: ${{ secrets.CRISP_API_IDENTIFIER }}
|
||||
CRISP_API_KEY: ${{ secrets.CRISP_API_KEY }}
|
||||
CRISP_WEBSITE_ID: ${{ secrets.CRISP_WEBSITE_ID }}
|
||||
|
|
|
@ -24,7 +24,6 @@ jobs:
|
|||
ZAMMAD_API_SECRET_KEY: ${{ secrets.ZAMMAD_API_SECRET_KEY }}
|
||||
ATINTERNET_API_SECRET_KEY: ${{ secrets.ATINTERNET_API_SECRET_KEY }}
|
||||
ATINTERNET_API_ACCESS_KEY: ${{ secrets.ATINTERNET_API_ACCESS_KEY }}
|
||||
PLAUSIBLE_API_KEY: ${{ secrets.PLAUSIBLE_API_KEY }}
|
||||
CRISP_API_IDENTIFIER: ${{ secrets.CRISP_API_IDENTIFIER }}
|
||||
CRISP_API_KEY: ${{ secrets.CRISP_API_KEY }}
|
||||
CRISP_WEBSITE_ID: ${{ secrets.CRISP_WEBSITE_ID }}
|
||||
|
|
|
@ -20,7 +20,6 @@ jobs:
|
|||
ZAMMAD_API_SECRET_KEY: ${{ secrets.ZAMMAD_API_SECRET_KEY }}
|
||||
ATINTERNET_API_SECRET_KEY: ${{ secrets.ATINTERNET_API_SECRET_KEY }}
|
||||
ATINTERNET_API_ACCESS_KEY: ${{ secrets.ATINTERNET_API_ACCESS_KEY }}
|
||||
PLAUSIBLE_API_KEY: ${{ secrets.PLAUSIBLE_API_KEY }}
|
||||
CRISP_API_IDENTIFIER: ${{ secrets.CRISP_API_IDENTIFIER }}
|
||||
CRISP_API_KEY: ${{ secrets.CRISP_API_KEY }}
|
||||
CRISP_WEBSITE_ID: ${{ secrets.CRISP_WEBSITE_ID }}
|
||||
|
|
|
@ -16,7 +16,6 @@ jobs:
|
|||
ZAMMAD_API_SECRET_KEY: ${{ secrets.ZAMMAD_API_SECRET_KEY }}
|
||||
ATINTERNET_API_SECRET_KEY: ${{ secrets.ATINTERNET_API_SECRET_KEY }}
|
||||
ATINTERNET_API_ACCESS_KEY: ${{ secrets.ATINTERNET_API_ACCESS_KEY }}
|
||||
PLAUSIBLE_API_KEY: ${{ secrets.PLAUSIBLE_API_KEY }}
|
||||
CRISP_API_IDENTIFIER: ${{ secrets.CRISP_API_IDENTIFIER }}
|
||||
CRISP_API_KEY: ${{ secrets.CRISP_API_KEY }}
|
||||
CRISP_WEBSITE_ID: ${{ secrets.CRISP_WEBSITE_ID }}
|
||||
|
@ -27,7 +26,6 @@ jobs:
|
|||
ZAMMAD_API_SECRET_KEY: ${{ secrets.ZAMMAD_API_SECRET_KEY }}
|
||||
ATINTERNET_API_SECRET_KEY: ${{ secrets.ATINTERNET_API_SECRET_KEY }}
|
||||
ATINTERNET_API_ACCESS_KEY: ${{ secrets.ATINTERNET_API_ACCESS_KEY }}
|
||||
PLAUSIBLE_API_KEY: ${{ secrets.PLAUSIBLE_API_KEY }}
|
||||
CRISP_API_IDENTIFIER: ${{ secrets.CRISP_API_IDENTIFIER }}
|
||||
CRISP_API_KEY: ${{ secrets.CRISP_API_KEY }}
|
||||
CRISP_WEBSITE_ID: ${{ secrets.CRISP_WEBSITE_ID }}
|
||||
|
|
|
@ -5,7 +5,6 @@ ATINTERNET_API_ACCESS_KEY=
|
|||
ATINTERNET_API_SECRET_KEY=
|
||||
DEEPL_API_SECRET=
|
||||
ZAMMAD_API_SECRET_KEY=
|
||||
PLAUSIBLE_API_KEY=
|
||||
SENTRY_AUTH_TOKEN=
|
||||
|
||||
# Utilisez votre propre token (voir issue en haut pour en créé un)
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
import dotenv from 'dotenv'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const fetchApiStats = async (page, start, end, interval) => {
|
||||
if (!process.env.PLAUSIBLE_API_KEY) {
|
||||
throw new Error(
|
||||
"Variables d'environnement manquantes : PLAUSIBLE_API_KEY, nous ne récupérons pas les statistiques d'usage"
|
||||
)
|
||||
}
|
||||
|
||||
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 await 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),
|
||||
])
|
||||
)
|
||||
.filter((x) => !!x)
|
||||
.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
|
||||
}
|
|
@ -5,7 +5,6 @@ import { fileURLToPath } from 'url'
|
|||
import dotenv from 'dotenv'
|
||||
|
||||
import { createDataDir, readInDataDir, writeInDataDir } from '../utils.js'
|
||||
import { apiStats } from './fetch-api-stats.js'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
|
@ -148,6 +147,45 @@ const buildSiteQuery =
|
|||
},
|
||||
})
|
||||
|
||||
const buildAPIQuery =
|
||||
(period, granularity) =>
|
||||
(page = 1) => ({
|
||||
columns: ['page', 'm_page_loads'],
|
||||
space: {
|
||||
s: [617190, 617189],
|
||||
},
|
||||
period: {
|
||||
p1: [period],
|
||||
},
|
||||
evo: {
|
||||
granularity,
|
||||
top: {
|
||||
'page-num': page,
|
||||
'max-results': 100,
|
||||
sort: ['-m_page_loads'],
|
||||
filter: {
|
||||
property: {
|
||||
$AND: [
|
||||
{
|
||||
page_chapter1: {
|
||||
$eq: 'api',
|
||||
},
|
||||
},
|
||||
{
|
||||
page: {
|
||||
$eq: 'evaluate',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
options: {
|
||||
ignore_null_properties: true,
|
||||
},
|
||||
})
|
||||
|
||||
const yesterday = new Date(new Date().setDate(new Date().getDate() - 1))
|
||||
.toISOString()
|
||||
.slice(0, 10)
|
||||
|
@ -181,11 +219,25 @@ const last12Months = {
|
|||
|
||||
const uniformiseData = (data) =>
|
||||
data
|
||||
.map(({ d_evo_day, d_evo_month, m_visits, m_events, ...data }) => ({
|
||||
date: d_evo_day != null ? d_evo_day : d_evo_month,
|
||||
nombre: m_visits != null ? m_visits : m_events,
|
||||
...data,
|
||||
}))
|
||||
.map(
|
||||
({
|
||||
d_evo_day,
|
||||
d_evo_month,
|
||||
m_visits,
|
||||
m_events,
|
||||
m_page_loads,
|
||||
...data
|
||||
}) => ({
|
||||
date: d_evo_day != null ? d_evo_day : d_evo_month,
|
||||
nombre:
|
||||
m_visits != null
|
||||
? m_visits
|
||||
: m_page_loads != null
|
||||
? m_page_loads
|
||||
: m_events,
|
||||
...data,
|
||||
})
|
||||
)
|
||||
// For some reason, an artifact create ghost page with unlogical chapter metrics...
|
||||
// It seems to only by one per month thought... This hacks resolves it
|
||||
.filter(({ m_visits }) => m_visits === undefined || m_visits > 2)
|
||||
|
@ -203,14 +255,14 @@ async function fetchDailyVisits() {
|
|||
const site = uniformiseData(
|
||||
(await fetchApi(buildSiteQuery(last60days, 'D')))[0].Rows
|
||||
)
|
||||
|
||||
const { start, end } = last60days
|
||||
const api = uniformiseData(
|
||||
(await fetchApi(buildAPIQuery(last60days, 'D')))[0].Rows
|
||||
)
|
||||
|
||||
return {
|
||||
pages,
|
||||
site,
|
||||
|
||||
api: await apiStats(start, end, 'date'),
|
||||
api,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,13 +280,14 @@ async function fetchMonthlyVisits() {
|
|||
(await fetchApi(buildSiteQuery(last12Months, 'M')))[0].Rows
|
||||
),
|
||||
]
|
||||
|
||||
const { start, end } = last12Months
|
||||
const api = uniformiseData(
|
||||
(await fetchApi(buildAPIQuery(last12Months, 'M')))[0].Rows
|
||||
)
|
||||
|
||||
return {
|
||||
pages,
|
||||
site,
|
||||
api: await apiStats(start, end, 'month'),
|
||||
api,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,23 +70,6 @@ export const StatsDetail = ({ stats, accessibleMode }: StatsDetailProps) => {
|
|||
[visites]
|
||||
)
|
||||
|
||||
type ApiData = {
|
||||
date: string
|
||||
nombre: { evaluate: number; rules: number; rule: number }
|
||||
}
|
||||
const apiCumul =
|
||||
filter === 'api-rest' &&
|
||||
slicedVisits.length > 0 &&
|
||||
typeof slicedVisits[0]?.nombre === 'object' &&
|
||||
(slicedVisits as ApiData[]).reduce(
|
||||
(acc, { nombre }) => ({
|
||||
evaluate: acc.evaluate + nombre.evaluate,
|
||||
rules: acc.rules + nombre.rules,
|
||||
rule: acc.rule + nombre.rule,
|
||||
}),
|
||||
{ evaluate: 0, rules: 0, rule: 0 }
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<H2>Statistiques détaillées</H2>
|
||||
|
@ -179,24 +162,6 @@ export const StatsDetail = ({ stats, accessibleMode }: StatsDetailProps) => {
|
|||
)}
|
||||
|
||||
<Grid container spacing={2}>
|
||||
{apiCumul ? (
|
||||
<>
|
||||
<BigIndicator
|
||||
main={apiCumul.evaluate}
|
||||
subTitle="Appel à /evaluate"
|
||||
/>
|
||||
<BigIndicator main={apiCumul.rules} subTitle="Appel à /rules" />
|
||||
<BigIndicator main={apiCumul.rule} subTitle="Appel à /rule/*" />
|
||||
</>
|
||||
) : (
|
||||
<BigIndicator
|
||||
main={formatValue(
|
||||
typeof totals === 'number' ? totals : totals.accueil
|
||||
)}
|
||||
subTitle="Visites"
|
||||
/>
|
||||
)}
|
||||
|
||||
{typeof totals !== 'number' && 'simulation_commencee' in totals && (
|
||||
<>
|
||||
{' '}
|
||||
|
@ -222,6 +187,20 @@ export const StatsDetail = ({ stats, accessibleMode }: StatsDetailProps) => {
|
|||
/>
|
||||
</>
|
||||
)}
|
||||
{typeof totals === 'number' && (
|
||||
<BigIndicator
|
||||
main={formatValue(totals)}
|
||||
subTitle={
|
||||
filter === 'api-rest' ? (
|
||||
<>
|
||||
Nombre d'appels à <code>evaluate</code>
|
||||
</>
|
||||
) : (
|
||||
<>Nombre total de visites </>
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Grid>
|
||||
|
||||
{period === 'mois' && !!satisfaction.length && (
|
||||
|
|
|
@ -43,7 +43,7 @@ interface Visites {
|
|||
creer: Page[]
|
||||
pages: Page[]
|
||||
site: Site[]
|
||||
api: API[]
|
||||
api: Site[]
|
||||
}
|
||||
|
||||
interface Site {
|
||||
|
@ -51,13 +51,6 @@ interface Site {
|
|||
nombre: number
|
||||
}
|
||||
|
||||
interface API {
|
||||
date: string
|
||||
evaluate: number
|
||||
rules: number
|
||||
rule: number
|
||||
}
|
||||
|
||||
export enum PageChapter2 {
|
||||
AideDeclarationIndependant = 'aide_declaration_independant',
|
||||
ArtisteAuteur = 'artiste_auteur',
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { useMemo } from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
||||
import { Body } from '@/design-system/typography/paragraphs'
|
||||
import { groupBy } from '@/utils'
|
||||
|
||||
import { Page, PageChapter2, PageSatisfaction, StatsStruct } from './types'
|
||||
|
@ -28,14 +26,7 @@ export function useStatistiques({
|
|||
return rawData.site
|
||||
}
|
||||
if (filter === 'api-rest') {
|
||||
return (rawData.api ?? []).map(({ date, ...nombre }) => ({
|
||||
date,
|
||||
nombre,
|
||||
info:
|
||||
(period === 'jours' ? '2023-06-16' : '2023-06-01') === date ? (
|
||||
<ChangeJune2023 />
|
||||
) : null,
|
||||
}))
|
||||
return rawData.api
|
||||
}
|
||||
if (
|
||||
typeof filter !== 'string' &&
|
||||
|
@ -74,15 +65,6 @@ export function useStatistiques({
|
|||
}
|
||||
}
|
||||
|
||||
const ChangeJune2023 = () => (
|
||||
<Body style={{ maxWidth: '350px' }}>
|
||||
<Trans i18nKey="stats.change_june_2023">
|
||||
Ajout d'un cache sur l'API pour améliorer les performances et réduire le
|
||||
nombre de requêtes.
|
||||
</Trans>
|
||||
</Body>
|
||||
)
|
||||
|
||||
const isPAM = (name: string | undefined) =>
|
||||
name &&
|
||||
[
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue