Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ jobs:
NUXT_SITE_URL: http://localhost:3000
NUXT_PUBLIC_CHARTS_API_BASE: http://localhost:7000
NUXT_PUBLIC_CADA_RESOURCE_ID: 3a6d6fe3-8548-43ed-ad09-72052771447c
NUXT_PUBLIC_TABULAR_API_PREVIEW_RESOURCES_ID: 982d9dd0-365a-4c4b-8a83-75dec40c36bb
Comment thread
nicolaskempf57 marked this conversation as resolved.
NUXT_PUBLIC_TABULAR_API_PREVIEW_STATS_ID: 33cf9a65-3f77-4d88-acd1-bca420d83e60
run: |
# Start cdata server in background using pre-built artifacts
PORT=3000 node .output/server/index.mjs > cdata.log 2>&1 &
Expand Down
2 changes: 2 additions & 0 deletions E2E-TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ Pour tester localement :
```
NUXT_PUBLIC_HOMEPAGE_HERO_IMAGES="hero_15.png" # to avoid homepage image randomness
UDATA_WORKING_DIR="/path/to/udata/" # to allow for udata commands execution (ex: in 2FA flow tests cleanup)
NUXT_PUBLIC_TABULAR_API_PREVIEW_RESOURCES_ID="982d9dd0-365a-4c4b-8a83-75dec40c36bb" # preview dashboard E2E tests
NUXT_PUBLIC_TABULAR_API_PREVIEW_STATS_ID="33cf9a65-3f77-4d88-acd1-bca420d83e60" # preview dashboard E2E tests
```
3. **Lancer les tests** :
```bash
Expand Down
4 changes: 2 additions & 2 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ export default defineNuxtConfig({
schemaValidataUrl: 'https://validata.fr',
tabularApiUrl: 'https://tabular-api.data.gouv.fr',
tabularApiDataserviceId: undefined,
tabularApiPreviewResourcesId: '982d9dd0-365a-4c4b-8a83-75dec40c36bb',
tabularApiPreviewStatsId: '33cf9a65-3f77-4d88-acd1-bca420d83e60',
tabularApiPreviewResourcesId: undefined,
tabularApiPreviewStatsId: undefined,
cadaResourceId: undefined,
cadaDatasetUrl: 'https://www.data.gouv.fr/datasets/avis-et-conseils-de-la-cada',

Expand Down
12 changes: 11 additions & 1 deletion pages/admin/beta/preview-dashboard/fichiers.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
<template>
<TabularExplorer
v-if="config.public.tabularApiPreviewResourcesId"
:resource-id="config.public.tabularApiPreviewResourcesId"
:initial-filters="initialFilters"
>
<TabularToolbar class="py-3" />
<TabularTable />
<TabularMobileFilters />
</TabularExplorer>
<SimpleBanner
v-else
class="mt-2"
type="warning"
>
{{ t('Liste des fichiers non disponible.') }}
</SimpleBanner>
</template>

<script setup lang="ts">
import { TabularExplorer, TabularMobileFilters, TabularTable, TabularToolbar } from '@datagouv/components-next'
import { SimpleBanner, TabularExplorer, TabularMobileFilters, TabularTable, TabularToolbar } from '@datagouv/components-next'
import { buildFiltersFromQuery } from '~/utils/previewDashboard'

const config = useRuntimeConfig()
const route = useRoute()

const { t } = useTranslation()

// The query only seeds the filters, it is never pushed back: /admin keys its
// NuxtPage on the full path, so every filter click would remount the page and
// reset sort and visible columns.
Expand Down
30 changes: 16 additions & 14 deletions pages/admin/beta/preview-dashboard/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
{{ t('Images : prévisualisation limitée aux fichiers de {size} maximum.', { size: imagePreviewSize }) }}
</li>
<li>
{{ t('Seuls les formats représentant au moins 100 ressources sont affichés individuellement.') }}
{{ t('Seuls les formats représentant au moins 10 ressources sont affichés individuellement.') }}
</li>
<li>
{{ t('Certains formats très proches ont été regroupés pour rendre l’analyse plus lisible.') }}
Expand Down Expand Up @@ -64,6 +64,13 @@
>
<AnimatedLoader />
</div>
<SimpleBanner
v-else-if="!config.public.tabularApiPreviewStatsId"
type="warning"
class="mt-2"
>
{{ t('Statistiques de prévisualisations non disponibles.') }}
</SimpleBanner>
<SimpleBanner
v-else-if="error"
type="warning"
Expand Down Expand Up @@ -127,7 +134,7 @@
scope="col"
class="px-3 py-3.5 text-right text-sm font-semibold text-gray-title border-b border-r border-gray-default last:border-r-0"
>
{{ t('% too big') }}
{{ t('% trop volumineux') }}
</th>
<th
scope="col"
Expand Down Expand Up @@ -215,14 +222,14 @@
class="link"
:to="{
path: '/admin/beta/preview-dashboard/fichiers',
query: { format: row.Format },
query: { format: row['format normalisé'] },
}"
>
{{ row.Format }}
{{ row['format normalisé'] }}
</NuxtLink>
</td>
<td class="px-3 py-1 text-right text-sm whitespace-nowrap text-gray-plain border-r border-gray-default last:border-r-0">
<div>{{ formatNumber(row.Nombre) }}</div>
<div>{{ formatNumber(row.nombre) }}</div>
<DeltaIndicator
:value="row.countDelta"
unit="count"
Expand All @@ -238,10 +245,10 @@
{{ formatPercentage(row['% erreur']) }}
</td>
<td class="px-3 py-1 text-right text-sm whitespace-nowrap text-gray-plain border-r border-gray-default last:border-r-0">
{{ formatPercentage(row['% too big']) }}
{{ formatPercentage(row['% trop volumineux']) }}
</td>
<td class="px-3 py-1 text-right text-sm whitespace-nowrap text-gray-plain border-r border-gray-default last:border-r-0">
{{ formatNumber(row['Prévisualisable']) }}
{{ formatNumber(row['prévisualisable']) }}
</td>
<td class="px-3 py-1 text-right text-sm whitespace-nowrap text-gray-plain border-r border-gray-default last:border-r-0">
<PercentageMeter :value="row['% prévisualisable']" />
Expand All @@ -267,7 +274,6 @@ import {
RiFileTextLine,
RiImageLine,
RiLink,
RiMap2Line,
RiQuestionLine,
RiTableLine,
} from '@remixicon/vue'
Expand Down Expand Up @@ -313,10 +319,11 @@ const PAGE_SIZE = 100
// a response cut off at one page would silently skew all of them: keep paging
// until the API has handed over as many rows as it announced.
async function fetchMonthRows(month: string): Promise<PreviewDashboardFormatStat[]> {
if (!config.public.tabularApiPreviewStatsId) return []
const base = `${config.public.tabularApiUrl}/api/resources/${config.public.tabularApiPreviewStatsId}/data/`
const rows: PreviewDashboardFormatStat[] = []
for (let page = 1; ; page++) {
const params = new URLSearchParams({ page: String(page), page_size: String(PAGE_SIZE), Mois__exact: month })
const params = new URLSearchParams({ page: String(page), page_size: String(PAGE_SIZE), mois__exact: month })
const response = await $fetch<TabularDataResponse<PreviewDashboardFormatStat>>(`${base}?${params.toString()}`)
rows.push(...response.data)
if (response.data.length === 0 || rows.length >= response.meta.total) return rows
Expand Down Expand Up @@ -378,20 +385,15 @@ function getFamilyIcon(family: string): Component {
case 'données structurées':
return RiBracesLine
case 'tabulaire':
case 'tableur':
return RiTableLine
case 'api':
return RiCodeSSlashLine
case 'document':
return RiFileTextLine
case 'archive':
return RiArchiveLine
case 'géospatial':
return RiMap2Line
case 'image':
case 'images':
return RiImageLine
case 'lien':
case 'liens':
return RiLink
default:
Expand Down
66 changes: 33 additions & 33 deletions tests-unit/utils/preview-dashboard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import type { PreviewDashboardFormatStat } from '~/types/preview-dashboard'

function makeStat(overrides: Partial<PreviewDashboardFormatStat> = {}): PreviewDashboardFormatStat {
return {
'Famille de format': 'Tabulaire',
'Format': 'csv',
'Nombre': 10,
'Prévisualisable': 5,
'famille': 'Tabulaire',
'format normalisé': 'csv',
'nombre': 10,
'prévisualisable': 5,
'% catalogue': 2.5,
'% erreur': 0.5,
'% too big': 0.2,
'% trop volumineux': 0.2,
'% prévisualisable': 50,
'% prévisualisation manquante': 0.3,
'Mois': '2026-07',
'mois': '2026-07',
'__id': 1,
...overrides,
}
Expand Down Expand Up @@ -147,19 +147,19 @@ describe('getDeltaDirection', () => {
describe('computeFamilyStats', () => {
it('ignores rows from other months', () => {
const rows = [
makeStat({ Mois: '2026-07', __id: 1 }),
makeStat({ Mois: '2026-06', __id: 2 }),
makeStat({ Mois: '2026-05', __id: 3 }),
makeStat({ mois: '2026-07', __id: 1 }),
makeStat({ mois: '2026-06', __id: 2 }),
makeStat({ mois: '2026-05', __id: 3 }),
]
const stats = computeFamilyStats(rows, '2026-07', '2026-06')
expect(stats).toHaveLength(1)
expect(stats[0].count).toBe(10)
})

it('weights percentage columns by Nombre', () => {
it('weights percentage columns by nombre', () => {
const rows = [
makeStat({ 'Format': 'csv', 'Nombre': 10, '% erreur': 10, '% too big': 5, '% prévisualisation manquante': 2, '__id': 1 }),
makeStat({ 'Format': 'xlsx', 'Nombre': 30, '% erreur': 20, '% too big': 15, '% prévisualisation manquante': 6, '__id': 2 }),
makeStat({ 'format normalisé': 'csv', 'nombre': 10, '% erreur': 10, '% trop volumineux': 5, '% prévisualisation manquante': 2, '__id': 1 }),
makeStat({ 'format normalisé': 'xlsx', 'nombre': 30, '% erreur': 20, '% trop volumineux': 15, '% prévisualisation manquante': 6, '__id': 2 }),
]
const stats = computeFamilyStats(rows, '2026-07', '2026-06')
expect(stats[0].percentageError).toBeCloseTo(17.5)
Expand All @@ -169,9 +169,9 @@ describe('computeFamilyStats', () => {

it('computes percentageOfCatalog against the current month total', () => {
const rows = [
makeStat({ 'Famille de format': 'Tabulaire', 'Nombre': 10, '__id': 1 }),
makeStat({ 'Famille de format': 'Document', 'Format': 'pdf', 'Nombre': 30, '__id': 2 }),
makeStat({ 'Famille de format': 'Tabulaire', 'Mois': '2026-06', 'Nombre': 100, '__id': 3 }),
makeStat({ famille: 'Tabulaire', nombre: 10, __id: 1 }),
makeStat({ 'famille': 'Document', 'format normalisé': 'pdf', 'nombre': 30, '__id': 2 }),
makeStat({ famille: 'Tabulaire', mois: '2026-06', nombre: 100, __id: 3 }),
]
const stats = computeFamilyStats(rows, '2026-07', '2026-06')
const tabulaire = stats.find(s => s.family === 'Tabulaire')!
Expand All @@ -180,12 +180,12 @@ describe('computeFamilyStats', () => {

it('computes positive, negative and zero deltas against the previous month', () => {
const rows = [
makeStat({ 'Famille de format': 'Tabulaire', 'Format': 'csv', 'Mois': '2026-07', 'Nombre': 13, 'Prévisualisable': 13, '__id': 1 }),
makeStat({ 'Famille de format': 'Tabulaire', 'Format': 'csv', 'Mois': '2026-06', 'Nombre': 10, 'Prévisualisable': 5, '__id': 2 }),
makeStat({ 'Famille de format': 'Document', 'Format': 'pdf', 'Mois': '2026-07', 'Nombre': 8, 'Prévisualisable': 4, '__id': 3 }),
makeStat({ 'Famille de format': 'Document', 'Format': 'pdf', 'Mois': '2026-06', 'Nombre': 10, 'Prévisualisable': 5, '__id': 4 }),
makeStat({ 'Famille de format': 'Image', 'Format': 'png', 'Mois': '2026-07', 'Nombre': 10, 'Prévisualisable': 5, '__id': 5 }),
makeStat({ 'Famille de format': 'Image', 'Format': 'png', 'Mois': '2026-06', 'Nombre': 10, 'Prévisualisable': 5, '__id': 6 }),
makeStat({ 'famille': 'Tabulaire', 'format normalisé': 'csv', 'mois': '2026-07', 'nombre': 13, 'prévisualisable': 13, '__id': 1 }),
makeStat({ 'famille': 'Tabulaire', 'format normalisé': 'csv', 'mois': '2026-06', 'nombre': 10, 'prévisualisable': 5, '__id': 2 }),
makeStat({ 'famille': 'Document', 'format normalisé': 'pdf', 'mois': '2026-07', 'nombre': 8, 'prévisualisable': 4, '__id': 3 }),
makeStat({ 'famille': 'Document', 'format normalisé': 'pdf', 'mois': '2026-06', 'nombre': 10, 'prévisualisable': 5, '__id': 4 }),
makeStat({ 'famille': 'Image', 'format normalisé': 'png', 'mois': '2026-07', 'nombre': 10, 'prévisualisable': 5, '__id': 5 }),
makeStat({ 'famille': 'Image', 'format normalisé': 'png', 'mois': '2026-06', 'nombre': 10, 'prévisualisable': 5, '__id': 6 }),
]
const stats = computeFamilyStats(rows, '2026-07', '2026-06')
const tabulaire = stats.find(s => s.family === 'Tabulaire')!
Expand All @@ -202,10 +202,10 @@ describe('computeFamilyStats', () => {

it('sums every format of a family before comparing it to the previous month', () => {
const rows = [
makeStat({ Format: 'csv', Mois: '2026-07', Nombre: 30, Prévisualisable: 24, __id: 1 }),
makeStat({ Format: 'xlsx', Mois: '2026-07', Nombre: 10, Prévisualisable: 6, __id: 2 }),
makeStat({ Format: 'csv', Mois: '2026-06', Nombre: 20, Prévisualisable: 10, __id: 3 }),
makeStat({ Format: 'xlsx', Mois: '2026-06', Nombre: 10, Prévisualisable: 5, __id: 4 }),
makeStat({ 'format normalisé': 'csv', 'mois': '2026-07', 'nombre': 30, 'prévisualisable': 24, '__id': 1 }),
makeStat({ 'format normalisé': 'xlsx', 'mois': '2026-07', 'nombre': 10, 'prévisualisable': 6, '__id': 2 }),
makeStat({ 'format normalisé': 'csv', 'mois': '2026-06', 'nombre': 20, 'prévisualisable': 10, '__id': 3 }),
makeStat({ 'format normalisé': 'xlsx', 'mois': '2026-06', 'nombre': 10, 'prévisualisable': 5, '__id': 4 }),
]
const stats = computeFamilyStats(rows, '2026-07', '2026-06')
// 40 - 30 resources, and 30/40 previewable against 15/30 the month before
Expand All @@ -214,7 +214,7 @@ describe('computeFamilyStats', () => {
})

it('leaves deltas undefined when there is no previous month data', () => {
const rows = [makeStat({ Mois: '2026-07', __id: 1 })]
const rows = [makeStat({ mois: '2026-07', __id: 1 })]
const stats = computeFamilyStats(rows, '2026-07', '2026-06')
expect(stats[0].countDelta).toBeUndefined()
expect(stats[0].previewDelta).toBeUndefined()
Expand All @@ -224,17 +224,17 @@ describe('computeFamilyStats', () => {

it('leaves previewDelta undefined when the current month count is zero', () => {
const rows = [
makeStat({ Mois: '2026-07', Nombre: 0, Prévisualisable: 0, __id: 1 }),
makeStat({ Mois: '2026-06', Nombre: 10, Prévisualisable: 5, __id: 2 }),
makeStat({ mois: '2026-07', nombre: 0, prévisualisable: 0, __id: 1 }),
makeStat({ mois: '2026-06', nombre: 10, prévisualisable: 5, __id: 2 }),
]
const stats = computeFamilyStats(rows, '2026-07', '2026-06')
expect(stats[0].previewDelta).toBeUndefined()
})

it('sorts families by descending count', () => {
const rows = [
makeStat({ 'Famille de format': 'Tabulaire', 'Nombre': 10, '__id': 1 }),
makeStat({ 'Famille de format': 'Document', 'Format': 'pdf', 'Nombre': 30, '__id': 2 }),
makeStat({ famille: 'Tabulaire', nombre: 10, __id: 1 }),
makeStat({ 'famille': 'Document', 'format normalisé': 'pdf', 'nombre': 30, '__id': 2 }),
]
const stats = computeFamilyStats(rows, '2026-07', '2026-06')
expect(stats.map(s => s.family)).toEqual(['Document', 'Tabulaire'])
Expand All @@ -246,8 +246,8 @@ describe('computeFamilyStats', () => {
describe('computeFamilyStats summary totals', () => {
it('sums only the rows of the requested month', () => {
const rows = [
makeStat({ Mois: '2026-07', Nombre: 10, Prévisualisable: 5, __id: 1 }),
makeStat({ Mois: '2026-06', Nombre: 7, Prévisualisable: 4, __id: 2 }),
makeStat({ mois: '2026-07', nombre: 10, prévisualisable: 5, __id: 1 }),
makeStat({ mois: '2026-06', nombre: 7, prévisualisable: 4, __id: 2 }),
]
const stats = computeFamilyStats(rows, '2026-07', '2026-06')
const total = stats.reduce((sum, family) => sum + family.count, 0)
Expand All @@ -258,7 +258,7 @@ describe('computeFamilyStats summary totals', () => {
})

it('returns no family when there are no rows for the requested month', () => {
const rows = [makeStat({ Mois: '2026-06', __id: 1 })]
const rows = [makeStat({ mois: '2026-06', __id: 1 })]
expect(computeFamilyStats(rows, '2026-07', '2026-06')).toEqual([])
})

Expand Down
Loading
Loading