SPB Git forge

spb/uqo-imm1003

Public
7commits 1branches 0releases
189.5 MBsize
maindefault branch
48 min agolast push
JavaScript 68% CSS 32%
28.3 KB · 439 lines markdown
Rendered Raw Blame History
1# uqo-imm1003 — Éléments d’évaluation immobilière23> **Notes de cours interactives du cours IMM1003 (IMM1003-20, Automne 2026) de l'Université du Québec en Outaouais (UQO).**4> Site statique généré à partir des sources LaTeX du manuel, servi par le cluster MacLustr.56| | |7|---|---|8| **Site public** | https://www.uqo-imm1003.app |9| **Cours** | IMM1003 — Éléments d’évaluation immobilière · 3 crédits · premier cycle · 14 séances |10| **Trimestre** | Automne 2026 |11| **Cours jumeau** | IMM1033 — Méthodes du coût en évaluation immobilière · https://www.uqo-imm1033.app |12| **App `mld`** | `uqo-imm1003` · port `8160` · nœud actuel : voir `mld status` (au 2026-09-06 : **M4M64b**) |13| **Processus PM2** | `uqo-imm1003` (Node, `server.mjs`) · `uqo-imm1003-ngrok` (tunnel `www.uqo-imm1003.app`) |14| **Santé** | `GET /api/health` → `{"ok":true,"started":…,"root":…,"uptime":…}` |15| **Dépôt** | `gitsrv:~/srv/git/uqo-imm1003.git` (spbgit, git.spboucher.ai) |16| **Auteur / contact** | **Simon-Pierre Boucher** — contact@spboucher.ai |17| **Version publiée** | v2 « refonte majeure » — build du 2026-09-23 23:14:52, déployée le 2026-09-05 23:37 |1819---2021## Sommaire22231. [Présentation](#1-présentation)242. [Ce que contient le site](#2-ce-que-contient-le-site)253. [Fonctionnalités interactives](#3-fonctionnalités-interactives)264. [Structure du cours publiée](#4-structure-du-cours-publiée)275. [Contenu du dépôt](#5-contenu-du-dépôt)286. [Arborescence du site généré (`dist/`)](#6-arborescence-du-site-généré-dist)297. [Le serveur `server.mjs`](#7-le-serveur-servermjs)308. [Chaîne de génération LaTeX → HTML](#8-chaîne-de-génération-latex--html)319. [Déploiement sur le cluster MacLustr (`mld`)](#9-déploiement-sur-le-cluster-maclustr-mld)3210. [Exploitation au quotidien](#10-exploitation-au-quotidien)3311. [Mettre à jour le contenu](#11-mettre-à-jour-le-contenu)3412. [Données personnelles, sécurité, performance](#12-données-personnelles-sécurité-performance)3513. [Identité visuelle](#13-identité-visuelle)3614. [Historique](#14-historique)3715. [Droits et licence](#15-droits-et-licence)3816. [Contact](#16-contact)3940---4142## 1. Présentation4344`uqo-imm1003` est la version web, interactive et « mobile-first » des notes de cours de **IMM1003 — Éléments d’évaluation immobilière**45(IMM1003-20, Automne 2026, 3 crédits · premier cycle · 14 séances), enseigné à l'UQO par Simon-Pierre Boucher.4647Principes, méthodes et pratique de l’évaluation immobilière au Québec : fondements, cadre professionnel, analyse de marché et les trois méthodes d’évaluation.4849Le site n'est **pas** une application avec base de données : c'est un **site statique** (HTML, CSS, JavaScript,50JSON, SVG, PDF) produit par un générateur maison à partir des **sources LaTeX** du manuel (`uqo-notes.sty`) et du51rapport *Le marché de l'habitation au Québec*. Toute l'interactivité (quiz, entraînement, jeux, progression, notes,52recherche, synthèse vocale, explorateurs) s'exécute **dans le navigateur** ; la progression est conservée53localement (`localStorage`) et exportable en JSON. Aucun compte, aucune collecte côté serveur.5455Le rendu est optimisé pour le téléphone (375 px) autant que pour l'ordinateur, installable comme PWA56(`manifest.webmanifest`, icônes 192/512), et indexable (`sitemap.xml`, `robots.txt`, balises Open Graph).5758**Source de vérité du contenu = les fichiers `.tex`** du dépôt de cours `UQO_COURS`. On ne modifie jamais le HTML59généré à la main : on modifie le LaTeX, on reconstruit, on redéploie (voir §11).6061## 2. Ce que contient le site6263Statistiques du build publié (`dist/data/meta.json`, build `2026-09-23 23:14:52`) :6465| Indicateur | Valeur |66|---|---|67| Séances (chapitres) | 14 |68| Mots de notes | 110 717 |69| Définitions | 87 |70| Formules encadrées | 27 |71| Exemples résolus | 85 |72| Exercices | 82 |73| Figures (TikZ → SVG, PDF → SVG) | 61 |74| Questions de quiz (14 chapitres) | 109 |75| Entrées de glossaire | 81 |76| Fiches recto-verso | 195 |77| Explorateurs interactifs insérés dans les séances | 9 |78| Générateurs de problèmes d'entraînement | 13 |79| Séquences (jeu de remise en ordre) | 6 |80| Entrées de l'index de recherche plein texte | 2 406 |81| Diapositives (PDF, une par séance) | 14 |82| Travaux pratiques publiés (énoncé PDF + gabarit Excel) | 3 |83| Rapport marché de l'habitation | 21 sections · 67 figures SVG · 28 tableaux |84| Fichiers dans `dist/` | 591 (≈ 831 Mo) |8586**Publié** : notes intégrales des 14 séances, annexes, bibliographie, plan de cours (PDF), notes complètes (PDF),87diapositives (PDF), énoncés des TP et gabarits Excel, rapport marché (HTML + PDF + données HPI ACI xlsx + script88Python d'analyse).8990**Jamais publié** (règle absolue du projet) : solutionnaires des TP, questionnaires d'examen, grilles de correction.91Le générateur ne les copie pas ; ne pas les ajouter à la main dans `dist/`.9293## 3. Fonctionnalités interactives9495### 3.1 Séances (`/seance/01` … `/seance/14`)96- Notes intégrales converties depuis LaTeX : boîtes pédagogiques (définition, formule, exemple, exercice, attention,97  à retenir), **notes marginales** façon Tufte, mathématiques **KaTeX** pré-rendues au build, tableaux, figures98  **TikZ** rendues en SVG, références auteur-année (`references.bib`).99- **Stepper de sections** (précédent / suivant), sommaire latéral avec sections déjà lues, **objectifs cochables**,100  **mode révision** (masque les exemples, met en avant définitions et formules).101- **Explorateurs SVG** insérés à la fin de la première sous-section ciblée (9 par cours, voir §4) : curseurs et102  graphiques recalculés en direct.103- **Surlignage + notes personnelles** (sélection de texte → surligner / annoter ; export Markdown), **lecture audio**104  (synthèse vocale du navigateur, section par section), **quiz de fin de séance** intégré.105- Infobulles de glossaire sur les termes définis.106107### 3.2 Réviser108- `/aide-memoire` : toutes les formules et règles clés par séance.109- `/glossaire` et `/definitions` : glossaire alphabétique, définitions intégrales avec lien vers la séance.110- `/fiches` : fiches recto-verso (question / réponse) mélangeables, filtrables par séance.111- `/carte` : **carte du cours** (SVG radial : parties → séances → objectifs).112- `/exercices` : banque de tous les exercices avec lien vers le contexte.113114### 3.3 S'exercer115- `/quiz` : quiz par séance ou **examen blanc** (tirage aléatoire multi-séances, correction commentée, score).116- `/entrainement` : **problèmes à données aléatoires** (13 générateurs, `data/entrainement.js`) avec solution117  détaillée recalculée à chaque tirage.118- `/jeux` : association (terme ↔ définition), vrai / faux, **remise en ordre** (6 séquences du cours).119- `/outils` : calculateurs spécialisés (`data/outils.js`).120- `/progression` : tableau de bord (séances lues, objectifs cochés, quiz réussis, fiches maîtrisées) ;121  **export / import JSON** pour changer d'appareil.122123### 3.4 Ressources124- `/diapositives` : 14 PDF (`files/slides/`), `/ressources` : téléchargements (plan, notes PDF, TP, gabarits).125- `/rapport` : rapport *Le marché de l'habitation au Québec* en 21 sections HTML (`rapport/0` … `rapport/15`,126  annexes `A` … `F`), 67 figures SVG, 28 tableaux, valeurs clés, **explorateurs HPI ACI** (`data/hpi.json`) et127  **FRED** (`data/fred.json`), PDF complet et données sources dans `files/rapport/`.128- `/bibliographie` : bibliographie fusionnée du manuel et du rapport.129- `/recherche` : recherche plein texte côté client (`data/search.json`, 2 406 entrées).130131### 3.5 Transversal132- Thème clair UQO par défaut, thème sombre, taille de texte réglable, navigation clavier.133- PWA installable, `404.html` personnalisée, redirections propres (`/glossaire` → `/glossaire/`).134135## 4. Structure du cours publiée136137**Parties et séances**138139| Partie | Titre | Séances |140|---|---|---|141| I | Fondements de l’évaluation immobilière | 1 — Introduction à l'évaluation immobilière · 2 — Le cadre professionnel au Québec · 3 — Les principes fondamentaux de l'évaluation |142| II | Valeur, marché et données | 4 — La valeur : concepts et types · 5 — Le marché immobilier québécois · 6 — Collecte et analyse des données · 7 — Examen mi-session : révision des blocs 1 et 2 |143| III | Les trois méthodes d’évaluation | 8 — La méthode de comparaison — Partie 1 : principes et sélection des comparables · 9 — La méthode de comparaison — Partie 2 : ajustements et réconciliation · 10 — La méthode du coût · 11 — La méthode du revenu — Partie 1 : du revenu brut au RNE · 12 — La méthode du revenu — Partie 2 : le taux et le DCF |144| IV | Synthèse et pratique professionnelle | 13 — Le rapport d'évaluation · 14 — Révision générale et cas intégrateur |145146**Évaluations affichées sur le site** (la pondération et le calendrier viennent de `courses.py`) :147148| Évaluation | Portée / format | Pondération | Échéance |149|---|---|---|---|150| TP1 — Tableau de bord du marché de Gatineau avec UQO Éval | Séances 4 à 6 · données réelles d’UQO Éval · Excel + note de synthèse | 10 % | Séance 6 |151| Examen intra | Séances 1 à 6 · maison, fenêtre de 72 h | 30 % | Séance 7 |152| TP2 — Méthode de comparaison (53, rue du Tropique, Aylmer) | Grille de comparables chaînée (Excel) | 10 % | Séance 10 |153| TP3 — Méthode du revenu (triplex du 44, rue Jumonville, Hull) | Chaîne RBP → RNE → V (Excel) | 10 % | Séance 13 |154| Examen final | Toute la matière, accent séances 8 à 13 · maison, 72 h | 40 % | Période d’examens |155156**Travaux pratiques publiés** (`files/tp/`) :157158| TP | Pondération | Remise | Fichiers publiés | Description |159|---|---|---|---|---|160| TP1 — Tableau de bord du marché avec UQO Éval | 10 % | Séance 6 | `files/tp/IMM1003-20_TP1_enonce.pdf`, `files/tp/IMM1003-20_TP1_gabarit.xlsx` | Données réelles de www.uqo-eval.app : ventes réelles de Gatineau 2021-2026, rôle 2024-2026, dossier de dix propriétés à vendre (photos), collecte d’une propriété au choix et validation de données brutes ; gabarit Excel avec suivi de progression. |161| TP2 — Méthode de comparaison | 10 % | Séance 10 | `files/tp/IMM1003-20_TP2_enonce.pdf`, `files/tp/IMM1003-20_TP2_gabarit.xlsx` | Grille d’ajustements sur une inscription réelle du secteur Aylmer ; seuils net/brut, réconciliation motivée. |162| TP3 — Méthode du revenu | 10 % | Séance 13 | `files/tp/IMM1003-20_TP3_enonce.pdf`, `files/tp/IMM1003-20_TP3_gabarit.xlsx` | Triplex réel de Hull : reconstruction du RNE, extraction du TGA, capitalisation directe et sensibilité. |163164**Explorateurs interactifs** (widget → emplacement dans la séance) :165166| Widget | Inséré dans | Séance |167|---|---|---|168| `rpv` | `#s-1-4-3` | 1 — Introduction à l'évaluation immobilière |169| `mutation` | `#s-1-6-3` | 1 — Introduction à l'évaluation immobilière |170| `offre-demande` | `#s-3-1` | 3 — Les principes fondamentaux de l'évaluation |171| `cycle` | `#s-5-2` | 5 — Le marché immobilier québécois |172| `seuils` | `#s-9-4` | 9 — La méthode de comparaison — Partie 2 : ajustements et réconciliation |173| `age-vie` | `#s-10-3` | 10 — La méthode du coût |174| `cascade-revenus` | `#s-11-2` | 11 — La méthode du revenu — Partie 1 : du revenu brut au RNE |175| `sensibilite-tga` | `#s-12-1` | 12 — La méthode du revenu — Partie 2 : le taux et le DCF |176| `dcf` | `#s-12-3` | 12 — La méthode du revenu — Partie 2 : le taux et le DCF |177178**Séquences du jeu de remise en ordre** :179180- **Le processus d’évaluation en sept étapes** (7 étapes) → `/seance/01/#s-1-5-4`181- **L’ordre séquentiel des ajustements** (6 étapes) → `/seance/09/#s-9-2`182- **La cascade des revenus** (5 étapes) → `/seance/11/#s-11-2`183- **Le parcours vers le titre d’évaluateur agréé** (5 étapes) → `/seance/01/#s-1-2-2`184- **Le processus disciplinaire** (4 étapes) → `/seance/02/#s-2-3`185- **Les quatre critères de l’utilisation optimale** (4 étapes) → `/seance/03/#s-3-3`186187## 5. Contenu du dépôt188189```190uqo-imm1003/191├── README.md                 ← ce document192├── server.mjs                ← serveur statique Node ≥ 20, zéro dépendance (déployé tel quel)193├── package.json              ← {"start": "node server.mjs"} (aucune dépendance npm)194├── dist/                     ← LE SITE GÉNÉRÉ, identique octet pour octet à ce qui tourne sur le nœud195├── deploy/196│   └── uqo-imm1003.manifest.json   ← copie du manifeste mld (M1M32:~/dispatch/apps/uqo-imm1003.json) — sans secret197└── generator/                ← instantané du générateur au moment du build (lecture seule, voir ci-dessous)198    ├── README.md             ← README du générateur (_Site_web)199    ├── package.json          ← dépendance KaTeX pour le rendu des maths au build200    ├── build/                ← build.py, latex2html.py, rapport.py, tikz.py, render_math.mjs, bib.py,201    │                            brand.py, templates.py, courses.py, test_convert.py202    ├── data/                 ← quiz-*.json, outils-*.js, entrainement-*.js (les deux cours)203    └── assets/               ← css/site.css, js/app.js, brand/ (logos UQO sources)204```205206⚠️ **`generator/` est un instantané.** Le générateur canonique vit dans le dépôt de cours207`UQO_COURS/_Site_web/` (GitHub `spboucher-ai/UQO_COURS`, dossier partagé par les deux cours). Il est copié ici pour208que le dépôt de l'app soit auto-suffisant et lisible, mais **toute modification doit se faire dans `UQO_COURS`**, puis209être reconstruite et resynchronisée (§11). Les sources LaTeX (`.tex`) ne sont **pas** dans ce dépôt : elles210contiennent aussi les solutionnaires et examens, qui ne doivent jamais sortir de `UQO_COURS`.211212Ce qui est volontairement exclu : `cache/` du générateur (SVG TikZ et figures rendues, régénérables),213`node_modules/`, `.DS_Store`.214215## 6. Arborescence du site généré (`dist/`)216217| Chemin | Fichiers | Taille | Rôle |218|---|---|---|---|219| `aide-memoire/` | 1 | 173 Ko | Aide-mémoire (formules et règles par séance) |220| `assets/` | 182 | 21 Mo | CSS, JS, polices, SVG des figures (TikZ + rapport), logos, icônes PWA, image OG |221| `bibliographie/` | 1 | 49 Ko | Bibliographie fusionnée manuel + rapport |222| `carte/` | 1 | 43 Ko | Carte du cours (SVG radial) |223| `data/` | 9 | 2 Mo | JSON/JS consommés par le client (quiz, fiches, glossaire, recherche, outils, entraînement, HPI, FRED, meta) |224| `definitions/` | 1 | 108 Ko | Toutes les définitions |225| `diapositives/` | 1 | 30 Ko | Liste des 14 PDF de diapositives |226| `entrainement/` | 1 | 25 Ko | Problèmes à données aléatoires |227| `exercices/` | 1 | 581 Ko | Banque d'exercices |228| `fiches/` | 1 | 26 Ko | Fiches recto-verso |229| `files/` | 27 | 28 Mo | PDF (notes, plan, diapositives, TP, rapport), gabarits xlsx, données HPI, script Python |230| `glossaire/` | 1 | 51 Ko | Glossaire alphabétique |231| `jeux/` | 1 | 27 Ko | Association, vrai/faux, remise en ordre |232| `outils/` | 1 | 25 Ko | Calculateurs |233| `progression/` | 1 | 26 Ko | Tableau de bord + export/import JSON |234| `quiz/` | 1 | 25 Ko | Quiz par séance + examen blanc |235| `rapport/` | 22 | 880 Ko | Rapport marché de l'habitation : 21 sections + annexes A–F |236| `recherche/` | 1 | 25 Ko | Recherche plein texte |237| `ressources/` | 1 | 38 Ko | Téléchargements |238| `seance/` | 14 | 3 Mo | Les 14 séances (`seance/01` … `seance/14`) |239| `videos/` | 315 | 775 Mo | Séances vidéo : enregistrements Zoom (mp4/m4a hors git), sous-titres VTT, transcription synchronisée, chapitres, storyboard |240| `index.html` | 1 | — | Accueil : héros, parties, calendrier, évaluations, accès rapides |241| `404.html` | 1 | — | Page introuvable (servie avec le statut 404) |242| `manifest.webmanifest` | 1 | — | PWA (`name`, `short_name`, icônes, `theme_color #0f6180`) |243| `sitemap.xml`, `robots.txt` | 2 | — | Indexation (`Allow: /`) |244| `favicon.ico`, `favicon.svg` | 2 | — | Icônes (générées par `brand.py`) |245246`dist/data/` en détail : `meta.json` (statistiques + chapitres), `quiz.json` (109 questions en 14 chapitres),247`cards.json` (195 fiches), `glossaire.json`, `search.json` (index plein texte), `outils.js`,248`entrainement.js`, `hpi.json` (indice HPI ACI mensuel, désaisonnalisé et brut), `fred.json` (séries FRED).249250`dist/files/` en détail : `IMM1003_notes_de_cours.pdf`, `IMM1003_plan_de_cours.pdf`, `slides/` (14 PDF),251`tp/` (3 énoncés PDF + 3 gabarits `.xlsx`), `rapport/` (`rapport_immobilier_quebec.pdf`,252`HPI_ACI_mensuel_desaisonnalise.xlsx`, `HPI_ACI_mensuel_non_desaisonnalise.xlsx`, `analyse_hpi_quebec.py`).253254## 7. Le serveur `server.mjs`255256Serveur HTTP statique en **Node.js pur** (`node:http`, `node:fs`, `node:zlib`), aucune dépendance npm, ≈ 170 lignes.257258```259node server.mjs <port> <dossier-dist>260# en production : node server.mjs 8160 /Users/simon-pierreboucher/apps/uqo-imm1003/dist261```262263Comportement :264265| Aspect | Détail |266|---|---|267| Méthodes | `GET` et `HEAD` seulement ; autres → `405` + `Allow: GET, HEAD` |268| Santé | `GET /api/health` → JSON `{ok, started, root, uptime}` avec `Cache-Control: no-store` (utilisé par `mld` et par le monitoring) |269| Résolution | `/x/` → `x/index.html` ; `/x` sans extension → redirection `301` vers `/x/` si `x/index.html` existe, sinon `x.html` |270| Sécurité chemin | `safeJoin` : normalisation, refus de sortie de la racine (`400`) |271| Types MIME | html, css, js/mjs, json, svg, png, jpg, webp, ico, pdf, xlsx, woff/woff2/ttf, txt, xml, webmanifest |272| Compression | **Brotli** (qualité 5) ou **gzip** (niveau 6) à la volée pour html/css/js/json/svg/txt/xml/webmanifest > 1 Ko, selon `Accept-Encoding` ; `Vary: Accept-Encoding` |273| Cache | `ETag` faible (taille + mtime) + `304` ; `Cache-Control: no-cache` pour HTML et JSON ; `public, max-age=86400, stale-while-revalidate=604800` sous `/assets/` et `/files/` ; `public, max-age=3600` ailleurs |274| Plages | `Range: bytes=…` → `206` / `416` (lecture partielle des PDF dans le navigateur) |275| En-têtes | `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin`, `Accept-Ranges: bytes`, `Content-Security-Policy: frame-ancestors 'self'` sur le HTML |276| 404 | Sert `404.html` avec le statut 404 |277| Écoute | `0.0.0.0:<port>` (ngrok se connecte en local) ; journal de démarrage `[uqo-cours] port … · racine …` |278279Les SVG des figures matplotlib du rapport font ≈ 300 Ko chacun ; la compression Brotli/gzip les ramène à quelques280dizaines de Ko sur le fil.281282## 8. Chaîne de génération LaTeX → HTML283284Le pipeline (`generator/build/build.py`, ≈ 1 s par cours grâce aux caches) :2852861. **Lecture des métadonnées** (`courses.py`) : identité, parties et couleurs, calendrier, évaluations, TP à publier,287   widgets et séquences.2882. **Conversion LaTeX → HTML** (`latex2html.py`, ≈ 1 500 lignes) : dialecte `uqo-notes.sty` (boîtes, notes289   marginales, `\oe` avec avalement d'espace TeX, tableaux, listes, références croisées) et article du rapport.290   0 avertissement sur les 32 fichiers de séances + le rapport.2913. **TikZ → SVG** (`tikz.py`) : chaque `tikzpicture` est compilé en `standalone` avec `pdflatex`, converti par292   `pdftocairo`, mis en cache dans `cache/tikz/` (clé = hachage du source).2934. **Mathématiques** (`render_math.mjs`) : KaTeX exécuté en lot par Node au build → HTML statique, aucun JS294   mathématique côté client.2955. **Bibliographie** (`bib.py`) : `references.bib` → citations auteur-année + page `/bibliographie`.2966. **Rapport** (`rapport.py`) : découpage en 21 sections, tableaux `\input`, valeurs clés, figures PDF → SVG297   (`cache/rapport-fig/`), extraction HPI (xlsx → `hpi.json`) et FRED (json).2987. **Identité** (`brand.py`, Pillow + Fira Sans OTF de TeX Live) : logo UQO recoloré, favicons, `apple-touch-icon`,299   image Open Graph 1200 × 630 par cours.3008. **Gabarits** (`templates.py`) : accueil, séance, carte, rapport, jeux, entraînement, progression, recherche…3019. **Données** : `quiz-<code>.json`, `outils-<code>.js`, `entrainement-<code>.js` copiés dans `dist/data/`,302   index de recherche et fiches générés.30310. **Client** : `assets/css/site.css` (Fraunces / Source Serif 4 / Fira Sans, couleur par partie, mobile) et304    `assets/js/app.js` (recherche, quiz, fiches, widgets SVG, entraînement, jeux, progression, notes, TTS,305    explorateurs HPI/FRED).306307**Prérequis** : Python ≥ 3.12 (Pillow, openpyxl), Node ≥ 20 (`npm install` pour KaTeX), TeX Live (`pdflatex`,308FiraSans, fontawesome5, pgfplots), poppler (`pdftocairo`, `pdfinfo`).309310```bash311cd ~/Desktop/Academique/UQO/UQO_COURS/_Site_web312npm install                              # KaTeX, une fois313python3 build/build.py imm1003            # → dist/imm1003314node server/server.mjs 8160 dist/imm1003 # test local : http://localhost:8160315python3 build/test_convert.py            # test de conversion complet316```317318Pièges connus : Python 3.14 refuse `\"` dans les expressions de f-strings imbriquées (utiliser des variables319intermédiaires) ; un widget doit être inséré en fin de première sous-section, jamais dans une boîte ; `\oe` suivi320d'un espace doit avaler l'espace (sémantique TeX).321322## 9. Déploiement sur le cluster MacLustr (`mld`)323324L'app est orchestrée par **maclustr-dispatch (`mld`)** depuis la passerelle **M1M32** : elle choisit le nœud le plus325efficient, synchronise par rsync, démarre les processus PM2, ouvre le tunnel ngrok, vérifie la santé et met à jour326le registre (`M1M32:~/dispatch/registry.json`, copie laptop `~/Desktop/cluster-skill/cluster-registry.json`).327328Manifeste (`deploy/uqo-imm1003.manifest.json`, copie de `M1M32:~/dispatch/apps/uqo-imm1003.json`) :329330| Clé | Valeur |331|---|---|332| `domain` / `port` | `www.uqo-imm1003.app` / `8160` |333| `health_path` | `/api/health` |334| `requires.runtimes` | `ngrok`, `node`, `pm2` · `ram_gb: 1` · port `8160` libre |335| `processes[0]` | PM2 `uqo-imm1003` : `/opt/homebrew/bin/node server.mjs 8160 ~/apps/uqo-imm1003/dist`, `autorestart`, `max_memory_restart: 300M` (≈ 80 Mo observés) |336| `ngrok` | PM2 `uqo-imm1003-ngrok` → `https://www.uqo-imm1003.app` (CNAME chez le registraire → ngrok) |337| `placement` | libre (pas de `pin`), `source_node: laptop` |338| `sync_excludes` | `.DS_Store` |339340Procédure complète depuis le laptop :341342```bash343# 1. (re)construire le site344cd ~/Desktop/Academique/UQO/UQO_COURS/_Site_web && python3 build/build.py imm1003345346# 2. rafraîchir le dossier d'app (= ce dépôt) et versionner347rm -rf stage/uqo-imm1003/dist && cp -R dist/imm1003 stage/uqo-imm1003/dist348cd stage/uqo-imm1003 && git add -A && git commit -m "build $(date +%F)" && git push origin main349350# 3. laptop → passerelle M1M32 (staging), puis déploiement sur le nœud choisi par score351~/Desktop/cluster-skill/mld stage ~/Desktop/Academique/UQO/UQO_COURS/_Site_web/stage/uqo-imm1003 uqo-imm1003352~/Desktop/cluster-skill/mld deploy uqo-imm1003            # --node X pour imposer un nœud353354# 4. vérifier355~/Desktop/cluster-skill/mld status --live | grep uqo-imm1003356curl -s https://www.uqo-imm1003.app/api/health357```358359Autres commandes utiles : `mld move uqo-imm1003 --to <nœud>` (migration à chaud avec rollback), `mld plan uqo-imm1003`360(placement simulé), `mld apps` (manifestes). Le nœud n'a **pas** de dépôt git : c'est `mld` qui synchronise depuis le361staging de M1M32 ; ce dépôt spbgit est la référence versionnée de ce qui est déployé.362363## 10. Exploitation au quotidien364365```bash366NODE=$(~/Desktop/cluster-skill/mld status | awk '/uqo-imm1003 /{print $2}')   # ou lire mld status367ssh $NODE 'pm2 ls | grep uqo-imm1003; pm2 logs uqo-imm1003 --lines 50 --nostream'368ssh $NODE 'pm2 restart uqo-imm1003'                 # redémarrage du serveur Node369ssh $NODE 'pm2 restart uqo-imm1003-ngrok'           # tunnel370curl -sI https://www.uqo-imm1003.app/ | head -5           # 200 + en-têtes371curl -s  https://www.uqo-imm1003.app/api/health           # uptime372```373374- PM2 est configuré au démarrage (`pm2 startup`) sur tous les nœuds d'apps : l'app revient après un redémarrage.375- Le site est aussi surveillé par l'app MacLustr (macOS / iOS) via le registre `mld` (santé loopback + PM2).376- Emplacement disque sur le nœud : `~/apps/uqo-imm1003/` (`server.mjs`, `package.json`, `README.md`, `dist/`, ≈ 831 Mo).377- Si `mld status` montre `stale`/`offline` : `ssh <nœud> pm2 ls`, puis `mld deploy uqo-imm1003` (idempotent).378379## 11. Mettre à jour le contenu380381| Changement | Où | Puis |382|---|---|---|383| Texte, exemple, exercice, figure d'une séance | `.tex` dans `UQO_COURS/IMM1003-20_Elements_evaluation_immobiliere/IMM1003_Notes_de_cours/sources/` | build → §9 |384| Question de quiz | `_Site_web/data/quiz-imm1003.json` (`q`, `c` choix, `a` index, `e` explication, `url`) | build → §9 |385| Problème d'entraînement | `_Site_web/data/entrainement-imm1003.js` : objet `{id, title, ch, url, gen(R)}` | build → §9 |386| Calculateur | `_Site_web/data/outils-imm1003.js` | build → §9 |387| Widget dans une séance | `courses.py → widgets` : `("s-<ch>-<sec>", "<nom>")` + `Widgets.register("<nom>", …)` dans `assets/js/app.js` | build → §9 |388| Séquence (remise en ordre) | `courses.py → sequences` : `(titre, [étapes…], "/seance/NN/#ancre")` | build → §9 |389| Calendrier, évaluations, TP publiés | `courses.py → events / evaluation / tps` | build → §9 |390| Diapositives | PDF dans `02_Slides/pdf/seance*.pdf` du cours | build → §9 |391| Rapport marché | `Rapport_Immobilier_Quebec/` (LaTeX, figures PDF, xlsx HPI, json FRED) | build → §9 |392| Style / comportement client | `assets/css/site.css`, `assets/js/app.js` | build → §9 |393394Après un rebuild des notes, penser à **ré-ingérer** le site dans **UQO-Chat** (tuteur IA, dépôt `uqo-chat`) : il395indexe le HTML de `dist/<cours>/seance/NN` pour son RAG.396397## 12. Données personnelles, sécurité, performance398399- **Aucune donnée côté serveur** : pas de compte, pas de base, pas de formulaire, pas de cookie de suivi. La400  progression, les notes, les surlignages et les préférences sont dans le `localStorage` du navigateur de401  l'étudiant·e (export/import JSON depuis `/progression`).402- Polices servies localement ou via le CSS du site ; KaTeX pré-rendu (pas de CDN mathématique au chargement).403- En-têtes de sécurité posés par `server.mjs` (§7) ; ngrok termine le TLS (`https://www.uqo-imm1003.app`).404- Performance : HTML < 300 Ko par séance avant compression, SVG compressés à la volée, cache long sur `/assets/` et405  `/files/`, `ETag`/`304`. Un site complet pèse ≈ 831 Mo dont ≈ 18 Mo de PDF.406- Accessibilité : contrastes du thème UQO vérifiés, navigation clavier, `lang="fr-CA"`, lecture audio.407408## 13. Identité visuelle409410- Couleur principale UQO **teal `#0F6180`**, or `#cca424`, couleur par partie (`part_colors`), accent du cours411  `#2976bb`.412- Typographies : **Fraunces** (titres), **Source Serif 4** (texte), **Fira Sans** (interface).413- Logo : mot-symbole UQO (source Wikimedia + sources LaTeX, `generator/assets/brand/`) recoloré par `brand.py` ;414  favicons, `apple-touch-icon`, icônes PWA 192/512 et image OG 1200 × 630 générés au build.415- Le logo et le nom UQO appartiennent à l'Université du Québec en Outaouais ; usage dans le cadre du cours.416417## 14. Historique418419| Date | Étape |420|---|---|421| 2026-08-28 | Manuels LaTeX IMM1003 / IMM1033 (`uqo-notes.sty`, `latexmk`) — base du contenu |422| 2026-09-04 → 05 (nuit) | v1 : générateur `_Site_web` (latex2html, TikZ→SVG, KaTeX, quiz, outils), `server.mjs`, premier déploiement `mld` |423| 2026-09-05 (soir) | **v2 « refonte majeure »** : thème UQO, brand.py, 9 explorateurs par cours, entraînement, jeux, carte, progression, notes/audio, rapport marché intégré (HPI + FRED) ; build `2026-09-23 23:14:52`, déployé à 23:37 sur M4M64b |424| 2026-09-06 | UQO-Chat (tuteur IA) indexe ce site pour son RAG (714 passages pour les deux cours) |425| 2026-09-06 | Dépôt spbgit `uqo-imm1003.git` créé : artefact déployé + instantané du générateur + ce README |426427## 15. Droits et licence428429Contenu pédagogique © Simon-Pierre Boucher, UQO — usage réservé aux étudiant·es du cours ; toute reproduction430hors de ce cadre requiert une autorisation écrite. Code du générateur et du serveur : © Simon-Pierre Boucher, tous431droits réservés (dépôt privé). Données externes : ACI (HPI), FRED, sources citées dans la bibliographie du rapport.432433## 16. Contact434435**Simon-Pierre Boucher**436Courriel : **contact@spboucher.ai**437Git personnel : https://git.spboucher.ai (spbgit, dépôt `uqo-imm1003`)438Cours : https://www.uqo-imm1003.app · cours jumeau : https://www.uqo-imm1033.app · tuteur IA : https://www.uqo-chat.app439