# uqo-imm1003 — Éléments d’évaluation immobilière > **Notes de cours interactives du cours IMM1003 (IMM1003-20, Automne 2026) de l'Université du Québec en Outaouais (UQO).** > Site statique généré à partir des sources LaTeX du manuel, servi par le cluster MacLustr. | | | |---|---| | **Site public** | https://www.uqo-imm1003.app | | **Cours** | IMM1003 — Éléments d’évaluation immobilière · 3 crédits · premier cycle · 14 séances | | **Trimestre** | Automne 2026 | | **Cours jumeau** | IMM1033 — Méthodes du coût en évaluation immobilière · https://www.uqo-imm1033.app | | **App `mld`** | `uqo-imm1003` · port `8160` · nœud actuel : voir `mld status` (au 2026-09-06 : **M4M64b**) | | **Processus PM2** | `uqo-imm1003` (Node, `server.mjs`) · `uqo-imm1003-ngrok` (tunnel `www.uqo-imm1003.app`) | | **Santé** | `GET /api/health` → `{"ok":true,"started":…,"root":…,"uptime":…}` | | **Dépôt** | `gitsrv:~/srv/git/uqo-imm1003.git` (spbgit, git.spboucher.ai) | | **Auteur / contact** | **Simon-Pierre Boucher** — contact@spboucher.ai | | **Version publiée** | v2 « refonte majeure » — build du 2026-09-23 23:14:52, déployée le 2026-09-05 23:37 | --- ## Sommaire 1. [Présentation](#1-présentation) 2. [Ce que contient le site](#2-ce-que-contient-le-site) 3. [Fonctionnalités interactives](#3-fonctionnalités-interactives) 4. [Structure du cours publiée](#4-structure-du-cours-publiée) 5. [Contenu du dépôt](#5-contenu-du-dépôt) 6. [Arborescence du site généré (`dist/`)](#6-arborescence-du-site-généré-dist) 7. [Le serveur `server.mjs`](#7-le-serveur-servermjs) 8. [Chaîne de génération LaTeX → HTML](#8-chaîne-de-génération-latex--html) 9. [Déploiement sur le cluster MacLustr (`mld`)](#9-déploiement-sur-le-cluster-maclustr-mld) 10. [Exploitation au quotidien](#10-exploitation-au-quotidien) 11. [Mettre à jour le contenu](#11-mettre-à-jour-le-contenu) 12. [Données personnelles, sécurité, performance](#12-données-personnelles-sécurité-performance) 13. [Identité visuelle](#13-identité-visuelle) 14. [Historique](#14-historique) 15. [Droits et licence](#15-droits-et-licence) 16. [Contact](#16-contact) --- ## 1. Présentation `uqo-imm1003` est la version web, interactive et « mobile-first » des notes de cours de **IMM1003 — Éléments d’évaluation immobilière** (IMM1003-20, Automne 2026, 3 crédits · premier cycle · 14 séances), enseigné à l'UQO par Simon-Pierre Boucher. Principes, 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. Le site n'est **pas** une application avec base de données : c'est un **site statique** (HTML, CSS, JavaScript, JSON, SVG, PDF) produit par un générateur maison à partir des **sources LaTeX** du manuel (`uqo-notes.sty`) et du rapport *Le marché de l'habitation au Québec*. Toute l'interactivité (quiz, entraînement, jeux, progression, notes, recherche, synthèse vocale, explorateurs) s'exécute **dans le navigateur** ; la progression est conservée localement (`localStorage`) et exportable en JSON. Aucun compte, aucune collecte côté serveur. Le rendu est optimisé pour le téléphone (375 px) autant que pour l'ordinateur, installable comme PWA (`manifest.webmanifest`, icônes 192/512), et indexable (`sitemap.xml`, `robots.txt`, balises Open Graph). **Source de vérité du contenu = les fichiers `.tex`** du dépôt de cours `UQO_COURS`. On ne modifie jamais le HTML généré à la main : on modifie le LaTeX, on reconstruit, on redéploie (voir §11). ## 2. Ce que contient le site Statistiques du build publié (`dist/data/meta.json`, build `2026-09-23 23:14:52`) : | Indicateur | Valeur | |---|---| | Séances (chapitres) | 14 | | Mots de notes | 110 717 | | Définitions | 87 | | Formules encadrées | 27 | | Exemples résolus | 85 | | Exercices | 82 | | Figures (TikZ → SVG, PDF → SVG) | 61 | | Questions de quiz (14 chapitres) | 109 | | Entrées de glossaire | 81 | | Fiches recto-verso | 195 | | Explorateurs interactifs insérés dans les séances | 9 | | Générateurs de problèmes d'entraînement | 13 | | Séquences (jeu de remise en ordre) | 6 | | Entrées de l'index de recherche plein texte | 2 406 | | Diapositives (PDF, une par séance) | 14 | | Travaux pratiques publiés (énoncé PDF + gabarit Excel) | 3 | | Rapport marché de l'habitation | 21 sections · 67 figures SVG · 28 tableaux | | Fichiers dans `dist/` | 591 (≈ 831 Mo) | **Publié** : notes intégrales des 14 séances, annexes, bibliographie, plan de cours (PDF), notes complètes (PDF), diapositives (PDF), énoncés des TP et gabarits Excel, rapport marché (HTML + PDF + données HPI ACI xlsx + script Python d'analyse). **Jamais publié** (règle absolue du projet) : solutionnaires des TP, questionnaires d'examen, grilles de correction. Le générateur ne les copie pas ; ne pas les ajouter à la main dans `dist/`. ## 3. Fonctionnalités interactives ### 3.1 Séances (`/seance/01` … `/seance/14`) - Notes intégrales converties depuis LaTeX : boîtes pédagogiques (définition, formule, exemple, exercice, attention, à retenir), **notes marginales** façon Tufte, mathématiques **KaTeX** pré-rendues au build, tableaux, figures **TikZ** rendues en SVG, références auteur-année (`references.bib`). - **Stepper de sections** (précédent / suivant), sommaire latéral avec sections déjà lues, **objectifs cochables**, **mode révision** (masque les exemples, met en avant définitions et formules). - **Explorateurs SVG** insérés à la fin de la première sous-section ciblée (9 par cours, voir §4) : curseurs et graphiques recalculés en direct. - **Surlignage + notes personnelles** (sélection de texte → surligner / annoter ; export Markdown), **lecture audio** (synthèse vocale du navigateur, section par section), **quiz de fin de séance** intégré. - Infobulles de glossaire sur les termes définis. ### 3.2 Réviser - `/aide-memoire` : toutes les formules et règles clés par séance. - `/glossaire` et `/definitions` : glossaire alphabétique, définitions intégrales avec lien vers la séance. - `/fiches` : fiches recto-verso (question / réponse) mélangeables, filtrables par séance. - `/carte` : **carte du cours** (SVG radial : parties → séances → objectifs). - `/exercices` : banque de tous les exercices avec lien vers le contexte. ### 3.3 S'exercer - `/quiz` : quiz par séance ou **examen blanc** (tirage aléatoire multi-séances, correction commentée, score). - `/entrainement` : **problèmes à données aléatoires** (13 générateurs, `data/entrainement.js`) avec solution détaillée recalculée à chaque tirage. - `/jeux` : association (terme ↔ définition), vrai / faux, **remise en ordre** (6 séquences du cours). - `/outils` : calculateurs spécialisés (`data/outils.js`). - `/progression` : tableau de bord (séances lues, objectifs cochés, quiz réussis, fiches maîtrisées) ; **export / import JSON** pour changer d'appareil. ### 3.4 Ressources - `/diapositives` : 14 PDF (`files/slides/`), `/ressources` : téléchargements (plan, notes PDF, TP, gabarits). - `/rapport` : rapport *Le marché de l'habitation au Québec* en 21 sections HTML (`rapport/0` … `rapport/15`, annexes `A` … `F`), 67 figures SVG, 28 tableaux, valeurs clés, **explorateurs HPI ACI** (`data/hpi.json`) et **FRED** (`data/fred.json`), PDF complet et données sources dans `files/rapport/`. - `/bibliographie` : bibliographie fusionnée du manuel et du rapport. - `/recherche` : recherche plein texte côté client (`data/search.json`, 2 406 entrées). ### 3.5 Transversal - Thème clair UQO par défaut, thème sombre, taille de texte réglable, navigation clavier. - PWA installable, `404.html` personnalisée, redirections propres (`/glossaire` → `/glossaire/`). ## 4. Structure du cours publiée **Parties et séances** | Partie | Titre | Séances | |---|---|---| | 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 | | 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 | | 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 | | IV | Synthèse et pratique professionnelle | 13 — Le rapport d'évaluation · 14 — Révision générale et cas intégrateur | **Évaluations affichées sur le site** (la pondération et le calendrier viennent de `courses.py`) : | Évaluation | Portée / format | Pondération | Échéance | |---|---|---|---| | 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 | | Examen intra | Séances 1 à 6 · maison, fenêtre de 72 h | 30 % | Séance 7 | | TP2 — Méthode de comparaison (53, rue du Tropique, Aylmer) | Grille de comparables chaînée (Excel) | 10 % | Séance 10 | | TP3 — Méthode du revenu (triplex du 44, rue Jumonville, Hull) | Chaîne RBP → RNE → V (Excel) | 10 % | Séance 13 | | Examen final | Toute la matière, accent séances 8 à 13 · maison, 72 h | 40 % | Période d’examens | **Travaux pratiques publiés** (`files/tp/`) : | TP | Pondération | Remise | Fichiers publiés | Description | |---|---|---|---|---| | 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. | | 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. | | 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é. | **Explorateurs interactifs** (widget → emplacement dans la séance) : | Widget | Inséré dans | Séance | |---|---|---| | `rpv` | `#s-1-4-3` | 1 — Introduction à l'évaluation immobilière | | `mutation` | `#s-1-6-3` | 1 — Introduction à l'évaluation immobilière | | `offre-demande` | `#s-3-1` | 3 — Les principes fondamentaux de l'évaluation | | `cycle` | `#s-5-2` | 5 — Le marché immobilier québécois | | `seuils` | `#s-9-4` | 9 — La méthode de comparaison — Partie 2 : ajustements et réconciliation | | `age-vie` | `#s-10-3` | 10 — La méthode du coût | | `cascade-revenus` | `#s-11-2` | 11 — La méthode du revenu — Partie 1 : du revenu brut au RNE | | `sensibilite-tga` | `#s-12-1` | 12 — La méthode du revenu — Partie 2 : le taux et le DCF | | `dcf` | `#s-12-3` | 12 — La méthode du revenu — Partie 2 : le taux et le DCF | **Séquences du jeu de remise en ordre** : - **Le processus d’évaluation en sept étapes** (7 étapes) → `/seance/01/#s-1-5-4` - **L’ordre séquentiel des ajustements** (6 étapes) → `/seance/09/#s-9-2` - **La cascade des revenus** (5 étapes) → `/seance/11/#s-11-2` - **Le parcours vers le titre d’évaluateur agréé** (5 étapes) → `/seance/01/#s-1-2-2` - **Le processus disciplinaire** (4 étapes) → `/seance/02/#s-2-3` - **Les quatre critères de l’utilisation optimale** (4 étapes) → `/seance/03/#s-3-3` ## 5. Contenu du dépôt ``` uqo-imm1003/ ├── README.md ← ce document ├── server.mjs ← serveur statique Node ≥ 20, zéro dépendance (déployé tel quel) ├── package.json ← {"start": "node server.mjs"} (aucune dépendance npm) ├── dist/ ← LE SITE GÉNÉRÉ, identique octet pour octet à ce qui tourne sur le nœud ├── deploy/ │ └── uqo-imm1003.manifest.json ← copie du manifeste mld (M1M32:~/dispatch/apps/uqo-imm1003.json) — sans secret └── generator/ ← instantané du générateur au moment du build (lecture seule, voir ci-dessous) ├── README.md ← README du générateur (_Site_web) ├── package.json ← dépendance KaTeX pour le rendu des maths au build ├── build/ ← build.py, latex2html.py, rapport.py, tikz.py, render_math.mjs, bib.py, │ brand.py, templates.py, courses.py, test_convert.py ├── data/ ← quiz-*.json, outils-*.js, entrainement-*.js (les deux cours) └── assets/ ← css/site.css, js/app.js, brand/ (logos UQO sources) ``` ⚠️ **`generator/` est un instantané.** Le générateur canonique vit dans le dépôt de cours `UQO_COURS/_Site_web/` (GitHub `spboucher-ai/UQO_COURS`, dossier partagé par les deux cours). Il est copié ici pour que le dépôt de l'app soit auto-suffisant et lisible, mais **toute modification doit se faire dans `UQO_COURS`**, puis être reconstruite et resynchronisée (§11). Les sources LaTeX (`.tex`) ne sont **pas** dans ce dépôt : elles contiennent aussi les solutionnaires et examens, qui ne doivent jamais sortir de `UQO_COURS`. Ce qui est volontairement exclu : `cache/` du générateur (SVG TikZ et figures rendues, régénérables), `node_modules/`, `.DS_Store`. ## 6. Arborescence du site généré (`dist/`) | Chemin | Fichiers | Taille | Rôle | |---|---|---|---| | `aide-memoire/` | 1 | 173 Ko | Aide-mémoire (formules et règles par séance) | | `assets/` | 182 | 21 Mo | CSS, JS, polices, SVG des figures (TikZ + rapport), logos, icônes PWA, image OG | | `bibliographie/` | 1 | 49 Ko | Bibliographie fusionnée manuel + rapport | | `carte/` | 1 | 43 Ko | Carte du cours (SVG radial) | | `data/` | 9 | 2 Mo | JSON/JS consommés par le client (quiz, fiches, glossaire, recherche, outils, entraînement, HPI, FRED, meta) | | `definitions/` | 1 | 108 Ko | Toutes les définitions | | `diapositives/` | 1 | 30 Ko | Liste des 14 PDF de diapositives | | `entrainement/` | 1 | 25 Ko | Problèmes à données aléatoires | | `exercices/` | 1 | 581 Ko | Banque d'exercices | | `fiches/` | 1 | 26 Ko | Fiches recto-verso | | `files/` | 27 | 28 Mo | PDF (notes, plan, diapositives, TP, rapport), gabarits xlsx, données HPI, script Python | | `glossaire/` | 1 | 51 Ko | Glossaire alphabétique | | `jeux/` | 1 | 27 Ko | Association, vrai/faux, remise en ordre | | `outils/` | 1 | 25 Ko | Calculateurs | | `progression/` | 1 | 26 Ko | Tableau de bord + export/import JSON | | `quiz/` | 1 | 25 Ko | Quiz par séance + examen blanc | | `rapport/` | 22 | 880 Ko | Rapport marché de l'habitation : 21 sections + annexes A–F | | `recherche/` | 1 | 25 Ko | Recherche plein texte | | `ressources/` | 1 | 38 Ko | Téléchargements | | `seance/` | 14 | 3 Mo | Les 14 séances (`seance/01` … `seance/14`) | | `videos/` | 315 | 775 Mo | Séances vidéo : enregistrements Zoom (mp4/m4a hors git), sous-titres VTT, transcription synchronisée, chapitres, storyboard | | `index.html` | 1 | — | Accueil : héros, parties, calendrier, évaluations, accès rapides | | `404.html` | 1 | — | Page introuvable (servie avec le statut 404) | | `manifest.webmanifest` | 1 | — | PWA (`name`, `short_name`, icônes, `theme_color #0f6180`) | | `sitemap.xml`, `robots.txt` | 2 | — | Indexation (`Allow: /`) | | `favicon.ico`, `favicon.svg` | 2 | — | Icônes (générées par `brand.py`) | `dist/data/` en détail : `meta.json` (statistiques + chapitres), `quiz.json` (109 questions en 14 chapitres), `cards.json` (195 fiches), `glossaire.json`, `search.json` (index plein texte), `outils.js`, `entrainement.js`, `hpi.json` (indice HPI ACI mensuel, désaisonnalisé et brut), `fred.json` (séries FRED). `dist/files/` en détail : `IMM1003_notes_de_cours.pdf`, `IMM1003_plan_de_cours.pdf`, `slides/` (14 PDF), `tp/` (3 énoncés PDF + 3 gabarits `.xlsx`), `rapport/` (`rapport_immobilier_quebec.pdf`, `HPI_ACI_mensuel_desaisonnalise.xlsx`, `HPI_ACI_mensuel_non_desaisonnalise.xlsx`, `analyse_hpi_quebec.py`). ## 7. Le serveur `server.mjs` Serveur HTTP statique en **Node.js pur** (`node:http`, `node:fs`, `node:zlib`), aucune dépendance npm, ≈ 170 lignes. ``` node server.mjs # en production : node server.mjs 8160 /Users/simon-pierreboucher/apps/uqo-imm1003/dist ``` Comportement : | Aspect | Détail | |---|---| | Méthodes | `GET` et `HEAD` seulement ; autres → `405` + `Allow: GET, HEAD` | | Santé | `GET /api/health` → JSON `{ok, started, root, uptime}` avec `Cache-Control: no-store` (utilisé par `mld` et par le monitoring) | | Résolution | `/x/` → `x/index.html` ; `/x` sans extension → redirection `301` vers `/x/` si `x/index.html` existe, sinon `x.html` | | Sécurité chemin | `safeJoin` : normalisation, refus de sortie de la racine (`400`) | | Types MIME | html, css, js/mjs, json, svg, png, jpg, webp, ico, pdf, xlsx, woff/woff2/ttf, txt, xml, webmanifest | | 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` | | 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 | | Plages | `Range: bytes=…` → `206` / `416` (lecture partielle des PDF dans le navigateur) | | 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 | | 404 | Sert `404.html` avec le statut 404 | | Écoute | `0.0.0.0:` (ngrok se connecte en local) ; journal de démarrage `[uqo-cours] port … · racine …` | Les SVG des figures matplotlib du rapport font ≈ 300 Ko chacun ; la compression Brotli/gzip les ramène à quelques dizaines de Ko sur le fil. ## 8. Chaîne de génération LaTeX → HTML Le pipeline (`generator/build/build.py`, ≈ 1 s par cours grâce aux caches) : 1. **Lecture des métadonnées** (`courses.py`) : identité, parties et couleurs, calendrier, évaluations, TP à publier, widgets et séquences. 2. **Conversion LaTeX → HTML** (`latex2html.py`, ≈ 1 500 lignes) : dialecte `uqo-notes.sty` (boîtes, notes marginales, `\oe` avec avalement d'espace TeX, tableaux, listes, références croisées) et article du rapport. 0 avertissement sur les 32 fichiers de séances + le rapport. 3. **TikZ → SVG** (`tikz.py`) : chaque `tikzpicture` est compilé en `standalone` avec `pdflatex`, converti par `pdftocairo`, mis en cache dans `cache/tikz/` (clé = hachage du source). 4. **Mathématiques** (`render_math.mjs`) : KaTeX exécuté en lot par Node au build → HTML statique, aucun JS mathématique côté client. 5. **Bibliographie** (`bib.py`) : `references.bib` → citations auteur-année + page `/bibliographie`. 6. **Rapport** (`rapport.py`) : découpage en 21 sections, tableaux `\input`, valeurs clés, figures PDF → SVG (`cache/rapport-fig/`), extraction HPI (xlsx → `hpi.json`) et FRED (json). 7. **Identité** (`brand.py`, Pillow + Fira Sans OTF de TeX Live) : logo UQO recoloré, favicons, `apple-touch-icon`, image Open Graph 1200 × 630 par cours. 8. **Gabarits** (`templates.py`) : accueil, séance, carte, rapport, jeux, entraînement, progression, recherche… 9. **Données** : `quiz-.json`, `outils-.js`, `entrainement-.js` copiés dans `dist/data/`, index de recherche et fiches générés. 10. **Client** : `assets/css/site.css` (Fraunces / Source Serif 4 / Fira Sans, couleur par partie, mobile) et `assets/js/app.js` (recherche, quiz, fiches, widgets SVG, entraînement, jeux, progression, notes, TTS, explorateurs HPI/FRED). **Prérequis** : Python ≥ 3.12 (Pillow, openpyxl), Node ≥ 20 (`npm install` pour KaTeX), TeX Live (`pdflatex`, FiraSans, fontawesome5, pgfplots), poppler (`pdftocairo`, `pdfinfo`). ```bash cd ~/Desktop/Academique/UQO/UQO_COURS/_Site_web npm install # KaTeX, une fois python3 build/build.py imm1003 # → dist/imm1003 node server/server.mjs 8160 dist/imm1003 # test local : http://localhost:8160 python3 build/test_convert.py # test de conversion complet ``` Pièges connus : Python 3.14 refuse `\"` dans les expressions de f-strings imbriquées (utiliser des variables intermédiaires) ; un widget doit être inséré en fin de première sous-section, jamais dans une boîte ; `\oe` suivi d'un espace doit avaler l'espace (sémantique TeX). ## 9. Déploiement sur le cluster MacLustr (`mld`) L'app est orchestrée par **maclustr-dispatch (`mld`)** depuis la passerelle **M1M32** : elle choisit le nœud le plus efficient, synchronise par rsync, démarre les processus PM2, ouvre le tunnel ngrok, vérifie la santé et met à jour le registre (`M1M32:~/dispatch/registry.json`, copie laptop `~/Desktop/cluster-skill/cluster-registry.json`). Manifeste (`deploy/uqo-imm1003.manifest.json`, copie de `M1M32:~/dispatch/apps/uqo-imm1003.json`) : | Clé | Valeur | |---|---| | `domain` / `port` | `www.uqo-imm1003.app` / `8160` | | `health_path` | `/api/health` | | `requires.runtimes` | `ngrok`, `node`, `pm2` · `ram_gb: 1` · port `8160` libre | | `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) | | `ngrok` | PM2 `uqo-imm1003-ngrok` → `https://www.uqo-imm1003.app` (CNAME chez le registraire → ngrok) | | `placement` | libre (pas de `pin`), `source_node: laptop` | | `sync_excludes` | `.DS_Store` | Procédure complète depuis le laptop : ```bash # 1. (re)construire le site cd ~/Desktop/Academique/UQO/UQO_COURS/_Site_web && python3 build/build.py imm1003 # 2. rafraîchir le dossier d'app (= ce dépôt) et versionner rm -rf stage/uqo-imm1003/dist && cp -R dist/imm1003 stage/uqo-imm1003/dist cd stage/uqo-imm1003 && git add -A && git commit -m "build $(date +%F)" && git push origin main # 3. laptop → passerelle M1M32 (staging), puis déploiement sur le nœud choisi par score ~/Desktop/cluster-skill/mld stage ~/Desktop/Academique/UQO/UQO_COURS/_Site_web/stage/uqo-imm1003 uqo-imm1003 ~/Desktop/cluster-skill/mld deploy uqo-imm1003 # --node X pour imposer un nœud # 4. vérifier ~/Desktop/cluster-skill/mld status --live | grep uqo-imm1003 curl -s https://www.uqo-imm1003.app/api/health ``` Autres commandes utiles : `mld move uqo-imm1003 --to ` (migration à chaud avec rollback), `mld plan uqo-imm1003` (placement simulé), `mld apps` (manifestes). Le nœud n'a **pas** de dépôt git : c'est `mld` qui synchronise depuis le staging de M1M32 ; ce dépôt spbgit est la référence versionnée de ce qui est déployé. ## 10. Exploitation au quotidien ```bash NODE=$(~/Desktop/cluster-skill/mld status | awk '/uqo-imm1003 /{print $2}') # ou lire mld status ssh $NODE 'pm2 ls | grep uqo-imm1003; pm2 logs uqo-imm1003 --lines 50 --nostream' ssh $NODE 'pm2 restart uqo-imm1003' # redémarrage du serveur Node ssh $NODE 'pm2 restart uqo-imm1003-ngrok' # tunnel curl -sI https://www.uqo-imm1003.app/ | head -5 # 200 + en-têtes curl -s https://www.uqo-imm1003.app/api/health # uptime ``` - PM2 est configuré au démarrage (`pm2 startup`) sur tous les nœuds d'apps : l'app revient après un redémarrage. - Le site est aussi surveillé par l'app MacLustr (macOS / iOS) via le registre `mld` (santé loopback + PM2). - Emplacement disque sur le nœud : `~/apps/uqo-imm1003/` (`server.mjs`, `package.json`, `README.md`, `dist/`, ≈ 831 Mo). - Si `mld status` montre `stale`/`offline` : `ssh pm2 ls`, puis `mld deploy uqo-imm1003` (idempotent). ## 11. Mettre à jour le contenu | Changement | Où | Puis | |---|---|---| | Texte, exemple, exercice, figure d'une séance | `.tex` dans `UQO_COURS/IMM1003-20_Elements_evaluation_immobiliere/IMM1003_Notes_de_cours/sources/` | build → §9 | | Question de quiz | `_Site_web/data/quiz-imm1003.json` (`q`, `c` choix, `a` index, `e` explication, `url`) | build → §9 | | Problème d'entraînement | `_Site_web/data/entrainement-imm1003.js` : objet `{id, title, ch, url, gen(R)}` | build → §9 | | Calculateur | `_Site_web/data/outils-imm1003.js` | build → §9 | | Widget dans une séance | `courses.py → widgets` : `("s--", "")` + `Widgets.register("", …)` dans `assets/js/app.js` | build → §9 | | Séquence (remise en ordre) | `courses.py → sequences` : `(titre, [étapes…], "/seance/NN/#ancre")` | build → §9 | | Calendrier, évaluations, TP publiés | `courses.py → events / evaluation / tps` | build → §9 | | Diapositives | PDF dans `02_Slides/pdf/seance*.pdf` du cours | build → §9 | | Rapport marché | `Rapport_Immobilier_Quebec/` (LaTeX, figures PDF, xlsx HPI, json FRED) | build → §9 | | Style / comportement client | `assets/css/site.css`, `assets/js/app.js` | build → §9 | Après un rebuild des notes, penser à **ré-ingérer** le site dans **UQO-Chat** (tuteur IA, dépôt `uqo-chat`) : il indexe le HTML de `dist//seance/NN` pour son RAG. ## 12. Données personnelles, sécurité, performance - **Aucune donnée côté serveur** : pas de compte, pas de base, pas de formulaire, pas de cookie de suivi. La progression, les notes, les surlignages et les préférences sont dans le `localStorage` du navigateur de l'étudiant·e (export/import JSON depuis `/progression`). - Polices servies localement ou via le CSS du site ; KaTeX pré-rendu (pas de CDN mathématique au chargement). - En-têtes de sécurité posés par `server.mjs` (§7) ; ngrok termine le TLS (`https://www.uqo-imm1003.app`). - Performance : HTML < 300 Ko par séance avant compression, SVG compressés à la volée, cache long sur `/assets/` et `/files/`, `ETag`/`304`. Un site complet pèse ≈ 831 Mo dont ≈ 18 Mo de PDF. - Accessibilité : contrastes du thème UQO vérifiés, navigation clavier, `lang="fr-CA"`, lecture audio. ## 13. Identité visuelle - Couleur principale UQO **teal `#0F6180`**, or `#cca424`, couleur par partie (`part_colors`), accent du cours `#2976bb`. - Typographies : **Fraunces** (titres), **Source Serif 4** (texte), **Fira Sans** (interface). - Logo : mot-symbole UQO (source Wikimedia + sources LaTeX, `generator/assets/brand/`) recoloré par `brand.py` ; favicons, `apple-touch-icon`, icônes PWA 192/512 et image OG 1200 × 630 générés au build. - Le logo et le nom UQO appartiennent à l'Université du Québec en Outaouais ; usage dans le cadre du cours. ## 14. Historique | Date | Étape | |---|---| | 2026-08-28 | Manuels LaTeX IMM1003 / IMM1033 (`uqo-notes.sty`, `latexmk`) — base du contenu | | 2026-09-04 → 05 (nuit) | v1 : générateur `_Site_web` (latex2html, TikZ→SVG, KaTeX, quiz, outils), `server.mjs`, premier déploiement `mld` | | 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 | | 2026-09-06 | UQO-Chat (tuteur IA) indexe ce site pour son RAG (714 passages pour les deux cours) | | 2026-09-06 | Dépôt spbgit `uqo-imm1003.git` créé : artefact déployé + instantané du générateur + ce README | ## 15. Droits et licence Contenu pédagogique © Simon-Pierre Boucher, UQO — usage réservé aux étudiant·es du cours ; toute reproduction hors de ce cadre requiert une autorisation écrite. Code du générateur et du serveur : © Simon-Pierre Boucher, tous droits réservés (dépôt privé). Données externes : ACI (HPI), FRED, sources citées dans la bibliographie du rapport. ## 16. Contact **Simon-Pierre Boucher** Courriel : **contact@spboucher.ai** Git personnel : https://git.spboucher.ai (spbgit, dépôt `uqo-imm1003`) Cours : https://www.uqo-imm1003.app · cours jumeau : https://www.uqo-imm1033.app · tuteur IA : https://www.uqo-chat.app