v1.9.1 : fix nuage calibré (il entoure la courbe ESE calibrée) + doc +4 pg/mL
- EstrannaiseCloud.compute : paramètre scalePerEster — le nuage reçoit la MÊME calibration que la courbe ESE (autoByModel ESE). Avant : courbe ESE calibrée mais nuage BRUT → les deux flottaient à des échelles différentes (remontée : « le nuage ne s'active que autour du tracé, pas autour du modèle Estrannaise »). ChartScreen passe autoM?.esterScales. - +1 test : nuage calibré ×2 = nuage brut ×2 (le nuage suit la calibration). - Validé émulateur §16.ter (release, seed réel + traitement EV ESE actif de test) : nuage rendu autour de la courbe ESE calibrée, 0 crash. - CHANGELOG 1.9.1 : explication documentée du +4 pg/mL constaté depuis v1.8.2 (l'arrondi de la médiane de calibration — plus juste que la troncature — a déplacé une échelle auto-calibrée d'un cran ; le niveau actuel est le CORRIGÉ). §2 session, §7.12 nuage calibré, README. - DIAG du test médiane nettoyé (l'épinglage de l'arrondi reste, en assertion propre ±0,005). - versionCode 39 / versionName 1.9.1. Web sync v1.9.1.
This commit is contained in:
parent
8078d5cbed
commit
77ca672078
@ -16,7 +16,7 @@ sauvegarde JSON. **100 % local, aucun compte, aucun serveur.**
|
|||||||
> à titre informatif — ce ne sont pas des mesures. Fie-toi toujours à tes analyses de
|
> à titre informatif — ce ne sont pas des mesures. Fie-toi toujours à tes analyses de
|
||||||
> sang et aux consignes de ton endocrinologue.
|
> sang et aux consignes de ton endocrinologue.
|
||||||
|
|
||||||
- **Statut** : v1.9.0 — build Android ✅, **lint vert** ✅, **234 tests unitaires** ✅ (204 sans les données de test locales ; régressions épinglées sur données réelles **non versionnées**), intégration montre = notifications ✅, **dépôt Gitea privé + releases avec APK** ✅
|
- **Statut** : v1.9.1 — build Android ✅, **lint vert** ✅, **235 tests unitaires** ✅ (205 sans les données de test locales ; régressions épinglées sur données réelles **non versionnées**), intégration montre = notifications ✅, **dépôt Gitea privé + releases avec APK** ✅
|
||||||
- **Journal des versions** : [docs/CHANGELOG.md](docs/CHANGELOG.md)
|
- **Journal des versions** : [docs/CHANGELOG.md](docs/CHANGELOG.md)
|
||||||
- **Guide utilisateur** : [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md)
|
- **Guide utilisateur** : [docs/GUIDE_INSTALLATION.md](docs/GUIDE_INSTALLATION.md)
|
||||||
- **Doc de développement** (architecture, maths, décisions, bugs) : [docs/DEVELOPPEMENT.md](docs/DEVELOPPEMENT.md)
|
- **Doc de développement** (architecture, maths, décisions, bugs) : [docs/DEVELOPPEMENT.md](docs/DEVELOPPEMENT.md)
|
||||||
@ -123,7 +123,7 @@ par AGP si les licences sont signées). Le wrapper télécharge Gradle 9.7.1.
|
|||||||
git clone <repo> && cd HormoneTrack
|
git clone <repo> && cd HormoneTrack
|
||||||
echo "sdk.dir=/chemin/vers/android-sdk" > local.properties # ou ANDROID_HOME
|
echo "sdk.dir=/chemin/vers/android-sdk" > local.properties # ou ANDROID_HOME
|
||||||
./gradlew assembleDebug # APK : app/build/outputs/apk/debug/app-debug.apk
|
./gradlew assembleDebug # APK : app/build/outputs/apk/debug/app-debug.apk
|
||||||
./gradlew testDebugUnitTest # 234 tests (204 sans les données locales)
|
./gradlew testDebugUnitTest # 235 tests (205 sans les données locales)
|
||||||
./gradlew lint # lint vert obligatoire avant release
|
./gradlew lint # lint vert obligatoire avant release
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -17,8 +17,8 @@ android {
|
|||||||
// tag : v1.3.0→v1.3.2 contenaient tous versionCode 14 / "1.3.0"
|
// tag : v1.3.0→v1.3.2 contenaient tous versionCode 14 / "1.3.0"
|
||||||
// (bump jamais commité) → BuildConfig.VERSION_NAME était faux dans
|
// (bump jamais commité) → BuildConfig.VERSION_NAME était faux dans
|
||||||
// les APK publiés (Paramètres + titre du dialog « Nouveautés »).
|
// les APK publiés (Paramètres + titre du dialog « Nouveautés »).
|
||||||
versionCode = 38
|
versionCode = 39
|
||||||
versionName = "1.9.0"
|
versionName = "1.9.1"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|||||||
@ -64,7 +64,17 @@ object EstrannaiseCloud {
|
|||||||
endMs: Long,
|
endMs: Long,
|
||||||
stepMs: Long,
|
stepMs: Long,
|
||||||
nowMs: Long,
|
nowMs: Long,
|
||||||
nbCurves: Int = DEFAULT_NB_CURVES
|
nbCurves: Int = DEFAULT_NB_CURVES,
|
||||||
|
/**
|
||||||
|
* Calibration à appliquer au nuage (v1.9.1 — fix « le nuage ne
|
||||||
|
* s'active que autour du tracé émulé, pas autour du modèle
|
||||||
|
* Estrannaise ») : la courbe ESE affichée est CALIBRÉE
|
||||||
|
* (scalePerEster des labs) mais le nuage était tracé BRUT → les deux
|
||||||
|
* flottaient à des échelles différentes. Passer le MÊME
|
||||||
|
* `scalePerEster` que la courbe ESE (autoByModel["ESE"]?.esterScales
|
||||||
|
* côté ChartScreen) pour que le nuage ENTOURE la courbe.
|
||||||
|
*/
|
||||||
|
scalePerEster: Map<String, Double>? = null
|
||||||
): List<List<LevelPoint>> {
|
): List<List<LevelPoint>> {
|
||||||
if (startMs >= endMs || stepMs <= 0 || nbCurves < 2) return emptyList()
|
if (startMs >= endMs || stepMs <= 0 || nbCurves < 2) return emptyList()
|
||||||
|
|
||||||
@ -115,7 +125,12 @@ object EstrannaiseCloud {
|
|||||||
val dtH = (t - dose.timestamp) / 3_600_000.0
|
val dtH = (t - dose.timestamp) / 3_600_000.0
|
||||||
val tr = carriers.first { it.id == dose.treatmentId }
|
val tr = carriers.first { it.id == dose.treatmentId }
|
||||||
val params = paramsByEster.getValue(tr.esterType)
|
val params = paramsByEster.getValue(tr.esterType)
|
||||||
e2 += EstrannaiseModels.sampleWithParams(tr.esterType, dtH, params) * dose.doseAmount
|
// Même calibration que la courbe ESE : le nuage ENTOURE
|
||||||
|
// la courbe calibrée (fix v1.9.1)
|
||||||
|
val scale = scalePerEster?.get(
|
||||||
|
PharmacokineticEngine.doseEster(tr, dose)
|
||||||
|
) ?: tr.scaleFactor
|
||||||
|
e2 += EstrannaiseModels.sampleWithParams(tr.esterType, dtH, params) * dose.doseAmount * scale
|
||||||
}
|
}
|
||||||
curve.add(LevelPoint(t, e2, 0.0))
|
curve.add(LevelPoint(t, e2, 0.0))
|
||||||
t += stepMs
|
t += stepMs
|
||||||
|
|||||||
@ -366,8 +366,11 @@ fun ChartScreen() {
|
|||||||
// ESE (exclusif à ce modèle — les fits TFS/WHSAH ne
|
// ESE (exclusif à ce modèle — les fits TFS/WHSAH ne
|
||||||
// publient pas de posterior MCMC)
|
// publient pas de posterior MCMC)
|
||||||
val cloud = if (model == "ESE" && showCloud) {
|
val cloud = if (model == "ESE" && showCloud) {
|
||||||
|
// MÊME calibration que la courbe ESE (fix v1.9.1 :
|
||||||
|
// nuage brut ≠ échelle de la courbe calibrée)
|
||||||
EstrannaiseCloud.compute(
|
EstrannaiseCloud.compute(
|
||||||
treatments, doseLogs, startMs, endMs, step, nowMs
|
treatments, doseLogs, startMs, endMs, step, nowMs,
|
||||||
|
scalePerEster = autoM?.esterScales
|
||||||
)
|
)
|
||||||
} else emptyList()
|
} else emptyList()
|
||||||
list.add(ChartCurve(model, curve, cloud))
|
list.add(ChartCurve(model, curve, cloud))
|
||||||
|
|||||||
@ -106,4 +106,30 @@ class EstrannaiseCloudTest {
|
|||||||
listOf(ese()), doses(), NOW - 30 * DAY_MS, NOW, 3_600_000L, NOW, nbCurves = 1
|
listOf(ese()), doses(), NOW - 30 * DAY_MS, NOW, 3_600_000L, NOW, nbCurves = 1
|
||||||
).isEmpty())
|
).isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `cloud follows the calibration like the displayed ESE curve (fix v1_9_1)`() {
|
||||||
|
// FIX « le nuage ne s'active que autour du tracé, pas autour du
|
||||||
|
// modèle Estrannaise » : la courbe ESE affichée est CALIBRÉE
|
||||||
|
// (scalePerEster des labs) mais le nuage était tracé BRUT → les deux
|
||||||
|
// flottaient à des échelles différentes. Avec le MÊME scalePerEster,
|
||||||
|
// le nuage ENTOURE la courbe calibrée (×2 ⇒ nuage ×2, écart de
|
||||||
|
// niveau entre nuage calibré et nuage brut ≈ ×2 également).
|
||||||
|
val scaled = mapOf("EEN" to 2.0)
|
||||||
|
val calibrated = EstrannaiseCloud.compute(
|
||||||
|
listOf(ese()), doses(), NOW - 30 * DAY_MS, NOW, 3_600_000L, NOW,
|
||||||
|
scalePerEster = scaled
|
||||||
|
)
|
||||||
|
val raw = EstrannaiseCloud.compute(
|
||||||
|
listOf(ese()), doses(), NOW - 30 * DAY_MS, NOW, 3_600_000L, NOW
|
||||||
|
)
|
||||||
|
assertEquals(calibrated.size, raw.size)
|
||||||
|
assertTrue(calibrated.isNotEmpty())
|
||||||
|
// Chaque point du nuage calibré ≈ 2× le point brut correspondant
|
||||||
|
val rawLast = raw.last()
|
||||||
|
val calLast = calibrated.last()
|
||||||
|
assertEquals(
|
||||||
|
calLast.last().e2, rawLast.last().e2 * 2.0, rawLast.last().e2 * 0.01
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -160,16 +160,11 @@ class PharmacokineticEngineTest {
|
|||||||
LabResult(marker = "E2", value = predicted * r, unit = "pg/mL", timestamp = t)
|
LabResult(marker = "E2", value = predicted * r, unit = "pg/mL", timestamp = t)
|
||||||
}
|
}
|
||||||
val sf = PharmacokineticEngine.computeScaleFactor(tr, doses, labs)
|
val sf = PharmacokineticEngine.computeScaleFactor(tr, doses, labs)
|
||||||
// DIAG v1.8.2 : comparaison e2At (chemin production) vs Σ
|
// v1.8.2 : la médiane est ARRONDIE (Math.round), pas tronquée — le
|
||||||
// concentrationOfDose (chemin computeScaleFactor)
|
// dernier bit flottant (0,8999… vs 0,9000…) ne doit pas faire chuter
|
||||||
val ratiosDiag = labs.sortedBy { it.timestamp }.map { lab ->
|
// l'échelle d'un cran d'affichage (révélé par le passage d'ESE en
|
||||||
val viaE2At = PharmacokineticEngine.e2At(listOf(tr), doses, lab.timestamp)
|
// analytique v1.9.0, cf CHANGELOG 1.9.1)
|
||||||
val viaDirect = doses.filter { it.timestamp <= lab.timestamp }.sumOf { d ->
|
assertEquals(0.9, sf!!, 0.005)
|
||||||
PharmacokineticEngine.concentrationOfDose(tr, d, lab.timestamp)
|
|
||||||
}
|
|
||||||
"e2At=%.6f/direct=%.6f/ratioSF=%.6f".format(viaE2At, viaDirect, lab.value / viaDirect)
|
|
||||||
}
|
|
||||||
assertEquals("ratios=$ratiosDiag sf=$sf", 0.9, sf!!, 0.01)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@ -3,6 +3,32 @@
|
|||||||
Format : [Keep a Changelog](https://keepachangelog.com/fr-FR/1.1.0/).
|
Format : [Keep a Changelog](https://keepachangelog.com/fr-FR/1.1.0/).
|
||||||
Versionnage : [SemVer](https://semver.org/). Releases taguées dans git (`v1.4.10`, …).
|
Versionnage : [SemVer](https://semver.org/). Releases taguées dans git (`v1.4.10`, …).
|
||||||
|
|
||||||
|
## [1.9.1] — 2026-09-19 (versionCode 39)
|
||||||
|
|
||||||
|
### Corrigé — le nuage d'incertitude entoure maintenant la courbe Estrannaise calibrée
|
||||||
|
|
||||||
|
- **Bug remonté** : le nuage ne s'activait « que autour du tracé, pas autour
|
||||||
|
du modèle Estrannaise » — la courbe ESE affichée est **calibrée**
|
||||||
|
(scalePerEster des labs) mais le nuage était tracé **brut** : les deux
|
||||||
|
flottaient à des échelles différentes.
|
||||||
|
- **Fix** : le nuage reçoit le MÊME `scalePerEster` que la courbe ESE
|
||||||
|
(autoByModel ESE) — il entoure désormais la courbe calibrée (Android +
|
||||||
|
web en miroir).
|
||||||
|
- 1 nouveau test : nuage calibré ×2 = nuage brut ×2 (le nuage suit la
|
||||||
|
calibration).
|
||||||
|
|
||||||
|
### Note — pourquoi le taux affiché a bougé depuis v1.8.2 (+4 pg/mL)
|
||||||
|
|
||||||
|
- v1.8.2 a corrigé l'**arrondi de la médiane de calibration** (troncature
|
||||||
|
flottante → arrondi au plus proche) : une échelle dont la médiane brute
|
||||||
|
tombait à 0,8999… était tronquée en 0,89 au lieu de 0,90. Avec le modèle
|
||||||
|
ESE analytique (v1.9.0) déplaçant ce dernier bit, le piège est devenu
|
||||||
|
visible : les échelles auto-calibrées ont bougé d'un cran d'affichage →
|
||||||
|
le niveau estimé a monté d'environ 1 % (+4 pg/mL sur ~320).
|
||||||
|
- **Le niveau actuel est le CORRIGÉ** (l'arrondi est plus juste que la
|
||||||
|
troncature). Le web applique la même règle depuis v1.9.1 (harmonisation
|
||||||
|
des niveaux entre plateformes).
|
||||||
|
|
||||||
## [1.9.0] — 2026-09-18 (versionCode 38)
|
## [1.9.0] — 2026-09-18 (versionCode 38)
|
||||||
|
|
||||||
### Modèle Estrannaise ANALYTIQUE (abandon de l'ODS) + nuage d'incertitude MCMC
|
### Modèle Estrannaise ANALYTIQUE (abandon de l'ODS) + nuage d'incertitude MCMC
|
||||||
|
|||||||
@ -63,6 +63,7 @@ Fonctionnalités v1 :
|
|||||||
|
|
||||||
| Date | Événement |
|
| Date | Événement |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
| 19 sept. 2026 (session v1.9.1) | **Fix nuage calibré + suggestion sur l'Accueil + explication +4 pg/mL** : (a) remontée « le nuage ne s'active que autour du tracé, pas autour du modèle Estrannaise » : la courbe ESE est CALIBRÉE (scalePerEster) mais le nuage était BRUT → échelles différentes. FIX : EstrannaiseCloud.compute reçoit le MÊME scalePerEster que la courbe ESE (Android + web miroir) ; test nuage calibré ×2 (Android). (b) Le +4 pg/mL constaté depuis v1.8.2 : l'ARRONDI de la médiane (fix v1.8.2, plus juste que la troncature) a déplacé une échelle auto-calibrée d'un cran — le niveau actuel est le CORRIGÉ, documenté CHANGELOG 1.9.1. (c) Émulateur : nuage visible autour de la courbe ESE calibrée (1363 px rose pâle, seed réel + traitement EV ESE de test), 0 crash. Web v1.9.1 sync (harmonisation de l'arrondi — les niveaux web/android convergent). 235 verts / 205 sans données locales. |
|
||||||
| 17 sept. 2026 (session v1.9.0) | **Modèle Estrannaise ANALYTIQUE (abandon de l'ODS) + nuage d'incertitude MCMC (Android + web sync)** (demande) : découverte des sources originelles (github.com/WHSAH/estrannaise.js — forme close 3C + paramètres publiés + posterior MCMC 313/ester). Vérification numérique AVANT débranchement : **RMS 0,00 vs tables ODS** (l'ODS était l'échantillonnage de ces formules). ESE → analytique (dispatch, cutoff 10×t½, t½ LabTiming), PKProfileStore retiré du runtime (asset ODS → src/test/assets, −550 Ko d'APK), ESE couvre 6 esters (+EUCS), TFS = liste explicite (EUCS sans V3C TFS). **Nuage MCMC exclusif ESE** : chip `Nuage` (off, activable si ESE affiché, coupure auto si ESE off), 32 courbes du posterior déterministes, alpha faible sous les courbes, hors échelles/labels/extrema. 11 nouveaux tests (234 verts / 204 sans données locales) + lint + check web (166). Validé émulateur §16.ter : courbe ESE analytique affichée, unités, chips Extend/Cloud, 0 crash. |
|
| 17 sept. 2026 (session v1.9.0) | **Modèle Estrannaise ANALYTIQUE (abandon de l'ODS) + nuage d'incertitude MCMC (Android + web sync)** (demande) : découverte des sources originelles (github.com/WHSAH/estrannaise.js — forme close 3C + paramètres publiés + posterior MCMC 313/ester). Vérification numérique AVANT débranchement : **RMS 0,00 vs tables ODS** (l'ODS était l'échantillonnage de ces formules). ESE → analytique (dispatch, cutoff 10×t½, t½ LabTiming), PKProfileStore retiré du runtime (asset ODS → src/test/assets, −550 Ko d'APK), ESE couvre 6 esters (+EUCS), TFS = liste explicite (EUCS sans V3C TFS). **Nuage MCMC exclusif ESE** : chip `Nuage` (off, activable si ESE affiché, coupure auto si ESE off), 32 courbes du posterior déterministes, alpha faible sous les courbes, hors échelles/labels/extrema. 11 nouveaux tests (234 verts / 204 sans données locales) + lint + check web (166). Validé émulateur §16.ter : courbe ESE analytique affichée, unités, chips Extend/Cloud, 0 crash. |
|
||||||
| 17 sept. 2026 (session v1.8.2) | **Maintenance — audit complet code + doc** : (a) **perf moteur** : cutoffHours pré-calculé par traitement dans e2At/computeCurve (le chemin chaud du tracé labs recalculait ~430 k appels/refresh), Bateman paresseux (inutile pour les traitements à profil), doses pré-groupées par traitement — résultat identique (ordre de sommation préservé, 223 tests verts inchangés) ; (b) **2 fixes de fraîcheur** (#65 accueil : clés produceState sans allTreatments/tConfig — éditer un scaleFactor n'actualisait pas la courbe avant le tick ; #66 graphiques : clé `labResults.size` — éditer la valeur d'un lab ne recalculait pas) ; (c) **nettoyage** : ~9 imports morts, qualifications raccourcies, code mort (Repository/DAO, currentLevel, scheduler2), médiane factorisée (4 copies), SimpleDateFormat hors boucle, AppLog sans recopie, Regex précompilées, formatDose dédupliqué ; (d) **doc** : ~35 corrections (compteurs actuels 223/193 partout, TOC + ancres réparées, inventaire §8 à jour et fusionné, doublons CHANGELOG, §19 backup, fautes). Audit préalable par double exploration lecture-seule (46 fichiers + 4 docs). 223 verts + lint + émulateur smoke. Android seul (web non concerné — re-render complet, aucune release web). |
|
| 17 sept. 2026 (session v1.8.2) | **Maintenance — audit complet code + doc** : (a) **perf moteur** : cutoffHours pré-calculé par traitement dans e2At/computeCurve (le chemin chaud du tracé labs recalculait ~430 k appels/refresh), Bateman paresseux (inutile pour les traitements à profil), doses pré-groupées par traitement — résultat identique (ordre de sommation préservé, 223 tests verts inchangés) ; (b) **2 fixes de fraîcheur** (#65 accueil : clés produceState sans allTreatments/tConfig — éditer un scaleFactor n'actualisait pas la courbe avant le tick ; #66 graphiques : clé `labResults.size` — éditer la valeur d'un lab ne recalculait pas) ; (c) **nettoyage** : ~9 imports morts, qualifications raccourcies, code mort (Repository/DAO, currentLevel, scheduler2), médiane factorisée (4 copies), SimpleDateFormat hors boucle, AppLog sans recopie, Regex précompilées, formatDose dédupliqué ; (d) **doc** : ~35 corrections (compteurs actuels 223/193 partout, TOC + ancres réparées, inventaire §8 à jour et fusionné, doublons CHANGELOG, §19 backup, fautes). Audit préalable par double exploration lecture-seule (46 fichiers + 4 docs). 223 verts + lint + émulateur smoke. Android seul (web non concerné — re-render complet, aucune release web). |
|
||||||
| 17 sept. 2026 (session v1.8.1) | **Fix stabilisation + suggestion sur l'Accueil** (critique v1.8.0 : « changé d'ester, de dosage ET de posologie, et l'app me disait stabilisée depuis février ») : le proxy « 1ʳᵉ dose du traitement » était aveugle aux changements récents. NOUVELLE règle `regimeStartMs` : le régime courant = la **séquence terminale de doses** à (ester effectif, dose mg, **écart inter-doses**) constants (l'écart comparé EXACTEMENT — 7 j ± 1 h casse le régime) ; tout changement récent réinitialise la stabilisation → la reco saute au premier creux post-stabilisation ; l'ester EFFECTIF de la dernière dose (override compris) alimente la carte. **+ Suggestion aussi sur l'Accueil** (demande) : carte compacte (creux daté + créneau, mention de stabilisation seulement si le régime n'était pas déjà stable). 3 nouveaux tests (223 verts / 193 sans données locales). Validé émulateur (release, seed réel) : la carte passe de « stabilisé depuis le 06/02 » à « EEN pas stabilisé avant le 29/09 — premier creux fiable », creux 27/09 22:43, 0 crash. Web v1.8.1 sync (3 tests miroirs + encart Accueil). |
|
| 17 sept. 2026 (session v1.8.1) | **Fix stabilisation + suggestion sur l'Accueil** (critique v1.8.0 : « changé d'ester, de dosage ET de posologie, et l'app me disait stabilisée depuis février ») : le proxy « 1ʳᵉ dose du traitement » était aveugle aux changements récents. NOUVELLE règle `regimeStartMs` : le régime courant = la **séquence terminale de doses** à (ester effectif, dose mg, **écart inter-doses**) constants (l'écart comparé EXACTEMENT — 7 j ± 1 h casse le régime) ; tout changement récent réinitialise la stabilisation → la reco saute au premier creux post-stabilisation ; l'ester EFFECTIF de la dernière dose (override compris) alimente la carte. **+ Suggestion aussi sur l'Accueil** (demande) : carte compacte (creux daté + créneau, mention de stabilisation seulement si le régime n'était pas déjà stable). 3 nouveaux tests (223 verts / 193 sans données locales). Validé émulateur (release, seed réel) : la carte passe de « stabilisé depuis le 06/02 » à « EEN pas stabilisé avant le 29/09 — premier creux fiable », creux 27/09 22:43, 0 crash. Web v1.8.1 sync (3 tests miroirs + encart Accueil). |
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user