# Chaîne de données

:::tip{title="En bref"}
`data:extract` copie les sources telles quelles dans `data/raw/`, `data:build` les fusionne et publie des fiches JSON + Markdown dans `data/public/`, le site se génère statiquement à partir de ces fiches. Décision : [ADR 008](/decisions/chaine-de-donnees-ouvertes).
:::

<Mermaid
  title="Chaîne de données"
  chart={`flowchart LR
  subgraph Sources
    RN[RNSR] & SC[scanR] & HA[HAL] & AN[Annuaire] & RO[ROR] & WD[Wikidata] & OR[ORCID] & OA[OpenAIRE] & TH[theses.fr] & OX[OpenAlex]
  end
  Sources -->|pnpm data:extract| RAW["data/raw/&lt;instance&gt;/&lt;source&gt;/*.ndjson + manifest.json"]
  RAW -->|pnpm data:build| PUB["data/public/&lt;instance&gt;/ structures · persons · projects · stats .json + markdown/"]
  PUB -->|next build| SITE[pages statiques .next-&lt;instance&gt;]
  SITE -->|pagefind| IDX[public/pagefind/&lt;instance&gt;]
  RAW -.->|pnpm data:probe| COV[data/coverage/&lt;instance&gt;]`}
/>

## Fichiers

| Étape      | Code                                                                                                                     | Sortie                                                                            |
| ---------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| Moissonner | `scripts/data/extract.ts`, `extract/sources.ts` (un moissonneur par source), `extract/directory.ts` (annuaire générique) | `data/raw/<instance>/<source>/<entité>.ndjson`, `manifest.json`                   |
| Mesurer    | `scripts/data/probe.ts`, `sources/*.ts`, `model.ts` (modèle cible), `report.ts`                                          | `data/coverage/<instance>/latest.md`, `latest.json`, `<date>.json`                |
| Publier    | `scripts/data/build.ts`, `build/markdown.ts`                                                                             | `data/public/<instance>/*.json`, `markdown/{structures,persons,projects}/<id>.md` |
| Lire       | `src/server/data/publicData.ts` (validation Zod `src/contracts/public.ts`)                                               | Pages Next.js                                                                     |

## Règles de fusion

| Entité    | Clé                  | Liste maître                                                                                                                                                          | Enrichissements, par priorité                                                                                                                                                              |
| --------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Structure | Numéro RNSR          | Annuaire officiel de l'établissement si `masterList: directory` (rapproché par RNSR, code d'unité, nom, sigle), sinon RNSR ∪ structures de recherche actives de scanR | Présentation : annuaire > scanR > Wikidata (≥ 80 caractères). Site : annuaire > RNSR > scanR > Wikidata > ROR. ROR : scanR > Wikidata > AureHAL. Publications : HAL via AureHAL (par RNSR) |
| Personne  | Identifiant scanR    | scanR, filtré (ADR 010)                                                                                                                                               | IdHAL et ORCID pour l'appariement HAL ; expertise = domaines scanR                                                                                                                         |
| Projet    | Numéro de convention | scanR (participants du périmètre)                                                                                                                                     | OpenAIRE pour les projets européens absents de scanR                                                                                                                                       |

Chaque fiche porte la liste de ses sources et, pour la présentation et le site web, la source exacte du champ.

## Volumes et temps (24/10/2026)

|                            | Paris-Saclay                                   | Paris 1         |
| -------------------------- | ---------------------------------------------- | --------------- |
| Moisson                    | ≈ 15 min, 1,7 Go                               | ≈ 5 min, 347 Mo |
| Publication (`data:build`) | 10 s, 67 Mo                                    | 2 s, 12 Mo      |
| Fiches                     | 248 structures, 7 766 personnes, 3 647 projets | 37, 1 931, 383  |

Le fichier le plus lourd est `scanr/persons.ndjson` (1,26 Go) : chaque affiliation embarque la fiche complète de sa structure. `build.ts` le lit en flux.

## Ajouter une source

1. Écrire son moissonneur dans `extract/sources.ts` (fonction `run(config, out)`), l'ajouter à `EXTRACTORS`.
2. Écrire sa sonde dans `sources/<source>.ts` (correspondance vers le modèle cible), l'ajouter à `PROBES`.
3. Déclarer la source dans `SOURCE_IDS` (`src/config/schema.ts`) et dans `sources` de chaque instance.
4. L'utiliser dans `build.ts` en respectant la priorité des champs ; ajouter son libellé dans `SOURCE_LABELS` (`src/contracts/public.ts`).

## Déploiement

Les pages étant statiques, le serveur n'a besoin de `data/public/<instance>/` que pour `/api/metrics` (volumes publiés). Un déploiement `standalone` copie `.next-<instance>/standalone`, `.next-<instance>/static`, `public/` et `data/public/<instance>/`.
