Skip to content

Latest commit

 

History

History
215 lines (171 loc) · 19.4 KB

File metadata and controls

215 lines (171 loc) · 19.4 KB

Feuille de route — theses-quebec

État au 29 avril 2026. Document vivant : ouvre une PR pour proposer un ajout, ou réagis dans une issue pour discuter d'une priorité.

v0.2 livrée le 26 avril 2026 — Sprint 1 quasi complet (1.1 à 1.6), plus 2.3 (investigué) et 3.1 (24 tests). Le seul item Sprint 1 restant est 1.7 (highlight diacritiques-aware, faible valeur).

v0.3 livrée le 26 avril 2026 — Cap 500→2 000/source (5 002 → 20 171 records, ×4), Polytechnique ajoutée, taxonomie 33 → 74 disciplines Érudit-aligned, classifieur bilingue, audit a11y FastAPI, SEO (sitemap + JSON-LD Dataset). 1,04 % records non classés (vs 28,6 % au prototype, 0,02 % avec LLM en v0.2).

v0.4 livrée le 27 avril 2026 — Cap totalement levé (×6.7 → 123 824 records) ; harvest incrémental + suppressions OAI honorées (25 993 tombstones traités) ; toutes les sources passent à des prefixes riches (dim / oai_etdms / uketd_dc) qui exposent une discipline qualifiée par l'institution ; 3-pass classifier avec attribution de provenance explicite (auth 60 % / rule 18 % / rule_abstract 6 % / llm 17 %). 0,21 % records non classés. Couverture : 16 sources québécoises, toutes répondent (UQAM/TÉLUQ inclus).

v0.5 livrée le 27 avril 2026 — Migration Pagefind : MiniSearch (26 MB initial) → Pagefind chunks à la demande (~50 KB initial + 100-300 KB par requête). Abstracts re-indexés (1 500 chars). Stemming français + anglais natif. Excerpts surlignés natifs.

v0.5.1 livrée le 27 avril 2026 — Refactor : extraction de toute la logique UI partagée (~780 lignes) dans web/common.js + adaptateurs web/backends/{fastapi,pagefind}.js. L'a11y de web/index.html est désormais automatiquement disponible sur web/static.html (fini le backport). Click sur titre ouvre le modal détail (et non la source).

Légende

  • Effort : S ≤ 4 h · M ½–2 jours · L une semaine et plus
  • Valeur : ✶ marginal · ✶✶ utile · ✶✶✶ change l'utilité du produit
  • Statut : ⏳ à faire · 🟡 en cours · ✅ fait · 🚫 abandonné

Sprint 1 — Quick wins UX (~1 jour total)

Ce qui ferait passer le projet de démo à outil de travail pour un·e chercheur·se.

# Amélioration Effort Valeur Statut
1.1 Vue détail par thèse — modal au clic d'une carte, abstract complet, tous les champs DC, lien vers le PDF source. M ✶✶✶ 738434a
1.2 Export citation — bouton « Copier la citation » dans le détail (BibTeX, APA, RIS). S ✶✶✶ 738434a
1.3 Auteur cliquable — clic sur un nom → filtre par auteur. Stockage : facette dédiée. S ✶✶✶ 208a4bf
1.4 Discipline cliquable sur les cartes — clic sur la pill → sélectionne la facette. S ✶✶ 208a4bf
1.5 Mode sombredark: Tailwind + toggle dans le header. Respect prefers-color-scheme. S ✶✶ 229ff0c
1.6 Bouton « Copier le lien » — visible quand des filtres sont actifs. URL est déjà sync, juste rendre le geste explicite. S 229ff0c
1.7 Surlignage diacritiques-aware<mark> couvre maintenant les variantes accentuées (« éducation » highlighte sur query « education » et inversement). Position-mapping NFD-aware dans common.js::highlight(). S ✅ v0.5.2

Sprint 2 — Couverture & données (~1 weekend)

Le projet vit ou meurt sur la profondeur du corpus.

# Amélioration Effort Valeur Statut
2.1 Lever le cap 500/source — re-harvester sans --max-per-source. Estimation : ~50 000 records, DB ~250 MB. S ✶✶✶ ✅ v0.4 (cap totalement levé · 123 824 records)
2.2 Migrer vers Pagefind — à 50k records l'index MiniSearch dépasse 30 MB. Pagefind charge des chunks à la demande, ~100–500 KB par session. M ✶✶✶ ✅ v0.5
2.3 Ajouter Éruditoai.erudit.org, couvre revues + thèses. Le déclencheur initial du projet. Investigué : OAI-PMH ne sert que des articles de revues (aucun set thèses, dc:type=text); les thèses Érudit sont fédérées depuis Papyrus/Savoirs UdeS/Archipel — déjà moissonnés directement. Voir notes dans sources.yaml. M ✶✶✶ ✅ investigué
2.4 Étendre la taxonomie disciplinaire — passer de 33 à 60+ catégories (sous-disciplines). Re-classifier en batch. M ✶✶ ✅ v0.3 (74 disciplines, Érudit-aligned)
2.5 Re-classifier l'historique quand classify.py évolue — bouton CLI classify-existing. S ✶✶
2.6 Dépôts canadiens hors Québec — UofT, UBC, McMaster, Dalhousie. OAI-PMH partout. Renomme en theses-canada ? L ✶✶
2.7 Fallback PDF text extraction — pour les records sans abstract, extraire les 500 premiers mots du PDF (institutions qui le permettent). Améliore la classif LLM. L ✶✶ éthique

Sprint 3 — Qualité & confiance (~1 semaine cumulée)

# Amélioration Effort Valeur Statut
3.1 Tests Pythonpytest sur normalize.py (extraction année, type), classify.py (mots-clés). Ajouter à ci.yml. S ✶✶ e00c3b3 (24 tests)
3.2 Audit accessibilité — ARIA labels sur facettes/pagination, traversée clavier des résultats, contraste. Test lighthouse. M ✶✶ ✅ v0.3 (FastAPI) + v0.5.1 (auto-applique aux 2 versions via common.js partagé)
3.3 i18n EN — toggle FR/EN dans le header. Interface uniquement, pas de traduction des résumés. M
3.4 OGP / cartes sociales<meta> pour partages X/Bluesky/LinkedIn. Capture statique du site. S
3.5 Sitemap.xml + JSON-LD Dataset — visibilité Google Scholar / Datasets. S ✶✶ ✅ v0.3
3.6 Documentation interne (docs/) — schéma DB, format JSONL Gemini, règles de classif. M
3.7 CONTRIBUTORS.md auto-générégh contributor list. S

Sprint 4 — Écosystème open data (~2 semaines)

# Amélioration Effort Valeur Statut
4.1 Endpoint OAI-PMH ré-exposant le corpus enrichi — autres agrégateurs peuvent moissonner notre classification. Ironie : on devient une source. M ✶✶
4.2 API JSON publique stable/api/v1/search, /api/v1/sources. Doc OpenAPI. Hébergée Fly.io ou Cloudflare Workers + D1. L ✶✶ dépend de 4.3
4.3 Déploiement Cloudflare Workers + D1 — backend serverless qui sert l'API et le frontend. Coût quasi-nul. M
4.4 Webhook de re-publication — quand un dépôt source met à jour un record, on reflète sous 24 h. L infrastructure
4.5 Dataset HuggingFace — uploader le corpus normalisé comme dataset libre. S ✶✶ doc seulement

Sprint 5 — Recherche avancée (selon l'appétit)

# Amélioration Effort Valeur Statut
5.1 Recherche sémantique — embeddings sentence-transformer multilingue (LaBSE / paraphrase-multilingual-MiniLM), index FAISS / WASM, recherche par similarité. ~50 MB. L ✶✶✶ scale
5.2 « Did you mean? » — suggestions de correction sur 0 résultat. Trigramme + dist. Levenshtein. S ✶✶ ✅ v0.6
5.3 Recherche par auteur fuzzy — variantes nom/prénom (« Jodoin, M. » vs « Maude Jodoin »). M ✶✶
5.4 Compare 2+ disciplines / décennies — vue côte-à-côte, courbes temporelles. M
5.5 Graphique temporel — distribution annuelle par discipline (chart.js / svg natif). S ✶✶ ✅ v0.6 (par décennie)
5.6 Sauvegarder une recherche — localStorage, retour facile aux dernières requêtes + recherches épinglées (« Mes recherches »). S ✅ v0.6.2
5.7 Détection de doublons — même thèse archivée à plusieurs endroits (rare mais existe). M

Sprint 6 — Opérations & déploiement alternatif

# Amélioration Effort Valeur Statut
6.1 Dockerfile + fly.toml — pour ceux qui veulent l'auto-héberger avec backend FastAPI. S
6.2 deploy/systemd/ + Caddyfile — recettes self-host VPS Linux. S
6.3 Monitoring de santé OAI — page /status qui ping chaque endpoint chaque 6 h, affiche un status board. M
6.4 Export régulier sur archive.org — preservation des snapshots de la DB. S ✶✶
6.5 Distribution DB hors LFS — DB publiée comme asset GitHub Release (zstd). Le quota LFS gratuit (1 Go) ne tenait pas le rythme une fois la DB à 666 Mo. S ✶✶ ✅ v0.6.3

Idées exploratoires (pas planifiées)

  • PWA / offline-first — installable, fonctionne sans connexion une fois chargé. Cache search.json via Service Worker.
  • Mode chercheur·se connecté·e — sauvegarder ses thèses pertinentes, annoter, exporter une bibliographie. Demande auth → augmente la complexité d'un cran.
  • Notifications nouvelle thèse — RSS / Atom feed des nouveaux records par discipline.
  • Visualisation des co-auteurs — graphe par université, par décennie.
  • API LLM publique pour Q&A sur le corpus — RAG sur les abstracts. Coûts variables.

v0.2 — livrée ✅

1.1 Vue détail · 1.2 Export citation · 1.3 Auteur cliquable · 1.4 Discipline cliquable · 1.5 Mode sombre · 1.6 Copier le lien · 2.3 Érudit (investigué) · 3.1 Tests Python (24)

v0.3 — livrée ✅ (saut quantitatif)

2.1 partiel (cap 2000) · 2.4 Taxonomie 33→74 · 3.2 a11y FastAPI · 3.5 Sitemap+JSON-LD

v0.4 — livrée ✅ (richesse + incrémental)

  • 2.1 Cap totalement levé : 123 824 records (×6.7)
  • Métadonnées riches : 14/16 sources sur dim / oai_etdms / uketd_dc au lieu d'oai_dc
  • Authoritative discipline : 100 775 records avec discipline curée par l'institution
  • 3-pass classifier : Pass 0 (auth) / Pass 1 (primary) / Pass 2 (abstract) avec provenance trackée
  • Harvest incrémental + tombstones honorés (25 993 suppressions)
  • 1.7 highlight, encore ⏳

v0.5 — livrée ✅ (scaling de la recherche)

  • 2.2 Migration Pagefind — bundle initial ÷500
  • Abstracts re-indexés (perdus en v0.3-v0.4 pour cause de taille)
  • Stemming français + anglais natif

v0.5.1 — livrée ✅ (architecture)

  • Refactor majeur : web/common.js (UI partagée) + 2 backends (fastapi.js/pagefind.js)
  • 780+ lignes de duplication JS éliminées
  • A11y backportée automatiquement (3.2 désormais complet)
  • UX : click sur titre ouvre le modal au lieu de naviguer

v0.5.2 — livrée ✅

  • 1.7 Surlignage diacritiques-aware — <mark> couvre les variantes accentuées dans les deux sens

v0.6 — livrée ✅ (qualité de recherche)

  • 5.2 « Did you mean? » — Levenshtein-tolérant (1 typo / 4 chars), vocabulaire construit à partir des labels de facettes (discipline + dépôt) repliés NFD-lowercase. Suggestion cliquable injectée dans l'état vide.
  • 5.5 Graphique temporel — barres horizontales par décennie, largeur proportionnelle au max. Click toggle filtre year_min/year_max. Trié chronologiquement, aria-pressed + aria-label.

v0.6.1 — livrée ✅ (qualité des données + polish UI)

  • Bug majeur — auteurs DSpace : parse_dim projetait dc.contributor.author vers contributor au lieu de creator, donc 100 % des records UdeM, Laval, Sherbrooke et Bishop's avaient un champ auteur vide (40 % du corpus). Fix dans parse_dim + re-harvest --full des 4 sources DSpace : 71 307 records → 5 records sans auteur (0,0 %).
  • Direction : nouveau champ advisors. Parsé depuis dc.contributor.advisor (DIM) et <etdms:contributor role="advisor"> (ETDMS). Surface dans la carte (Direction : …) et le modal (rangée labellisée), inclus dans l'index Pagefind, BibTeX note = {Sous la direction de …}, RIS A2 - …. Couverture : ~70 400 records (UdeM 99,2 %, Sherbrooke 99,9 %, Laval 97,1 %, Bishop's 82,1 %). Sources EPrints/UKETD n'exposent rien via OAI.
  • Modal restructuré : grille <dl> avec rangées Auteur·rice / Direction / Provenance qui se replient quand vide.
  • Bandeau de mise à jour : startUpdateWatcher dans common.js poll meta.json à visibilitychange + toutes les 15 min ; affiche un bandeau orange « Une nouvelle version est disponible — Actualiser » avec rechargement 1-clic, dismissable.
  • Préséance discipline corrigée : manual > auth > llm > rule_abstract > rule. L'upsert utilise maintenant un rang numérique au lieu d'une simple liste « préserve ». 1 524 records mal classés par le LLM (avant que authoritative_discipline existe) corrigés via auth + 4 473 raffinements de provenance. Cas type : « Morphologie et cycle annuel de l'Ancolie du Canada »Linguistique (llm)Biologie (auth).
  • Index Pagefind unifié : forceLanguage: "fr" au build pour éviter le partitionnement par langue (le frontend ne chargeait que le sous-index fr au splash, masquant 26 420 records anglais ; le compteur d'en-tête disait 177 413 mais la recherche vide en retournait 150 993).
  • Graphique décennies : filtre les barres avec n=0 pour qu'une décennie sélectionnée fasse disparaître les autres au lieu d'afficher 16 slivers à 2 %.
  • Recherche debounced — 200 ms → 250 ms, vidage instantané (pas de délai en effaçant), short-circuit si la requête trim-égalise l'état actuel.
  • Polish : suppression du clear-X natif WebKit qui chevauchait notre badge esc ; lien footer pointe vers data/theses.db (LFS) au lieu du défunt search.json ; footer crédite Pagefind, plus MiniSearch ; tag de version v0.1 → v0.6.
  • Workflow Pages élargi à web/** (auparavant web/static.html seul, donc les modifs common.js ne re-déployaient pas).

v0.6.2 — livrée ✅ (couverture + harvester resilience)

  • Bug majeur — UKETD empty-payload : UQAM (et à degré moindre UQAC) émettait <uketd_dc:uketddc/> vide pour ~80 % de ses records pré-2014, alors que oai_dc retournait le DC complet pour les mêmes identifiants. Notre parse_uketd_dc retournait un dc vide → normalize_record retournait None → record silencieusement écarté.
  • Fix dans ingest_record : fallback per-record vers oai_dc quand le préfixe configuré rend un payload vide. Coût : un appel HTTP supplémentaire uniquement pour les records autrement perdus. Compteur fallback ajouté aux stats par-source.
  • Récupération : UQAM 3 433 → 13 452 (+10 019 records), UQAC 3 450 → 3 470 (+20). Le record d'exemple oai:archipel.uqam.ca:4005 (« Les pratiques des enseignants d'expérience en milieu socio-économiquement faible », Maude Jodoin, 2010) est désormais ingéré.
  • Audit complet des 16 sources : probé chaque source pour détecter le même bug. Résultat — seuls UQAM et UQAC affectés (uketd_dc); UQAR, UQAT, INRS sont propres (uketd_dc rempli partout); UQO false alarm (les premiers IDs sont des tombstones); UQTR re-harvesté pour confirmer (fallback=0, +7 records). Sources dim et oai_etdms utilisent un autre chemin de code et ne sont pas touchées.
  • Comparaison OpenAlex : confirmé que notre couverture des thèses québécoises est plus complète que OpenAlex sur tous les dépôts. UQAM Archipel n'est même pas indexé comme Source dans OpenAlex — les 13 452 records UQAM sont essentiellement uniques à notre projet dans l'écosystème open data.
  • Corpus total : 177 413 → 187 459 (+10 046).

v0.6.3 — livrée ✅ (type-classification + distribution DB)

  • Bug majeur — McGill 100 % mal classé : McGill exposait oai_dc qui collapse le diplôme à <dc:type>Thesis</dc:type> sans distinction master/doctoral. 55 589 records → 98 % marqués thesis alors que la majorité sont des mémoires (les premiers 675 records échantillonnés : 672 master, 3 doctoral). McGill bascule vers oai_etdms qui expose <degree><name>Doctor of Philosophy / Master of Engineering> + <degree><discipline>Department of …>. Le provider Blacklight de McGill annule une page ListRecords entière sur cannotDisseminateFormat quand un seul record refuse la sérialisation ETDMS — workaround : _bump_token_offset parse le curseur :OFFSET du resumption token et incrémente d'1 jusqu'à passer la fenêtre fautive. Résultat : 963 mem / 54 626 thes → 34 756 / 20 835, et 81 % de McGill ont maintenant une discipline autoritative (vs 0 %).
  • Bug parser — qualificationlevel ignoré : parse_uketd_dc lisait degreelevel et qualificationname mais ratait <uketdterms:qualificationlevel> (la VRAIE source du doctoral/masters à ÉTS, INRS, UQO, UQAR, UQAT, UQAC). Combiné avec dc:type="Thèse" ou "Mémoire ou thèse" ambigu, ça produisait des classifications uniformément fausses : ÉTS = 100 % memoire, INRS = 100 % thesis, UQO = 100 % thesis. Fix dans parsers.py + ajout des tokens nus "doctoral", "masters", "doctor of" dans DOCTORAL_TYPE_SIGNALS / MASTER_TYPE_SIGNALS (normalize.py). ÉTS basculé oai_dcuketd_dc dans sources.yaml.
  • Splits redressés après re-harvest --full :
    • ÉTS : 3 397 mem / 0 thes → 2 285 / 1 112
    • INRS : 0 / 2 867 → 1 644 / 1 223
    • UQO : 0 / 1 108 → 665 / 443
    • UQAT : 844 / 218 → 680 / 382 (engd doctorats récupérés)
    • UQAC : 2 917 / 553 → 2 768 / 704 (engd)
    • 9 autres sources audités explicitement, classifications confirmées correctes
  • DB → GitHub Releases : la DB committée atteignait 666 Mo après les re-harvests, ce qui faisait sauter le quota Git LFS gratuit (1 Go) en 1-2 commits. Migration : npm run db:release → strip FTS5 + VACUUM (-34 %) → zstd -19 (-83 %) → gh release create db-YYYY-MM-DD --latest. Wire size : 666 Mo → 76 Mo (88 % de bande passante en moins par build Pages). npm run db:fetch télécharge + vérifie SHA-256 + décompresse ; harvester/db.py reconstruit FTS5 transparemment au premier connect() (~8 s). Workflow Pages déclenche maintenant sur release: published. Historique git réécrit (git filter-repo) pour purger les 4,3 Go de blobs LFS accumulés.
  • Modal de signalement : bouton "Signaler un problème" dans le modal détail → menu déroulant (mauvaise discipline / mauvais type / métadonnées / autre) → ouvre une issue GitHub pré-remplie avec l'identifiant OAI, le titre, le dépôt, le type/discipline actuels, et un template de saisie. Zéro backend (deep-link github.com/issues/new), fonctionne sur les deux variantes FastAPI et statique.
  • Lock contention sqlite : harvester db.py passe timeout=60 sur la connexion (défaut Python = 5 s) — évite les ! record error: database is locked quand un harvest concurrent attend l'écrivain.
  • CLAUDE.md ajouté à la racine pour donner aux instances Claude Code futures la mappe des 4 mondes de préfixes OAI, du skip-window McGill, et de la précédence du classifieur 3-pass.
  • Corpus total : 187 459 → 187 463.

v0.7 — proposition (qualité + données)

  1. 3.6 Documentation interne (docs/ schema DB, format JSONL Gemini)
  2. 2.7 Fallback PDF text extraction pour records sans abstract
  3. 3.3 i18n EN (toggle FR/EN)

v0.8+ — propositions ouvertes

  • 2.6 Dépôts canadiens hors-Québec (rebrand theses-canada?)
  • 5.1 Recherche sémantique (embeddings)
  • 4.5 Dataset sur HuggingFace
  • 4.1 OAI-PMH ré-exposant le corpus enrichi