This commit is contained in:
Daniel Allaire 2026-03-15 14:43:50 -04:00
parent 54f7c8c300
commit 3247d98fb3
44 changed files with 2871 additions and 973 deletions

View file

@ -61,6 +61,8 @@ validate-generated: $(ICINGA_FILES) bpm/Life-NOC.conf
@grep -R 'check_life_noc_mock' icinga/templates icinga/commands icinga/services >/dev/null
@$(PYTHON) -c 'import pathlib; p = pathlib.Path("bpm/Life-NOC.conf"); text = p.read_text(encoding="utf-8"); lines = [l.strip() for l in text.splitlines() if l.strip()]; assert any(l == "### Business Process Config File ###" for l in lines), "bpm/Life-NOC.conf invalide: en-tête BPM absent"; assert any(l.startswith("display 1;") for l in lines), "bpm/Life-NOC.conf invalide: aucun display absent"; print("Artefacts générés valides")'
check: generate validate
mock-ok:
@bash checks/check_life_noc_mock.sh --state OK --message "Sous contrôle" --item-name "demo" --date "2026-03-13"

View file

@ -7,7 +7,7 @@ Type=simple
User=root
Group=root
WorkingDirectory={{ life_noc_project_root }}
ExecStart=/usr/bin/python3 -m uvicorn scripts.life_noc_input_api:app --host 127.0.0.1 --port 8787
ExecStart=/usr/bin/python3 -m uvicorn scripts.life_noc_input_api:app --host 0.0.0.0 --port 8787
Restart=on-failure
RestartSec=3

View file

@ -1,121 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="${1:-.}"
need_file() {
local f="$1"
if [[ ! -f "$ROOT/$f" ]]; then
echo "Fichier introuvable: $f" >&2
exit 1
fi
}
echo "==> Vérification"
need_file "domains.yaml"
need_file "icinga/commands/check_life_noc_probe.conf"
need_file "scripts/generate_services.py"
need_file "Makefile"
echo "==> Sauvegarde minimale"
mkdir -p "$ROOT/.backup-days-until-due-pilot"
cp -a "$ROOT/domains.yaml" "$ROOT/.backup-days-until-due-pilot/domains.yaml.bak"
echo "==> Création du store d'intrants"
mkdir -p "$ROOT/data/inputs"
cat > "$ROOT/data/inputs/obligations-legales-personnelles.yaml" <<'EOF'
renouvellement-permis-conduire:
value: "2026-09-20"
captured_at: "2026-03-14T23:30:00Z"
origin: manual
EOF
echo "==> Mise à jour de domains.yaml"
python3 - "$ROOT/domains.yaml" <<'PY'
from pathlib import Path
import sys
import yaml
path = Path(sys.argv[1])
data = yaml.safe_load(path.read_text(encoding="utf-8"))
domains = data.get("domains")
if not isinstance(domains, dict):
raise SystemExit("domains.yaml invalide: clé racine 'domains' absente ou invalide")
domain_name = "obligations-legales-personnelles"
items = domains.get(domain_name)
if not isinstance(items, list):
raise SystemExit(f"Domaine introuvable ou invalide: {domain_name}")
target = None
for item in items:
if isinstance(item, dict) and item.get("name") == "renouvellement-permis-conduire":
target = item
break
if target is None:
raise SystemExit("Item 'renouvellement-permis-conduire' introuvable dans domains.yaml")
target["date"] = "2026-09-20"
target["notes"] = "Vérifier la proximité de léchéance du permis"
target["probe"] = {
"type": "days_until_due",
"source": {
"type": "manual_date",
"inputs_file": "/opt/life-noc/data/inputs/obligations-legales-personnelles.yaml",
"item_key": "renouvellement-permis-conduire",
},
"metric": {
"unit": "days",
},
"thresholds": {
"critical_lt": 7,
"warning_lt": 30,
"ok_lt": 90,
"unknown_gte": 90,
},
"policy": {
"on_error": "critical",
},
}
path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True), encoding="utf-8")
print("domains.yaml mis à jour")
PY
echo "==> Vérification rapide de la commande Icinga"
for needle in \
'--unknown-gte' \
'--ok-lt' \
'--warning-lt' \
'--critical-lt'
do
if ! grep -q -- "$needle" "$ROOT/icinga/commands/check_life_noc_probe.conf"; then
echo "ERREUR: argument manquant dans icinga/commands/check_life_noc_probe.conf : $needle" >&2
exit 1
fi
done
echo "==> Génération"
(
cd "$ROOT"
python3 scripts/generate_services.py
)
echo "==> Validation"
(
cd "$ROOT"
make check
)
echo "==> Déploiement"
(
cd "$ROOT"
make deploy-with-bpm
)
echo
echo "Terminé."
echo "Test utile ensuite :"
echo " python3 scripts/life_noc_input.py set obligations-legales-personnelles renouvellement-permis-conduire 2026-03-20"

View file

@ -0,0 +1,295 @@
# Architecture et fonctionnement de Life-NOC
## 1. Présentation générale
### 1.1 Définition
**Life-NOC** est un système de pilotage personnel qui applique à la vie réelle la logique dun **NOC** (Network Operations Center / centre dopérations).
Son but nest pas dautomatiser la vie à la place de la personne. Son but est de :
- rendre visibles les suivis importants ;
- transformer la charge mentale en état observable ;
- soutenir la mémoire prospective ;
- orienter lattention vers ce qui mérite réellement dêtre vu ;
- réduire la friction entre la prise de conscience et laction.
Life-NOC peut être résumé ainsi :
> **Voir clair, prioriser juste, agir au bon endroit.**
### 1.2 Finalité
Life-NOC sert à superviser des éléments de la vie personnelle, domestique, administrative, technique ou communautaire comme sil sagissait de services critiques dans une salle de contrôle.
Il permet par exemple de suivre :
- des revues périodiques ;
- des échéances ;
- des stocks ;
- des vérifications techniques ;
- des obligations administratives ;
- des contrôles de maintenance ;
- des routines de résilience ;
- des points dattention dans lenvironnement physique.
### 1.3 Positionnement
Life-NOC nest pas :
- un simple gestionnaire de tâches ;
- un agenda ;
- un ERP ;
- un moteur dautomatisation généraliste ;
- un outil dinventaire pur.
Life-NOC est :
- un **système de supervision attentionnelle** ;
- un **cadre dévaluation de suivis** ;
- un **point de convergence** entre intrants, règles, états et actions ;
- une **interface de réduction de charge mentale**.
## 2. Principes de fonctionnement
### 2.1 Séparation des couches
Life-NOC repose sur quatre couches distinctes :
1. **définition** ;
2. **ingestion** ;
3. **persistance** ;
4. **évaluation**.
**Définition**
Contient les items à superviser, les méthodes, les seuils, les notes et les liens utiles.
**Ingestion**
Contient les mécanismes par lesquels une donnée entre dans le système :
- saisie manuelle ;
- API ;
- page HTML ;
- QR code ;
- plus tard MQTT, GUI avancée, etc.
**Persistance**
Contient les intrants vivants à jour.
**Évaluation**
Contient la logique qui transforme un intrant en état Life-NOC.
### 2.2 Philosophie générale
Le système suit cette logique :
- le **domaine** organise ;
- la **méthode** évalue ;
- l**item** fait le lien entre les deux ;
- l**intrant** fournit la valeur vivante ;
- la **page item** réduit la friction daction ;
- **Icinga** rend les états visibles dans un cadre de supervision.
## 3. États Life-NOC
### 3.1 États
- **UNKNOWN** : pas encore dû, pas encore à faire ;
- **OK** : actionnable maintenant ;
- **WARNING** : il faut se presser ;
- **CRITICAL** : en retard, anormal, ou impossible à évaluer.
### 3.2 Politique derreur
Par défaut, si le système ne peut pas conclure techniquement, il retourne **CRITICAL**.
Exemples :
- fichier introuvable ;
- donnée absente ;
- format invalide ;
- parsing impossible ;
- seuil incohérent ;
- type de sonde non pris en charge.
## 4. Organisation métier par domaines
Les domaines servent à organiser les suivis.
Exemples :
- revue ;
- focus ;
- finances-personnelles ;
- obligations-legales-personnelles ;
- maison ;
- energie ;
- voiture ;
- projets ;
- documentation ;
- animaux.
Le domaine est une unité dorganisation métier, pas nécessairement une unité de calcul.
## 5. Taxonomie des méthodes de sonde
### 5.1 `elapsed_time`
Mesure le temps écoulé depuis une dernière exécution.
### 5.2 `days_until_due`
Mesure le temps restant avant une échéance fixe.
### 5.3 `elapsed_distance`
Mesure une distance écoulée depuis une action passée.
### 5.4 `current_value`
Évalue une valeur instantanée contre des seuils.
### 5.5 `remaining_quantity`
Évalue ce quil reste dun stock ou dune réserve.
## 6. Portée actuellement validée
Au stade actuel, les méthodes réellement validées sont :
- `elapsed_time` ;
- `days_until_due`.
## 7. Définition des items
Les items sont définis dans :
```text
domains.yaml
```
Un item peut contenir :
- `name`
- `date`
- `notes`
- `notes_url`
- `instructions_url`
- `action_url`
- `probe`
### 7.1 Rôle des champs descriptifs
- `notes` : résumé court ;
- `notes_url` : contexte / référence ;
- `instructions_url` : procédure ;
- `action_url` : outil ou page daction.
## 8. Store des intrants
Les intrants vivants sont stockés sous :
```text
data/inputs/<domaine>.yaml
```
Exemple :
```yaml
revue-hebdomadaire-priorites:
value: "2026-03-14"
captured_at: "2026-03-14T23:16:41Z"
origin: manual
```
## 9. CLI des intrants
Commandes principales :
- `list`
- `get`
- `set`
- `complete`
Exemples :
```bash
life-noc-input list revue
life-noc-input get revue revue-hebdomadaire-priorites
life-noc-input set revue revue-hebdomadaire-priorites 2026-03-14
life-noc-input complete revue revue-hebdomadaire-priorites
```
## 10. API locale des intrants
API locale sur :
```text
http://127.0.0.1:8787
```
Endpoints JSON :
- `GET /health`
- `GET /inputs/{domain}`
- `GET /inputs/{domain}/{item_key}`
- `POST /inputs/{domain}/{item_key}`
- `POST /inputs/{domain}/{item_key}/complete`
Dépendances importantes :
- `python3-fastapi`
- `python3-uvicorn`
- `python3-multipart`
## 11. Pages HTML Life-NOC
Route :
```text
/life-noc/item/<domaine>/<item_key>
```
La page affiche :
- titre humain ;
- état actuel ;
- type de sonde ;
- dernier intrant ;
- domaine ;
- item key ;
- notes ;
- liens utiles ;
- bouton `Compléter`.
Le bouton utilise :
```text
POST /life-noc/item/<domaine>/<item_key>/complete
```
## 12. Intégration Icinga
Life-NOC sappuie sur Icinga pour :
- la visualisation des états ;
- le `Check Now` ;
- les BPM natifs ;
- les servicegroups ;
- laffichage des variables utiles.
## 13. Flux complet
1. définition dans `domains.yaml` ;
2. intrant dans `data/inputs/<domaine>.yaml` ;
3. mise à jour par CLI, API ou page HTML ;
4. lecture par la sonde ;
5. calcul de létat ;
6. affichage dans Icinga ;
7. action de lusager ;
8. mise à jour de lintrant.
## 14. Déploiement
Commandes usuelles :
```bash
make check
make deploy-with-bpm
```
Le dépôt doit rester reproductible sans correctifs manuels cachés.

View file

@ -0,0 +1,83 @@
# Exploitation et dépannage Life-NOC
## 1. Vérifications utiles
### Vérifier lAPI
```bash
curl http://127.0.0.1:8787/health
```
### Vérifier un intrant
```bash
curl http://127.0.0.1:8787/inputs/revue/revue-hebdomadaire-priorites
```
### Vérifier une page item
```bash
curl http://127.0.0.1:8787/life-noc/item/revue/revue-hebdomadaire-priorites
```
## 2. Cas fréquent : lintrant change mais pas létat dans Icinga
Cela signifie souvent quIcinga na pas encore refait le check.
Il faut alors :
- lancer un `Check Now` ;
- ou attendre le prochain check ;
- ou tester directement le plugin à la main.
## 3. Cas fréquent : route HTML absente après déploiement
Vérifier :
- que le service API tourne ;
- que le fichier déployé contient bien les routes HTML ;
- que les dépendances FastAPI sont installées, notamment `python3-multipart`.
## 4. Vérifier le service API
```bash
sudo systemctl status life-noc-input-api --no-pager
sudo journalctl -u life-noc-input-api -n 80 --no-pager
```
## 5. Vérifier directement un plugin
Exemple pour `elapsed_time` :
```bash
/usr/lib/nagios/plugins/check_life_noc_probe.py --probe-type elapsed_time --source-type manual_date --inputs-file /opt/life-noc/data/inputs/revue.yaml --item-key revue-hebdomadaire-priorites --unit days --unknown-lt 5 --ok-gte 5 --warning-gte 7 --critical-gte 10 --on-error critical --label revue-hebdomadaire-priorites
```
## 6. Déploiement
```bash
make check
make deploy-with-bpm
```
## 7. Dépendances API importantes
Le service API nécessite :
- `python3-fastapi`
- `python3-uvicorn`
- `python3-multipart`
## 8. État actuel validé
Éléments validés en pratique :
- Icinga Web 2 ;
- BPM natif ;
- servicegroups ;
- `Check Now` ;
- store dintrants ;
- CLI ;
- API locale ;
- pages HTML ditem ;
- méthodes `elapsed_time` et `days_until_due`.

View file

@ -0,0 +1,79 @@
# Guide usager Life-NOC
## 1. À quoi sert Life-NOC
Life-NOC aide une personne à voir rapidement ce qui mérite son attention, sans tout garder dans sa tête.
Il sert à suivre par exemple :
- des revues régulières ;
- des dates importantes ;
- des entretiens ;
- des vérifications ;
- des obligations.
## 2. Ce que signifient les états
- **UNKNOWN** : pas encore le temps dagir ;
- **OK** : cest le bon moment pour sen occuper ;
- **WARNING** : il faut accélérer ;
- **CRITICAL** : cest urgent ou il y a un problème.
## 3. Comment consulter un item
Un item peut être consulté :
- dans Icinga ;
- via une page Life-NOC ;
- plus tard via un QR code.
La page Life-NOC affiche :
- le nom du suivi ;
- son état ;
- la dernière valeur enregistrée ;
- les notes ;
- les instructions ;
- un bouton pour marquer laction comme faite.
## 4. Comment marquer une action comme faite
Quand litem le permet, il suffit de cliquer sur **Compléter**.
Cela met à jour la valeur de lintrant avec la date du jour.
## 5. À quoi servent les liens utiles
- **Notes** : pour comprendre le contexte ;
- **Instructions** : pour savoir quoi faire ;
- **Action** : pour aller directement au bon outil ou à la bonne page.
## 6. Ce que lusager na pas besoin de savoir
Lusager na normalement pas besoin de comprendre :
- les fichiers YAML ;
- le moteur de sonde ;
- larchitecture technique ;
- le fonctionnement interne dIcinga.
Il lui suffit de :
- voir létat ;
- consulter les instructions ;
- marquer laction effectuée.
## 7. QR codes — direction prévue
Life-NOC est conçu pour permettre lusage de QR codes pointant vers :
```text
/life-noc/item/<domaine>/<item_key>
```
Le scan permettra :
- douvrir directement litem ;
- de lire les instructions ;
- de confirmer laction ;
- de mettre à jour le système facilement.

View file

@ -0,0 +1,734 @@
# Life-NOC — Documentation complète
## 1. Présentation générale
### 1.1 Définition
**Life-NOC** est un système de pilotage personnel qui applique à la vie réelle la logique dun **NOC** (Network Operations Center / centre dopérations).
Son but nest pas dautomatiser la vie à la place de la personne.
Son but est de :
- rendre visibles les suivis importants ;
- transformer la charge mentale en état observable ;
- soutenir la mémoire prospective ;
- orienter lattention vers ce qui mérite réellement dêtre vu ;
- réduire la friction entre la prise de conscience et laction.
Life-NOC peut être résumé ainsi :
> **Voir clair, prioriser juste, agir au bon endroit.**
---
### 1.2 Finalité
Life-NOC sert à superviser des éléments de la vie personnelle, domestique, administrative, technique ou communautaire comme sil sagissait de services critiques dans une salle de contrôle.
Il permet par exemple de suivre :
- des revues périodiques ;
- des échéances ;
- des stocks ;
- des vérifications techniques ;
- des obligations administratives ;
- des contrôles de maintenance ;
- des routines de résilience ;
- des points dattention dans lenvironnement physique.
---
### 1.3 Positionnement
Life-NOC nest pas :
- un simple gestionnaire de tâches ;
- un agenda ;
- un ERP ;
- un moteur dautomatisation généraliste ;
- un outil dinventaire pur.
Life-NOC est :
- un **système de supervision attentionnelle** ;
- un **cadre dévaluation de suivis** ;
- un **point de convergence** entre intrants, règles, états et actions ;
- une **interface de réduction de charge mentale**.
---
## 2. Principes de fonctionnement
### 2.1 Séparation des couches
Life-NOC repose sur quatre couches distinctes :
1. **définition** ;
2. **ingestion** ;
3. **persistance** ;
4. **évaluation**.
#### Définition
Contient les items à superviser, les méthodes, les seuils, les notes et les liens utiles.
#### Ingestion
Contient les mécanismes par lesquels une donnée entre dans le système :
- saisie manuelle ;
- API ;
- page HTML ;
- QR code ;
- plus tard MQTT, GUI avancée, etc.
#### Persistance
Contient les intrants vivants à jour.
#### Évaluation
Contient la logique qui transforme un intrant en état Life-NOC.
---
### 2.2 Philosophie générale
Le système suit cette logique :
- le **domaine** organise ;
- la **méthode** évalue ;
- l**item** fait le lien entre les deux ;
- l**intrant** fournit la valeur vivante ;
- la **page item** réduit la friction daction ;
- **Icinga** rend les états visibles dans un cadre de supervision.
---
## 3. États Life-NOC
Life-NOC utilise une sémantique propre des états.
### 3.1 États
- **UNKNOWN** : pas encore dû, pas encore à faire ;
- **OK** : actionnable maintenant ;
- **WARNING** : il faut se presser ;
- **CRITICAL** : en retard, anormal, ou impossible à évaluer.
### 3.2 Différence avec lusage classique de Nagios/Icinga
Dans Life-NOC :
- le vert ne signifie pas seulement « tout va bien » ;
- le vert signifie souvent « cest le bon moment pour agir » ;
- le gris signifie que litem nest pas encore entré dans sa fenêtre daction.
### 3.3 Politique derreur
Par défaut, si le système ne peut pas conclure techniquement, il retourne **CRITICAL**.
Exemples :
- fichier introuvable ;
- donnée absente ;
- format invalide ;
- parsing impossible ;
- seuil incohérent ;
- type de sonde non pris en charge.
Cette politique force la correction des faux positifs techniques plutôt que leur invisibilisation.
---
## 4. Organisation métier par domaines
Life-NOC regroupe les items dans des **domaines**.
Exemples de domaines :
- revue ;
- focus ;
- finances-personnelles ;
- fiscalite-personnelle ;
- obligations-legales-personnelles ;
- maison ;
- garage-et-rangement ;
- energie ;
- resilience ;
- stock-alimentaire ;
- sante ;
- voiture ;
- jardin ;
- informatique-personnelle ;
- infrastructure-chezlepro ;
- reseau-chezlepro ;
- securite-chezlepro ;
- exploitation-chezlepro ;
- projets ;
- communaute ;
- documentation ;
- animaux.
Le domaine est une unité dorganisation métier, pas nécessairement une unité de calcul.
Un même domaine peut contenir plusieurs méthodes de sonde.
---
## 5. Taxonomie des méthodes de sonde
Le modèle conceptuel v1 de Life-NOC prévoit cinq méthodes principales.
### 5.1 elapsed_time
Mesure le temps écoulé depuis un événement ou une exécution précédente.
Exemples :
- revue hebdomadaire ;
- inspection trimestrielle ;
- test périodique.
Intrant attendu :
- date de dernière exécution.
Unité typique :
- jours.
---
### 5.2 days_until_due
Mesure le temps restant avant une échéance fixe.
Exemples :
- renouvellement du permis ;
- passeport ;
- assurance ;
- obligation légale.
Intrant attendu :
- date déchéance.
Unité typique :
- jours.
---
### 5.3 elapsed_distance
Mesure la distance écoulée depuis une action passée.
Exemples :
- vidange ;
- entretien pneus ;
- entretien freins.
Intrants attendus :
- compteur courant ;
- compteur au dernier entretien.
Unité typique :
- kilomètres.
---
### 5.4 current_value
Évalue une valeur instantanée contre des seuils.
Exemples :
- tension batterie ;
- température ;
- humidité ;
- espace libre.
Intrant attendu :
- valeur actuelle.
Unité typique :
- variable selon le cas.
---
### 5.5 remaining_quantity
Évalue ce quil reste dun stock ou dune réserve.
Exemples :
- nourriture chats ;
- réserve deau ;
- carburant ;
- consommables.
Intrant attendu :
- quantité restante.
Unité typique :
- jours, litres, unités, pourcentage, etc.
---
## 6. Portée actuellement validée
Au stade actuel, les méthodes réellement validées dans le système sont :
- `elapsed_time` ;
- `days_until_due`.
Les autres méthodes sont conceptuellement définies, mais pas encore consolidées de la même manière dans le dépôt.
---
## 7. Définition des items
Les items supervisés sont définis dans :
```text
domains.yaml
```
Chaque item peut contenir :
- `name` ;
- `date` ;
- `notes` ;
- `notes_url` ;
- `instructions_url` ;
- `action_url` ;
- `probe` ;
- `thresholds` ;
- `policy`.
### 7.1 Rôle des champs descriptifs
- `notes` : résumé humain court ;
- `notes_url` : contexte / référence ;
- `instructions_url` : quoi faire / procédure ;
- `action_url` : où agir concrètement.
### 7.2 Champ probe
Le champ `probe` décrit :
- la méthode ;
- la source ;
- lunité ;
- les seuils ;
- la politique derreur.
Exemple :
```yaml
probe:
type: elapsed_time
source:
type: manual_date
inputs_file: "/opt/life-noc/data/inputs/revue.yaml"
item_key: "revue-hebdomadaire-priorites"
metric:
unit: days
thresholds:
unknown_lt: 5
ok_gte: 5
warning_gte: 7
critical_gte: 10
policy:
on_error: critical
```
---
## 8. Store des intrants
### 8.1 Principe
Les intrants vivants ne sont pas mélangés à la définition des items.
Ils sont stockés sous :
```text
data/inputs/<domaine>.yaml
```
Exemples :
- `data/inputs/revue.yaml`
- `data/inputs/obligations-legales-personnelles.yaml`
### 8.2 Contenu minimal dun intrant
Un intrant contient en général :
- `value` ;
- `captured_at` ;
- `origin`.
Exemple :
```yaml
revue-hebdomadaire-priorites:
value: "2026-03-14"
captured_at: "2026-03-14T23:16:41Z"
origin: manual
```
### 8.3 Rôle du store
Le store dintrants permet :
- de changer une valeur sans toucher à `domains.yaml` ;
- dalimenter les sondes avec des valeurs vivantes ;
- de préparer les futures interfaces GUI, API, QR et autres.
---
## 9. CLI de gestion des intrants
Life-NOC dispose dune CLI de gestion des intrants.
### 9.1 Commandes principales
- `list`
- `get`
- `set`
- `complete`
### 9.2 Exemples
```bash
life-noc-input list revue
life-noc-input get revue revue-hebdomadaire-priorites
life-noc-input set revue revue-hebdomadaire-priorites 2026-03-14
life-noc-input complete revue revue-hebdomadaire-priorites
```
### 9.3 Rôle de complete
`complete` est particulièrement utile pour les items de type `elapsed_time/manual_date`.
Il inscrit automatiquement :
- `value = date du jour`
- `captured_at = maintenant`
- `origin = manual`
---
## 10. API locale des intrants
Life-NOC expose une API locale sur :
```text
http://127.0.0.1:8787
```
### 10.1 Endpoints JSON
- `GET /health`
- `GET /inputs/{domain}`
- `GET /inputs/{domain}/{item_key}`
- `POST /inputs/{domain}/{item_key}`
- `POST /inputs/{domain}/{item_key}/complete`
### 10.2 Exemple
```bash
curl http://127.0.0.1:8787/health
curl http://127.0.0.1:8787/inputs/revue
curl http://127.0.0.1:8787/inputs/revue/revue-hebdomadaire-priorites
curl -X POST http://127.0.0.1:8787/inputs/revue/revue-hebdomadaire-priorites \
-H 'Content-Type: application/json' \
-d '{"value":"2026-03-14","origin":"manual"}'
```
### 10.3 Dépendances importantes
Le service FastAPI nécessite notamment :
- `python3-fastapi`
- `python3-uvicorn`
- `python3-multipart`
---
## 11. Pages HTML Life-NOC
Le serveur Life-NOC sert aussi des pages HTML simples destinées à laction humaine.
### 11.1 URL ditem
Format :
```text
/life-noc/item/<domaine>/<item_key>
```
Exemple :
```text
/life-noc/item/revue/revue-hebdomadaire-priorites
```
### 11.2 Contenu de la page
La page affiche :
- titre humain ;
- état actuel ;
- type de sonde ;
- dernier intrant ;
- domaine ;
- item key ;
- notes ;
- lien `notes_url` ;
- lien `instructions_url` ;
- lien `action_url` ;
- bouton `Compléter`.
### 11.3 Route daction HTML
Le bouton `Compléter` utilise :
```text
POST /life-noc/item/<domaine>/<item_key>/complete
```
Puis redirige vers la page de litem.
---
## 12. Intégration Icinga
Life-NOC sappuie sur Icinga 2 / Icinga Web 2 pour la visualisation des états supervisés.
### 12.1 Ce que le système sait faire
- déployer les définitions de services ;
- exposer les custom variables ;
- rendre les états lisibles dans Icinga ;
- supporter `Check Now` ;
- intégrer les BPM natifs ;
- générer des servicegroups cohérents.
### 12.2 Informations visibles dans Icinga
Selon litem, on peut voir notamment :
- létat calculé ;
- les variables de seuil ;
- les variables de source ;
- les liens utiles ;
- `instructions_url` en variable personnalisée.
---
## 13. BPM et servicegroups
Life-NOC génère :
- des services Icinga ;
- des groupes de services ;
- une configuration BPM native.
Lobjectif est de rendre visibles à la fois :
- les items unitaires ;
- les regroupements par domaine ;
- la vision densemble hiérarchisée.
---
## 14. Flux de fonctionnement complet
Le fonctionnement complet suit le chemin suivant :
1. litem est défini dans `domains.yaml` ;
2. lintrant vivant est stocké dans `data/inputs/<domaine>.yaml` ;
3. la CLI, lAPI ou la page HTML modifient cet intrant ;
4. la sonde lit le store ;
5. la sonde calcule létat ;
6. Icinga laffiche ;
7. lusager agit ;
8. lintrant est mis à jour ;
9. le cycle recommence.
---
## 15. Déploiement et reproductibilité
Le dépôt doit permettre de reproduire le système sans correctifs manuels cachés.
### 15.1 Principes
- les correctifs doivent vivre dans le dépôt ;
- les dépendances nécessaires doivent être installées automatiquement ;
- les scripts de chantier temporaires doivent être supprimés une fois les changements intégrés proprement.
### 15.2 Déploiement habituel
```bash
make check
make deploy-with-bpm
```
---
## 16. Documentation pour les usagers
# Guide usager Life-NOC
## 16.1 À quoi sert Life-NOC pour lusager
Life-NOC aide une personne à voir rapidement ce qui mérite son attention, sans tout garder dans sa tête.
Il sert à suivre des choses comme :
- des revues régulières ;
- des dates importantes ;
- des entretiens ;
- des vérifications ;
- des obligations.
## 16.2 Ce que signifient les états
- **UNKNOWN** : pas encore le temps dagir ;
- **OK** : cest le bon moment pour sen occuper ;
- **WARNING** : il faut accélérer ;
- **CRITICAL** : cest urgent ou il y a un problème.
## 16.3 Comment consulter un item
Un item peut être consulté :
- dans Icinga ;
- via une page Life-NOC ;
- plus tard via QR code.
La page Life-NOC affiche :
- le nom du suivi ;
- son état ;
- la dernière valeur enregistrée ;
- les notes ;
- les instructions ;
- un bouton pour marquer laction comme faite.
## 16.4 Comment marquer une action comme faite
Quand litem le permet, il suffit de cliquer sur **Compléter**.
Cela met à jour la valeur de lintrant avec la date du jour.
## 16.5 Quand utiliser les liens utiles
- **Notes** : pour comprendre le contexte ;
- **Instructions** : pour savoir quoi faire ;
- **Action** : pour aller directement au bon outil ou à la bonne page.
## 16.6 Ce que lusager na pas besoin de faire
Lusager na normalement pas besoin de comprendre :
- le moteur de sonde ;
- les fichiers YAML ;
- la structure technique ;
- larchitecture Icinga.
Il lui suffit de :
- voir létat ;
- consulter les instructions ;
- marquer laction effectuée.
---
## 17. Documentation dexploitation
### 17.1 Vérifications utiles
#### Vérifier lAPI
```bash
curl http://127.0.0.1:8787/health
```
#### Vérifier un intrant
```bash
curl http://127.0.0.1:8787/inputs/revue/revue-hebdomadaire-priorites
```
#### Vérifier une page item
```bash
curl http://127.0.0.1:8787/life-noc/item/revue/revue-hebdomadaire-priorites
```
### 17.2 Cas fréquent : lintrant change mais pas létat dans Icinga
Cela signifie souvent quIcinga na pas encore refait le check.
Il faut alors :
- lancer un `Check Now` ;
- ou attendre le prochain check ;
- ou tester directement le plugin à la main.
### 17.3 Cas fréquent : route HTML absente après déploiement
Vérifier :
- que le service API tourne ;
- que le fichier déployé contient bien les routes HTML ;
- que les dépendances FastAPI sont installées, notamment `python3-multipart`.
---
## 18. QR codes — direction prévue
Life-NOC est conçu pour pouvoir pointer des QR codes vers des pages item.
Format visé :
```text
/life-noc/item/<domaine>/<item_key>
```
Le scan permettrait :
- douvrir litem ;
- de lire les instructions ;
- de confirmer laction ;
- de mettre à jour le système facilement.
---
## 19. État actuel du projet
Le système a déjà validé en pratique :
- Icinga Web 2 fonctionnel ;
- BPM natif fonctionnel ;
- servicegroups fonctionnels ;
- `Check Now` fonctionnel ;
- store dintrants séparé ;
- CLI des intrants ;
- API locale ;
- pages HTML ditem ;
- méthodes réelles `elapsed_time` et `days_until_due`.
---
## 20. Conclusion
Life-NOC dispose désormais dun noyau opératoire réel.
Il ne sagit plus seulement dun concept ou dune maquette.
Le système sait déjà :
- définir des suivis ;
- stocker des intrants ;
- évaluer des états ;
- exposer ces états ;
- fournir des instructions ;
- permettre laction ;
- réintégrer cette action dans la supervision.
Cest la base dun véritable système de pilotage personnel orienté action.

View file

@ -0,0 +1,657 @@
# Plan de démockage Life-NOC
## 1. Objet
Ce document sert de plan directeur pour faire évoluer Life-NOC dune preuve de concept partiellement mockée vers un système où **chaque item** :
- possède une **méthode réelle** ;
- possède une **source dintrants réelle** ;
- possède une **page web canonique** ;
- possède une **URL stable** ;
- peut être **consulté**, **compris** et **renseigné** de manière interactive.
Lobjectif final est :
> **1 item = 1 URL = 1 page = 1 logique = 1 source de vérité**
---
## 2. Principes retenus
### 2.1 Tout item doit être démocké
À terme, aucun item ne devrait dépendre dun état mocké durablement.
Le mock a servi à :
- prouver larchitecture ;
- faire exister la structure BPM et Icinga ;
- valider les flux de déploiement ;
- permettre une mise en place progressive.
Mais la cible est désormais :
- **zéro item purement mocké**.
### 2.2 Chaque item doit avoir sa page web
Chaque item doit avoir une URL canonique de la forme :
```text
/life-noc/item/<domaine>/<item_key>
```
Cette page doit devenir :
- la page dinformation ;
- la page dinstructions ;
- la page daction ;
- la page de saisie des intrants.
### 2.3 La page web dun item est linstruction par défaut
Pour tout item :
- si `instructions_url` est défini, il surcharge ;
- sinon, la page canonique de litem est la page dinstructions implicite.
Règle :
```text
instructions_url implicite = /life-noc/item/<domaine>/<item_key>
```
### 2.4 Les domaines organisent, les méthodes évaluent
- le **domaine** organise les items par sens métier ;
- la **méthode** exprime la logique de calcul ;
- l**item** relie domaine, méthode, intrant, UI et action.
Donc :
- plusieurs items dun même domaine peuvent utiliser des méthodes différentes ;
- plusieurs domaines peuvent réutiliser la même méthode.
---
## 3. Méthodes de sonde cibles
### 3.1 Méthodes déjà validées
#### `elapsed_time`
Mesure le temps écoulé depuis une dernière exécution.
#### `days_until_due`
Mesure le temps restant avant une échéance fixe.
### 3.2 Méthodes encore à industrialiser
#### `elapsed_distance`
Mesure une distance écoulée depuis une action passée.
#### `current_value`
Évalue une valeur instantanée contre des seuils.
#### `remaining_quantity`
Évalue une quantité restante contre des seuils.
---
## 4. Types dintrants cibles
Les intrants vivants continueront dêtre stockés dans :
```text
data/inputs/<domaine>.yaml
```
Les types principaux dintrants à supporter sont :
- `manual_date`
- `manual_counter`
- `manual_value`
À terme, dautres sources pourront exister, mais le démockage initial se concentre sur les intrants simples et robustes.
---
## 5. Formulaires web cibles
Chaque item doit pouvoir afficher un formulaire adapté à son type réel.
### 5.1 `complete_date`
Pour les items de type `elapsed_time` avec saisie de date de dernière exécution.
Exemples :
- revue périodique ;
- inspection ;
- test ;
- vérification périodique.
Fonctions attendues :
- bouton `Compléter`
- saisie manuelle dune date
- bouton `Aujourdhui`
### 5.2 `due_date`
Pour les items de type `days_until_due`.
Exemples :
- permis ;
- assurance ;
- passeport ;
- échéance administrative.
Fonctions attendues :
- saisie dune date déchéance
- bouton `Enregistrer`
### 5.3 `counter_pair`
Pour les items de type `elapsed_distance`.
Exemples :
- huile moteur ;
- pneus ;
- freins ;
- timing belt.
Fonctions attendues :
- compteur courant
- compteur au dernier entretien
- bouton `Enregistrer`
### 5.4 `numeric_value`
Pour les items de type `current_value`.
Exemples :
- tension batterie
- capacité libre
- température
- mesure technique
Fonctions attendues :
- saisie numérique
- unité affichée
- bouton `Enregistrer`
### 5.5 `remaining_quantity`
Pour les items de type `remaining_quantity`.
Exemples :
- réserve deau
- stock nourriture chats
- stock consommables
- carburant
Fonctions attendues :
- quantité restante
- unité
- bouton `Enregistrer`
---
## 6. Modèle cible dun item réel
Le modèle canonique minimal dun item réel devrait converger vers ceci :
```yaml
- name: remplacement-filtre-fournaise
title: "Remplacement du filtre de la fournaise"
summary: "Suivi périodique du filtre principal."
notes: "À faire selon la fréquence dusage et la saison."
notes_url: ""
instructions: |
1. Couper le système si nécessaire.
2. Retirer le filtre usagé.
3. Vérifier le sens du flux dair.
4. Installer le nouveau filtre.
5. Confirmer la date dans Life-NOC.
instructions_url: ""
action_url: ""
probe:
type: elapsed_time
source:
type: manual_date
inputs_file: "/opt/life-noc/data/inputs/maison.yaml"
item_key: "remplacement-filtre-fournaise"
metric:
unit: days
thresholds:
unknown_lt: 30
ok_gte: 30
warning_gte: 60
critical_gte: 90
policy:
on_error: critical
ui:
form_mode: complete_date
allow_complete: true
allow_manual_edit: true
```
Tous les champs ne seront pas obligatoires immédiatement, mais cette structure donne la direction.
---
## 7. Tableau de migration
## 7.1 REVUE
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| revue-quotidienne-life-noc | elapsed_time | manual_date | complete_date | réel |
| revue-hebdomadaire-priorites | elapsed_time | manual_date | complete_date | réel |
| revue-mensuelle-systeme-vie | elapsed_time | manual_date | complete_date | à migrer |
| revue-trimestrielle-orientation | elapsed_time | manual_date | complete_date | à migrer |
## 7.2 FOCUS
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| entretien-processus-focus | elapsed_time | manual_date | complete_date | à migrer |
| nettoyage-inbox-mentale | elapsed_time | manual_date | complete_date | à migrer |
| revue-limitation-engagements | elapsed_time | manual_date | complete_date | à migrer |
## 7.3 FINANCES-PERSONNELLES
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| revision-comptes-bancaires | elapsed_time | manual_date | complete_date | à migrer |
| verification-cartes-credit | elapsed_time | manual_date | complete_date | à migrer |
| paiement-cartes-credit | elapsed_time | manual_date | complete_date | à migrer |
| revision-budget-personnel | elapsed_time | manual_date | complete_date | à migrer |
| verification-prelevements-automatiques | elapsed_time | manual_date | complete_date | à migrer |
| verification-placements | elapsed_time | manual_date | complete_date | à migrer |
| verification-cotisations-reer | elapsed_time | manual_date | complete_date | à migrer |
| verification-celi | elapsed_time | manual_date | complete_date | à migrer |
| preparation-dossier-financier-annuel | elapsed_time | manual_date | complete_date | à migrer |
## 7.4 FISCALITE-PERSONNELLE
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| preparation-documents-impots | elapsed_time | manual_date | complete_date | à migrer |
| production-impots-federal | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser |
| production-impots-quebec | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser |
| paiement-solde-impots | days_until_due | manual_date | due_date | à migrer |
| verification-avis-cotisation | elapsed_time | manual_date | complete_date | à migrer |
| archivage-documents-fiscaux | elapsed_time | manual_date | complete_date | à migrer |
| verification-acomptes-provisionnels | days_until_due | manual_date | due_date | à migrer |
## 7.5 OBLIGATIONS-LEGALES-PERSONNELLES
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-testament | elapsed_time | manual_date | complete_date | à migrer |
| verification-mandat-inaptitude | elapsed_time | manual_date | complete_date | à migrer |
| verification-directives-medicales | elapsed_time | manual_date | complete_date | à migrer |
| verification-papiers-identite | elapsed_time | manual_date | complete_date | à migrer |
| renouvellement-permis-conduire | days_until_due | manual_date | due_date | réel |
| verification-carte-assurance-maladie | days_until_due | manual_date | due_date | à migrer |
| verification-passeport | days_until_due | manual_date | due_date | à migrer |
## 7.6 MAISON
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| remplacement-filtre-fournaise | elapsed_time | manual_date | complete_date | à migrer |
| inspection-fournaise | elapsed_time | manual_date | complete_date | à migrer |
| inspection-thermopompe | elapsed_time | manual_date | complete_date | à migrer |
| nettoyage-unites-exterieures | elapsed_time | manual_date | complete_date | à migrer |
| inspection-toiture | elapsed_time | manual_date | complete_date | à migrer |
| inspection-gouttieres | elapsed_time | manual_date | complete_date | à migrer |
| inspection-fondation | elapsed_time | manual_date | complete_date | à migrer |
| verification-drainage-terrain | elapsed_time | manual_date | complete_date | à migrer |
| test-detecteurs-fumee | elapsed_time | manual_date | complete_date | à migrer |
| test-detecteurs-co | elapsed_time | manual_date | complete_date | à migrer |
| remplacement-piles-detecteurs | elapsed_time | manual_date | complete_date | à migrer |
| verification-plomberie-visible | elapsed_time | manual_date | complete_date | à migrer |
| inspection-sump-pump | elapsed_time | manual_date | complete_date | à migrer |
| verification-calfetrage | elapsed_time | manual_date | complete_date | à migrer |
| inspection-portes-garage | elapsed_time | manual_date | complete_date | à migrer |
| verification-extincteurs | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser |
## 7.7 GARAGE-ET-RANGEMENT
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| inspection-rangement-plafond | elapsed_time | manual_date | complete_date | à migrer |
| revue-inventaire-garage | elapsed_time | manual_date | complete_date | à migrer |
| verification-rouille-outillage | elapsed_time | manual_date | complete_date | à migrer |
| inspection-securite-garage | elapsed_time | manual_date | complete_date | à migrer |
| rotation-stockage-bacs | elapsed_time | manual_date | complete_date | à migrer |
## 7.8 ENERGIE
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| inspection-batteries-lifepo4 | elapsed_time | manual_date | complete_date | à migrer |
| verification-tension-batteries | current_value | manual_value | numeric_value | à migrer |
| verification-smartshunt | current_value | manual_value | numeric_value | à préciser |
| verification-cerbo-gx | elapsed_time / current_value | manual_date / manual_value | complete_date / numeric_value | à préciser |
| verification-can-rs485 | elapsed_time | manual_date | complete_date | à migrer |
| inspection-cablage-energie | elapsed_time | manual_date | complete_date | à migrer |
| verification-busbars | elapsed_time | manual_date | complete_date | à migrer |
| verification-parametres-charge | elapsed_time | manual_date | complete_date | à migrer |
| test-charge-batteries | elapsed_time | manual_date | complete_date | à migrer |
| test-comportement-onduleur | elapsed_time | manual_date | complete_date | à migrer |
| verification-journal-evenements-victron | elapsed_time | manual_date | complete_date | à migrer |
## 7.9 RESILIENCE
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| test-generatrice-propane | elapsed_time | manual_date | complete_date | à migrer |
| verification-carburant-generatrice | remaining_quantity | manual_value | remaining_quantity | à migrer |
| verification-procedure-bascule | elapsed_time | manual_date | complete_date | à migrer |
| verification-stock-lampes-piles | remaining_quantity | manual_value | remaining_quantity | à migrer |
| verification-trousses-urgence | remaining_quantity / elapsed_time | manual_value / manual_date | remaining_quantity / complete_date | à préciser |
| revision-plan-urgence | elapsed_time | manual_date | complete_date | à migrer |
| test-autonomie-base | elapsed_time | manual_date | complete_date | à migrer |
| verification-moyens-cuisson-secours | remaining_quantity / elapsed_time | manual_value / manual_date | remaining_quantity / complete_date | à préciser |
## 7.10 STOCK-ALIMENTAIRE
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| rotation-nourriture-seche | elapsed_time | manual_date | complete_date | à migrer |
| verification-reserve-eau | remaining_quantity | manual_value | remaining_quantity | à migrer |
| verification-mylar-absorbeurs | remaining_quantity | manual_value | remaining_quantity | à migrer |
| revue-inventaire-conserves | remaining_quantity | manual_value | remaining_quantity | à migrer |
| revue-inventaire-farine-riz-pates | remaining_quantity | manual_value | remaining_quantity | à migrer |
| verification-bacs-legumes-racines | remaining_quantity | manual_value | remaining_quantity | à migrer |
| revue-supplements-vitamines | remaining_quantity | manual_value | remaining_quantity | à migrer |
## 7.11 SANTE
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-trousse-premiers-soins | remaining_quantity / elapsed_time | manual_value / manual_date | remaining_quantity / complete_date | à préciser |
| verification-expiration-medicaments | days_until_due | manual_date | due_date | à migrer |
| revision-rendez-vous-medicaux | days_until_due | manual_date | due_date | à migrer |
| verification-lunettes-prescriptions | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser |
| verification-dossier-sante | elapsed_time | manual_date | complete_date | à migrer |
## 7.12 VOITURE
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-huile-moteur | elapsed_distance | manual_counter | counter_pair | à migrer |
| inspection-freins | elapsed_distance | manual_counter | counter_pair | à migrer |
| verification-pneus | elapsed_distance | manual_counter | counter_pair | à migrer |
| ajustement-valves | elapsed_distance / elapsed_time | manual_counter / manual_date | counter_pair / complete_date | à préciser |
| verification-timing-belt | elapsed_distance | manual_counter | counter_pair | à migrer |
| verification-batterie-voiture | current_value | manual_value | numeric_value | à migrer |
| verification-balais-essuie-glace | elapsed_time | manual_date | complete_date | à migrer |
| verification-liquides | current_value / elapsed_time | manual_value / manual_date | numeric_value / complete_date | à préciser |
| verification-eclairage | elapsed_time | manual_date | complete_date | à migrer |
| verification-immatriculation-assurance | days_until_due | manual_date | due_date | à migrer |
## 7.13 JARDIN
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| inspection-vignes | elapsed_time | manual_date | complete_date | à migrer |
| entretien-bleuetiers | elapsed_time | manual_date | complete_date | à migrer |
| entretien-framboisiers | elapsed_time | manual_date | complete_date | à migrer |
| inspection-kiwis-nordiques | elapsed_time | manual_date | complete_date | à migrer |
| entretien-rhubarbe | elapsed_time | manual_date | complete_date | à migrer |
| inspection-groseillier | elapsed_time | manual_date | complete_date | à migrer |
| inspection-muriers | elapsed_time | manual_date | complete_date | à migrer |
| entretien-houblon | elapsed_time | manual_date | complete_date | à migrer |
| revision-pharmacopee-jardin | remaining_quantity | manual_value | remaining_quantity | à migrer |
| revision-armoire-produits-jardin | remaining_quantity | manual_value | remaining_quantity | à migrer |
## 7.14 OUTILS-ET-EQUIPEMENTS
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| entretien-outils-jardin | elapsed_time | manual_date | complete_date | à migrer |
| inspection-outils-electriques | elapsed_time | manual_date | complete_date | à migrer |
| verification-rallonges-et-connecteurs | elapsed_time | manual_date | complete_date | à migrer |
| verification-compresseur-et-accessoires | elapsed_time | manual_date | complete_date | à migrer |
| revision-inventaire-outillage | elapsed_time | manual_date | complete_date | à migrer |
## 7.15 INFORMATIQUE-PERSONNELLE
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-sauvegardes-personnelles | elapsed_time | manual_date | complete_date | à migrer |
| nettoyage-stockage-personnel | elapsed_time | manual_date | complete_date | à migrer |
| verification-comptes-importants | elapsed_time | manual_date | complete_date | à migrer |
| revue-mots-de-passe-personnels | elapsed_time | manual_date | complete_date | à migrer |
| verification-documents-cloud-personnels | elapsed_time | manual_date | complete_date | à migrer |
## 7.16 INFRASTRUCTURE-CHEZLEPRO
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-proxmox | elapsed_time | manual_date | complete_date | à migrer |
| verification-ceph | elapsed_time / current_value | manual_date / manual_value | complete_date / numeric_value | à préciser |
| test-restauration-backups | elapsed_time | manual_date | complete_date | à migrer |
| verification-pbs-truenas | elapsed_time | manual_date | complete_date | à migrer |
| verification-ups-infrastructure | current_value | manual_value | numeric_value | à migrer |
| verification-capacite-stockage | current_value | manual_value | numeric_value | à migrer |
| verification-apt-cacher-ng | elapsed_time | manual_date | complete_date | à migrer |
| verification-icinga2 | elapsed_time | manual_date | complete_date | à migrer |
| verification-keycloak-openldap | elapsed_time | manual_date | complete_date | à migrer |
| verification-nextcloud | elapsed_time | manual_date | complete_date | à migrer |
| verification-mailcow | elapsed_time | manual_date | complete_date | à migrer |
| verification-openvas | elapsed_time | manual_date | complete_date | à migrer |
| verification-journaux-systemes | elapsed_time | manual_date | complete_date | à migrer |
## 7.17 RESEAU-CHEZLEPRO
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-firewalls | elapsed_time | manual_date | complete_date | à migrer |
| verification-vpn-openvpn | elapsed_time | manual_date | complete_date | à migrer |
| verification-crl-certificats | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser |
| verification-switches | elapsed_time | manual_date | complete_date | à migrer |
| verification-vlans-segmentation | elapsed_time | manual_date | complete_date | à migrer |
| verification-dns | elapsed_time | manual_date | complete_date | à migrer |
| verification-dynamic-dns | elapsed_time | manual_date | complete_date | à migrer |
| audit-regles-firewall | elapsed_time | manual_date | complete_date | à migrer |
| verification-geoblocking | elapsed_time | manual_date | complete_date | à migrer |
| verification-certificats-publics | days_until_due | manual_date | due_date | à migrer |
## 7.18 SECURITE-CHEZLEPRO
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-mises-a-jour-securite | elapsed_time | manual_date | complete_date | à migrer |
| audit-comptes-acces | elapsed_time | manual_date | complete_date | à migrer |
| verification-ids-snort | elapsed_time | manual_date | complete_date | à migrer |
| revue-pki-privee | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser |
| revue-politiques-securite | elapsed_time | manual_date | complete_date | à migrer |
| verification-sauvegardes-configuration | elapsed_time | manual_date | complete_date | à migrer |
## 7.19 EXPLOITATION-CHEZLEPRO
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| revision-playbooks-ansible | elapsed_time | manual_date | complete_date | à migrer |
| verification-inventaires-ansible | elapsed_time | manual_date | complete_date | à migrer |
| revision-documentation-technique | elapsed_time | manual_date | complete_date | à migrer |
| verification-procedures-reprise | elapsed_time | manual_date | complete_date | à migrer |
| revue-capacite-ressources | elapsed_time | manual_date | complete_date | à migrer |
| verification-jobs-automatises | elapsed_time | manual_date | complete_date | à migrer |
## 7.20 OBLIGATIONS-CHEZLEPRO
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-registraire-entreprise | days_until_due | manual_date | due_date | à migrer |
| verification-declarations-taxes | days_until_due | manual_date | due_date | à migrer |
| verification-dossier-comptable-entreprise | elapsed_time | manual_date | complete_date | à migrer |
| verification-facturation-clients | elapsed_time | manual_date | complete_date | à migrer |
| verification-paiements-fournisseurs | elapsed_time | manual_date | complete_date | à migrer |
| verification-contrats-ententes | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser |
| verification-assurances-entreprise | days_until_due | manual_date | due_date | à migrer |
| archivage-documents-entreprise | elapsed_time | manual_date | complete_date | à migrer |
## 7.21 ACTIFS-CHEZLEPRO
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| revision-inventaire-materiel | elapsed_time | manual_date | complete_date | à migrer |
| verification-amortissables | days_until_due / elapsed_time | manual_date | due_date / complete_date | à préciser |
| verification-serveurs-et-composants | elapsed_time | manual_date | complete_date | à migrer |
| verification-reserve-pieces | remaining_quantity | manual_value | remaining_quantity | à migrer |
| revision-actifs-transferts | elapsed_time | manual_date | complete_date | à migrer |
## 7.22 PROJETS
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| revision-life-noc | elapsed_time | manual_date | complete_date | à migrer |
| revision-alliance-boreale | elapsed_time | manual_date | complete_date | à migrer |
| revision-erplibre | elapsed_time | manual_date | complete_date | à migrer |
| revision-semence-numerique | elapsed_time | manual_date | complete_date | à migrer |
| revision-ortrux-1 | elapsed_time | manual_date | complete_date | à migrer |
| revision-district16 | elapsed_time | manual_date | complete_date | à migrer |
## 7.23 COMMUNAUTE
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-outils-aa | elapsed_time | manual_date | complete_date | à migrer |
| verification-espace-87-16 | elapsed_time | manual_date | complete_date | à migrer |
| verification-salles-visio-groupes | elapsed_time | manual_date | complete_date | à migrer |
| verification-depots-rapports-rsg | elapsed_time | manual_date | complete_date | à migrer |
| revision-outils-communication-communaute | elapsed_time | manual_date | complete_date | à migrer |
## 7.24 DOCUMENTATION
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| revue-docs-personnelles-importantes | elapsed_time | manual_date | complete_date | à migrer |
| revue-wiki-technique | elapsed_time | manual_date | complete_date | à migrer |
| revue-base-connaissances | elapsed_time | manual_date | complete_date | à migrer |
| verification-sauvegarde-docs-cles | elapsed_time | manual_date | complete_date | à migrer |
## 7.25 ANIMAUX
| item_key | méthode cible | intrant | formulaire | statut |
|---|---|---|---|---|
| verification-stock-nourriture-chats | remaining_quantity | manual_value | remaining_quantity | à migrer |
| verification-friandises-et-litiere | remaining_quantity | manual_value | remaining_quantity | à migrer |
| revision-routine-soins-animaux | elapsed_time | manual_date | complete_date | à migrer |
---
## 8. Priorité de migration
### Vague 1 — tout ce qui est date simple
Objectif :
- finir `elapsed_time`
- finir `days_until_due`
Cest la vague la plus simple, la plus rentable et la moins risquée.
### Vague 2 — voiture
Objectif :
- introduire `elapsed_distance`
### Vague 3 — stocks et réserves
Objectif :
- introduire `remaining_quantity`
### Vague 4 — mesures techniques
Objectif :
- introduire `current_value`
---
## 9. Pages web cibles
Chaque item doit converger vers une page web canonique :
```text
/life-noc/item/<domaine>/<item_key>
```
Cette page doit devenir :
- la fiche de contexte ;
- la fiche dinstructions ;
- la fiche daction ;
- la fiche de saisie des intrants.
### 9.1 Contenu minimal
- titre humain
- état actuel
- dernier intrant
- résumé
- notes
- instructions
- liens utiles
- formulaire adapté
- action disponible
### 9.2 Règle sur `instructions_url`
La page de litem est la page dinstructions par défaut.
Règle :
- si `instructions_url` est explicite, on lutilise ;
- sinon, on considère implicitement :
```text
/life-noc/item/<domaine>/<item_key>
```
---
## 10. Critère de fin de démockage
Le démockage dun item sera considéré terminé quand :
- litem possède un `probe` réel ;
- litem possède un intrant réel ;
- litem possède une page web opérationnelle ;
- litem possède un formulaire web adapté ;
- litem possède une URL canonique stable ;
- létat Icinga est calculé à partir du vrai store.
---
## 11. Stratégie de mise en œuvre
### Phase 1
Finir toutes les migrations `elapsed_time` et `days_until_due`.
### Phase 2
Faire évoluer la page item pour afficher :
- `title`
- `summary`
- `instructions`
- formulaire adapté au type dintrant.
### Phase 3
Ajouter les endpoints HTML de mise à jour interactive des intrants.
### Phase 4
Introduire successivement :
- `elapsed_distance`
- `remaining_quantity`
- `current_value`
### Phase 5
Éliminer complètement les items mockés restants.
---
## 12. Conclusion
La cible Life-NOC retenue est désormais claire :
- **tout item doit être réel**
- **tout item doit avoir sa page**
- **tout item doit avoir son URL**
- **tout item doit être renseignable**
- **tout item doit pouvoir guider laction**
Ce plan sert de carte de migration pour converger progressivement vers ce modèle.

View file

@ -21,6 +21,7 @@ domains:
- name: revue-hebdomadaire-priorites
date: '2026-03-10'
notes: Revoir les priorités de la semaine
instructions_url: "https://chezlepro.ca/life-noc/test-instructions"
probe:
type: elapsed_time
source:

View file

@ -11,6 +11,7 @@ apply Service "actifs-chezlepro-revision-inventaire-materiel" {
vars.mock_message = "Sous contrôle"
groups = [ "ACTIFS-CHEZLEPRO" ]
notes = "Revoir l'inventaire du matériel de Chezlepro"
vars.instructions_url = "/life-noc/item/actifs-chezlepro/revision-inventaire-materiel"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "actifs-chezlepro-verification-amortissables" {
vars.mock_message = "Sous contrôle"
groups = [ "ACTIFS-CHEZLEPRO" ]
notes = "Vérifier le registre des biens amortissables"
vars.instructions_url = "/life-noc/item/actifs-chezlepro/verification-amortissables"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "actifs-chezlepro-verification-serveurs-et-composants" {
vars.mock_message = "Sous contrôle"
groups = [ "ACTIFS-CHEZLEPRO" ]
notes = "Vérifier l'état des serveurs et composants matériels"
vars.instructions_url = "/life-noc/item/actifs-chezlepro/verification-serveurs-et-composants"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "actifs-chezlepro-verification-reserve-pieces" {
vars.mock_message = "Sous contrôle"
groups = [ "ACTIFS-CHEZLEPRO" ]
notes = "Vérifier les pièces de rechange critiques"
vars.instructions_url = "/life-noc/item/actifs-chezlepro/verification-reserve-pieces"
assign where host.name == "life-noc"
}
@ -51,5 +55,6 @@ apply Service "actifs-chezlepro-revision-actifs-transferts" {
vars.mock_message = "Sous contrôle"
groups = [ "ACTIFS-CHEZLEPRO" ]
notes = "Réviser les actifs transférés ou à transférer à l'entreprise"
vars.instructions_url = "/life-noc/item/actifs-chezlepro/revision-actifs-transferts"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "animaux-verification-stock-nourriture-chats" {
vars.mock_message = "Sous contrôle"
groups = [ "ANIMAUX" ]
notes = "Vérification du stock de nourriture pour chats"
vars.instructions_url = "/life-noc/item/animaux/verification-stock-nourriture-chats"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "animaux-verification-friandises-et-litiere" {
vars.mock_message = "Sous contrôle"
groups = [ "ANIMAUX" ]
notes = "Vérification des stocks de friandises et de litière"
vars.instructions_url = "/life-noc/item/animaux/verification-friandises-et-litiere"
assign where host.name == "life-noc"
}
@ -31,5 +33,6 @@ apply Service "animaux-revision-routine-soins-animaux" {
vars.mock_message = "Sous contrôle"
groups = [ "ANIMAUX" ]
notes = "Revue de la routine de soins et des besoins matériels des chats"
vars.instructions_url = "/life-noc/item/animaux/revision-routine-soins-animaux"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "communaute-verification-outils-aa" {
vars.mock_message = "Sous contrôle"
groups = [ "COMMUNAUTE" ]
notes = "Vérifier les outils numériques liés aux activités communautaires"
vars.instructions_url = "/life-noc/item/communaute/verification-outils-aa"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "communaute-verification-espace-87-16" {
vars.mock_message = "Sous contrôle"
groups = [ "COMMUNAUTE" ]
notes = "Vérifier l'espace Nextcloud du district"
vars.instructions_url = "/life-noc/item/communaute/verification-espace-87-16"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "communaute-verification-salles-visio-groupes" {
vars.mock_message = "Sous contrôle"
groups = [ "COMMUNAUTE" ]
notes = "Vérifier les salles de visioconférence des groupes"
vars.instructions_url = "/life-noc/item/communaute/verification-salles-visio-groupes"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "communaute-verification-depots-rapports-rsg" {
vars.mock_message = "Sous contrôle"
groups = [ "COMMUNAUTE" ]
notes = "Vérifier les dépôts de rapports RSG"
vars.instructions_url = "/life-noc/item/communaute/verification-depots-rapports-rsg"
assign where host.name == "life-noc"
}
@ -51,5 +55,6 @@ apply Service "communaute-revision-outils-communication-communaute" {
vars.mock_message = "Sous contrôle"
groups = [ "COMMUNAUTE" ]
notes = "Réviser les moyens de communication communautaires"
vars.instructions_url = "/life-noc/item/communaute/revision-outils-communication-communaute"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "documentation-revue-docs-personnelles-importantes" {
vars.mock_message = "Sous contrôle"
groups = [ "DOCUMENTATION" ]
notes = "Revoir l'organisation des documents personnels importants"
vars.instructions_url = "/life-noc/item/documentation/revue-docs-personnelles-importantes"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "documentation-revue-wiki-technique" {
vars.mock_message = "Sous contrôle"
groups = [ "DOCUMENTATION" ]
notes = "Revoir le wiki technique et sa cohérence"
vars.instructions_url = "/life-noc/item/documentation/revue-wiki-technique"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "documentation-revue-base-connaissances" {
vars.mock_message = "Sous contrôle"
groups = [ "DOCUMENTATION" ]
notes = "Revoir la base de connaissances globale"
vars.instructions_url = "/life-noc/item/documentation/revue-base-connaissances"
assign where host.name == "life-noc"
}
@ -41,5 +44,6 @@ apply Service "documentation-verification-sauvegarde-docs-cles" {
vars.mock_message = "Sous contrôle"
groups = [ "DOCUMENTATION" ]
notes = "Vérifier la sauvegarde des documents clés"
vars.instructions_url = "/life-noc/item/documentation/verification-sauvegarde-docs-cles"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "energie-inspection-batteries-lifepo4" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Inspection physique et logique des batteries LiFePO4"
vars.instructions_url = "/life-noc/item/energie/inspection-batteries-lifepo4"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "energie-verification-tension-batteries" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Vérification des tensions et cohérence des batteries"
vars.instructions_url = "/life-noc/item/energie/verification-tension-batteries"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "energie-verification-smartshunt" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Vérification SmartShunt et cohérence du monitoring"
vars.instructions_url = "/life-noc/item/energie/verification-smartshunt"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "energie-verification-cerbo-gx" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Vérification Cerbo GX et remontée des données"
vars.instructions_url = "/life-noc/item/energie/verification-cerbo-gx"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "energie-verification-can-rs485" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Vérification des communications CAN ou RS485"
vars.instructions_url = "/life-noc/item/energie/verification-can-rs485"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "energie-inspection-cablage-energie" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Inspection du câblage et des connexions énergétiques"
vars.instructions_url = "/life-noc/item/energie/inspection-cablage-energie"
assign where host.name == "life-noc"
}
@ -71,6 +77,7 @@ apply Service "energie-verification-busbars" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Vérification du serrage, corrosion et échauffement des busbars"
vars.instructions_url = "/life-noc/item/energie/verification-busbars"
assign where host.name == "life-noc"
}
@ -81,6 +88,7 @@ apply Service "energie-verification-parametres-charge" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Vérification des paramètres de charge et float"
vars.instructions_url = "/life-noc/item/energie/verification-parametres-charge"
assign where host.name == "life-noc"
}
@ -91,6 +99,7 @@ apply Service "energie-test-charge-batteries" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Test de comportement en charge des batteries"
vars.instructions_url = "/life-noc/item/energie/test-charge-batteries"
assign where host.name == "life-noc"
}
@ -101,6 +110,7 @@ apply Service "energie-test-comportement-onduleur" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Vérifier le comportement du Multiplus sous charge légère"
vars.instructions_url = "/life-noc/item/energie/test-comportement-onduleur"
assign where host.name == "life-noc"
}
@ -111,5 +121,6 @@ apply Service "energie-verification-journal-evenements-victron" {
vars.mock_message = "Sous contrôle"
groups = [ "ENERGIE" ]
notes = "Vérification des événements et anomalies Victron"
vars.instructions_url = "/life-noc/item/energie/verification-journal-evenements-victron"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "exploitation-chezlepro-revision-playbooks-ansible" {
vars.mock_message = "Sous contrôle"
groups = [ "EXPLOITATION-CHEZLEPRO" ]
notes = "Revue des playbooks Ansible"
vars.instructions_url = "/life-noc/item/exploitation-chezlepro/revision-playbooks-ansible"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "exploitation-chezlepro-verification-inventaires-ansible" {
vars.mock_message = "Sous contrôle"
groups = [ "EXPLOITATION-CHEZLEPRO" ]
notes = "Vérification des inventaires Ansible"
vars.instructions_url = "/life-noc/item/exploitation-chezlepro/verification-inventaires-ansible"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "exploitation-chezlepro-revision-documentation-technique" {
vars.mock_message = "Sous contrôle"
groups = [ "EXPLOITATION-CHEZLEPRO" ]
notes = "Revue de la documentation technique Chezlepro"
vars.instructions_url = "/life-noc/item/exploitation-chezlepro/revision-documentation-technique"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "exploitation-chezlepro-verification-procedures-reprise" {
vars.mock_message = "Sous contrôle"
groups = [ "EXPLOITATION-CHEZLEPRO" ]
notes = "Vérifier les procédures de reprise et restauration"
vars.instructions_url = "/life-noc/item/exploitation-chezlepro/verification-procedures-reprise"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "exploitation-chezlepro-revue-capacite-ressources" {
vars.mock_message = "Sous contrôle"
groups = [ "EXPLOITATION-CHEZLEPRO" ]
notes = "Revue CPU RAM stockage et charge de l'infrastructure"
vars.instructions_url = "/life-noc/item/exploitation-chezlepro/revue-capacite-ressources"
assign where host.name == "life-noc"
}
@ -61,5 +66,6 @@ apply Service "exploitation-chezlepro-verification-jobs-automatises" {
vars.mock_message = "Sous contrôle"
groups = [ "EXPLOITATION-CHEZLEPRO" ]
notes = "Vérification des jobs automatisés et scripts périodiques"
vars.instructions_url = "/life-noc/item/exploitation-chezlepro/verification-jobs-automatises"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "finances-personnelles-revision-comptes-bancaires" {
vars.mock_message = "Sous contrôle"
groups = [ "FINANCES-PERSONNELLES" ]
notes = "Révision mensuelle des comptes bancaires personnels"
vars.instructions_url = "/life-noc/item/finances-personnelles/revision-comptes-bancaires"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "finances-personnelles-verification-cartes-credit" {
vars.mock_message = "Sous contrôle"
groups = [ "FINANCES-PERSONNELLES" ]
notes = "Vérification des soldes et transactions des cartes de crédit"
vars.instructions_url = "/life-noc/item/finances-personnelles/verification-cartes-credit"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "finances-personnelles-paiement-cartes-credit" {
vars.mock_message = "Sous contrôle"
groups = [ "FINANCES-PERSONNELLES" ]
notes = "Paiement des cartes de crédit personnelles"
vars.instructions_url = "/life-noc/item/finances-personnelles/paiement-cartes-credit"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "finances-personnelles-revision-budget-personnel" {
vars.mock_message = "Sous contrôle"
groups = [ "FINANCES-PERSONNELLES" ]
notes = "Révision mensuelle du budget personnel"
vars.instructions_url = "/life-noc/item/finances-personnelles/revision-budget-personnel"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "finances-personnelles-verification-prelevements-automatiques" {
vars.mock_message = "Sous contrôle"
groups = [ "FINANCES-PERSONNELLES" ]
notes = "Vérification des prélèvements automatiques personnels"
vars.instructions_url = "/life-noc/item/finances-personnelles/verification-prelevements-automatiques"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "finances-personnelles-verification-placements" {
vars.mock_message = "Sous contrôle"
groups = [ "FINANCES-PERSONNELLES" ]
notes = "Vérification périodique des placements et comptes enregistrés"
vars.instructions_url = "/life-noc/item/finances-personnelles/verification-placements"
assign where host.name == "life-noc"
}
@ -71,6 +77,7 @@ apply Service "finances-personnelles-verification-cotisations-reer" {
vars.mock_message = "Sous contrôle"
groups = [ "FINANCES-PERSONNELLES" ]
notes = "Vérification des cotisations REER"
vars.instructions_url = "/life-noc/item/finances-personnelles/verification-cotisations-reer"
assign where host.name == "life-noc"
}
@ -81,6 +88,7 @@ apply Service "finances-personnelles-verification-celi" {
vars.mock_message = "Sous contrôle"
groups = [ "FINANCES-PERSONNELLES" ]
notes = "Vérification de l'espace CELI et de son utilisation"
vars.instructions_url = "/life-noc/item/finances-personnelles/verification-celi"
assign where host.name == "life-noc"
}
@ -91,5 +99,6 @@ apply Service "finances-personnelles-preparation-dossier-financier-annuel" {
vars.mock_message = "Sous contrôle"
groups = [ "FINANCES-PERSONNELLES" ]
notes = "Préparer le dossier financier annuel personnel"
vars.instructions_url = "/life-noc/item/finances-personnelles/preparation-dossier-financier-annuel"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "fiscalite-personnelle-preparation-documents-impots" {
vars.mock_message = "Sous contrôle"
groups = [ "FISCALITE-PERSONNELLE" ]
notes = "Rassembler tous les documents fiscaux personnels"
vars.instructions_url = "/life-noc/item/fiscalite-personnelle/preparation-documents-impots"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "fiscalite-personnelle-production-impots-federal" {
vars.mock_message = "Sous contrôle"
groups = [ "FISCALITE-PERSONNELLE" ]
notes = "Produire la déclaration de revenus fédérale"
vars.instructions_url = "/life-noc/item/fiscalite-personnelle/production-impots-federal"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "fiscalite-personnelle-production-impots-quebec" {
vars.mock_message = "Sous contrôle"
groups = [ "FISCALITE-PERSONNELLE" ]
notes = "Produire la déclaration de revenus Québec"
vars.instructions_url = "/life-noc/item/fiscalite-personnelle/production-impots-quebec"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "fiscalite-personnelle-paiement-solde-impots" {
vars.mock_message = "Sous contrôle"
groups = [ "FISCALITE-PERSONNELLE" ]
notes = "Payer tout solde d'impôt exigible"
vars.instructions_url = "/life-noc/item/fiscalite-personnelle/paiement-solde-impots"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "fiscalite-personnelle-verification-avis-cotisation" {
vars.mock_message = "Sous contrôle"
groups = [ "FISCALITE-PERSONNELLE" ]
notes = "Vérifier les avis de cotisation ARC et Revenu Québec"
vars.instructions_url = "/life-noc/item/fiscalite-personnelle/verification-avis-cotisation"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "fiscalite-personnelle-archivage-documents-fiscaux" {
vars.mock_message = "Sous contrôle"
groups = [ "FISCALITE-PERSONNELLE" ]
notes = "Archiver les documents fiscaux personnels"
vars.instructions_url = "/life-noc/item/fiscalite-personnelle/archivage-documents-fiscaux"
assign where host.name == "life-noc"
}
@ -71,5 +77,6 @@ apply Service "fiscalite-personnelle-verification-acomptes-provisionnels" {
vars.mock_message = "Sous contrôle"
groups = [ "FISCALITE-PERSONNELLE" ]
notes = "Vérifier si des acomptes provisionnels sont requis"
vars.instructions_url = "/life-noc/item/fiscalite-personnelle/verification-acomptes-provisionnels"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "focus-entretien-processus-focus" {
vars.mock_message = "Sous contrôle"
groups = [ "FOCUS" ]
notes = "Vérifier que les cartouches FOCUS sont à jour"
vars.instructions_url = "/life-noc/item/focus/entretien-processus-focus"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "focus-nettoyage-inbox-mentale" {
vars.mock_message = "Sous contrôle"
groups = [ "FOCUS" ]
notes = "Vider et clarifier les éléments capturés hors système"
vars.instructions_url = "/life-noc/item/focus/nettoyage-inbox-mentale"
assign where host.name == "life-noc"
}
@ -31,5 +33,6 @@ apply Service "focus-revue-limitation-engagements" {
vars.mock_message = "Sous contrôle"
groups = [ "FOCUS" ]
notes = "Vérifier que les engagements actifs demeurent réalistes"
vars.instructions_url = "/life-noc/item/focus/revue-limitation-engagements"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "garage-et-rangement-inspection-rangement-plafond" {
vars.mock_message = "Sous contrôle"
groups = [ "GARAGE-ET-RANGEMENT" ]
notes = "Vérification des supports suspendus au plafond du garage"
vars.instructions_url = "/life-noc/item/garage-et-rangement/inspection-rangement-plafond"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "garage-et-rangement-revue-inventaire-garage" {
vars.mock_message = "Sous contrôle"
groups = [ "GARAGE-ET-RANGEMENT" ]
notes = "Revue de l'inventaire et de l'ordre du garage"
vars.instructions_url = "/life-noc/item/garage-et-rangement/revue-inventaire-garage"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "garage-et-rangement-verification-rouille-outillage" {
vars.mock_message = "Sous contrôle"
groups = [ "GARAGE-ET-RANGEMENT" ]
notes = "Vérification de la corrosion sur les outils et équipements"
vars.instructions_url = "/life-noc/item/garage-et-rangement/verification-rouille-outillage"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "garage-et-rangement-inspection-securite-garage" {
vars.mock_message = "Sous contrôle"
groups = [ "GARAGE-ET-RANGEMENT" ]
notes = "Vérification des conditions de sécurité du garage"
vars.instructions_url = "/life-noc/item/garage-et-rangement/inspection-securite-garage"
assign where host.name == "life-noc"
}
@ -51,5 +55,6 @@ apply Service "garage-et-rangement-rotation-stockage-bacs" {
vars.mock_message = "Sous contrôle"
groups = [ "GARAGE-ET-RANGEMENT" ]
notes = "Revue des contenus de bacs de rangement"
vars.instructions_url = "/life-noc/item/garage-et-rangement/rotation-stockage-bacs"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "informatique-personnelle-verification-sauvegardes-personnelles" {
vars.mock_message = "Sous contrôle"
groups = [ "INFORMATIQUE-PERSONNELLE" ]
notes = "Vérification des sauvegardes personnelles"
vars.instructions_url = "/life-noc/item/informatique-personnelle/verification-sauvegardes-personnelles"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "informatique-personnelle-nettoyage-stockage-personnel" {
vars.mock_message = "Sous contrôle"
groups = [ "INFORMATIQUE-PERSONNELLE" ]
notes = "Nettoyage du stockage numérique personnel"
vars.instructions_url = "/life-noc/item/informatique-personnelle/nettoyage-stockage-personnel"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "informatique-personnelle-verification-comptes-importants" {
vars.mock_message = "Sous contrôle"
groups = [ "INFORMATIQUE-PERSONNELLE" ]
notes = "Vérification des comptes numériques personnels importants"
vars.instructions_url = "/life-noc/item/informatique-personnelle/verification-comptes-importants"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "informatique-personnelle-revue-mots-de-passe-personnels" {
vars.mock_message = "Sous contrôle"
groups = [ "INFORMATIQUE-PERSONNELLE" ]
notes = "Revue des mots de passe personnels critiques"
vars.instructions_url = "/life-noc/item/informatique-personnelle/revue-mots-de-passe-personnels"
assign where host.name == "life-noc"
}
@ -51,5 +55,6 @@ apply Service "informatique-personnelle-verification-documents-cloud-personnels"
vars.mock_message = "Sous contrôle"
groups = [ "INFORMATIQUE-PERSONNELLE" ]
notes = "Vérifier la cohérence des documents personnels sauvegardés"
vars.instructions_url = "/life-noc/item/informatique-personnelle/verification-documents-cloud-personnels"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "infrastructure-chezlepro-verification-proxmox" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification générale de l'environnement Proxmox"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-proxmox"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "infrastructure-chezlepro-verification-ceph" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification générale de l'état Ceph"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-ceph"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "infrastructure-chezlepro-test-restauration-backups" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Test de restauration des sauvegardes"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/test-restauration-backups"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "infrastructure-chezlepro-verification-pbs-truenas" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification des solutions de sauvegarde et synchronisation"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-pbs-truenas"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "infrastructure-chezlepro-verification-ups-infrastructure" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification des UPS de l'infrastructure"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-ups-infrastructure"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "infrastructure-chezlepro-verification-capacite-stockage" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérifier capacité et croissance du stockage"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-capacite-stockage"
assign where host.name == "life-noc"
}
@ -71,6 +77,7 @@ apply Service "infrastructure-chezlepro-verification-apt-cacher-ng" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification du bon fonctionnement d'apt-cacher-ng"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-apt-cacher-ng"
assign where host.name == "life-noc"
}
@ -81,6 +88,7 @@ apply Service "infrastructure-chezlepro-verification-icinga2" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification de la supervision Icinga2"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-icinga2"
assign where host.name == "life-noc"
}
@ -91,6 +99,7 @@ apply Service "infrastructure-chezlepro-verification-keycloak-openldap" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification de l'identité et de l'authentification centralisées"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-keycloak-openldap"
assign where host.name == "life-noc"
}
@ -101,6 +110,7 @@ apply Service "infrastructure-chezlepro-verification-nextcloud" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification de l'état Nextcloud"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-nextcloud"
assign where host.name == "life-noc"
}
@ -111,6 +121,7 @@ apply Service "infrastructure-chezlepro-verification-mailcow" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification de l'infrastructure Mailcow"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-mailcow"
assign where host.name == "life-noc"
}
@ -121,6 +132,7 @@ apply Service "infrastructure-chezlepro-verification-openvas" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Vérification de la plateforme OpenVAS"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-openvas"
assign where host.name == "life-noc"
}
@ -131,5 +143,6 @@ apply Service "infrastructure-chezlepro-verification-journaux-systemes" {
vars.mock_message = "Sous contrôle"
groups = [ "INFRASTRUCTURE-CHEZLEPRO" ]
notes = "Revue des journaux systèmes critiques"
vars.instructions_url = "/life-noc/item/infrastructure-chezlepro/verification-journaux-systemes"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "jardin-inspection-vignes" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Inspection et entretien des vignes"
vars.instructions_url = "/life-noc/item/jardin/inspection-vignes"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "jardin-entretien-bleuetiers" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Entretien des bleuetiers"
vars.instructions_url = "/life-noc/item/jardin/entretien-bleuetiers"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "jardin-entretien-framboisiers" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Entretien des framboisiers"
vars.instructions_url = "/life-noc/item/jardin/entretien-framboisiers"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "jardin-inspection-kiwis-nordiques" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Vérification des kiwis nordiques"
vars.instructions_url = "/life-noc/item/jardin/inspection-kiwis-nordiques"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "jardin-entretien-rhubarbe" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Entretien de la rhubarbe"
vars.instructions_url = "/life-noc/item/jardin/entretien-rhubarbe"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "jardin-inspection-groseillier" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Inspection du groseillier"
vars.instructions_url = "/life-noc/item/jardin/inspection-groseillier"
assign where host.name == "life-noc"
}
@ -71,6 +77,7 @@ apply Service "jardin-inspection-muriers" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Inspection des mûriers"
vars.instructions_url = "/life-noc/item/jardin/inspection-muriers"
assign where host.name == "life-noc"
}
@ -81,6 +88,7 @@ apply Service "jardin-entretien-houblon" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Vérification et entretien du houblon"
vars.instructions_url = "/life-noc/item/jardin/entretien-houblon"
assign where host.name == "life-noc"
}
@ -91,6 +99,7 @@ apply Service "jardin-revision-pharmacopée-jardin" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Vérification du stock de soins pour végétaux"
vars.instructions_url = "/life-noc/item/jardin/revision-pharmacopée-jardin"
assign where host.name == "life-noc"
}
@ -101,5 +110,6 @@ apply Service "jardin-revision-armoire-produits-jardin" {
vars.mock_message = "Sous contrôle"
groups = [ "JARDIN" ]
notes = "Vérification et ordre de l'armoire des produits pour végétaux"
vars.instructions_url = "/life-noc/item/jardin/revision-armoire-produits-jardin"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "maison-remplacement-filtre-fournaise" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Remplacer le filtre de la fournaise"
vars.instructions_url = "/life-noc/item/maison/remplacement-filtre-fournaise"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "maison-inspection-fournaise" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Inspection générale de la fournaise avant saison froide"
vars.instructions_url = "/life-noc/item/maison/inspection-fournaise"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "maison-inspection-thermopompe" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Inspection et entretien de la thermopompe"
vars.instructions_url = "/life-noc/item/maison/inspection-thermopompe"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "maison-nettoyage-unites-exterieures" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Nettoyer les unités extérieures et vérifier le dégagement"
vars.instructions_url = "/life-noc/item/maison/nettoyage-unites-exterieures"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "maison-inspection-toiture" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Inspection annuelle de la toiture"
vars.instructions_url = "/life-noc/item/maison/inspection-toiture"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "maison-inspection-gouttieres" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Inspection et nettoyage des gouttières"
vars.instructions_url = "/life-noc/item/maison/inspection-gouttieres"
assign where host.name == "life-noc"
}
@ -71,6 +77,7 @@ apply Service "maison-inspection-fondation" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Inspection de la fondation et des fissures"
vars.instructions_url = "/life-noc/item/maison/inspection-fondation"
assign where host.name == "life-noc"
}
@ -81,6 +88,7 @@ apply Service "maison-verification-drainage-terrain" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Vérification du drainage autour de la maison"
vars.instructions_url = "/life-noc/item/maison/verification-drainage-terrain"
assign where host.name == "life-noc"
}
@ -91,6 +99,7 @@ apply Service "maison-test-detecteurs-fumee" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Test des détecteurs de fumée"
vars.instructions_url = "/life-noc/item/maison/test-detecteurs-fumee"
assign where host.name == "life-noc"
}
@ -101,6 +110,7 @@ apply Service "maison-test-detecteurs-co" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Test des détecteurs de monoxyde de carbone"
vars.instructions_url = "/life-noc/item/maison/test-detecteurs-co"
assign where host.name == "life-noc"
}
@ -111,6 +121,7 @@ apply Service "maison-remplacement-piles-detecteurs" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Remplacement préventif des piles des détecteurs"
vars.instructions_url = "/life-noc/item/maison/remplacement-piles-detecteurs"
assign where host.name == "life-noc"
}
@ -121,6 +132,7 @@ apply Service "maison-verification-plomberie-visible" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Vérification visuelle de la plomberie accessible"
vars.instructions_url = "/life-noc/item/maison/verification-plomberie-visible"
assign where host.name == "life-noc"
}
@ -131,6 +143,7 @@ apply Service "maison-inspection-sump-pump" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Vérification de la pompe de puisard si applicable"
vars.instructions_url = "/life-noc/item/maison/inspection-sump-pump"
assign where host.name == "life-noc"
}
@ -141,6 +154,7 @@ apply Service "maison-verification-calfetrage" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Vérification du calfeutrage portes et fenêtres"
vars.instructions_url = "/life-noc/item/maison/verification-calfetrage"
assign where host.name == "life-noc"
}
@ -151,6 +165,7 @@ apply Service "maison-inspection-portes-garage" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Vérification mécanique et sécurité des portes de garage"
vars.instructions_url = "/life-noc/item/maison/inspection-portes-garage"
assign where host.name == "life-noc"
}
@ -161,5 +176,6 @@ apply Service "maison-verification-extincteurs" {
vars.mock_message = "Sous contrôle"
groups = [ "MAISON" ]
notes = "Vérification des extincteurs"
vars.instructions_url = "/life-noc/item/maison/verification-extincteurs"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "obligations-chezlepro-verification-registraire-entreprise" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-CHEZLEPRO" ]
notes = "Vérifier les obligations au Registraire des entreprises"
vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-registraire-entreprise"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "obligations-chezlepro-verification-declarations-taxes" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-CHEZLEPRO" ]
notes = "Vérifier les obligations de taxes de l'entreprise si applicables"
vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-declarations-taxes"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "obligations-chezlepro-verification-dossier-comptable-entreprise"
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-CHEZLEPRO" ]
notes = "Vérifier l'ordre du dossier comptable Chezlepro"
vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-dossier-comptable-entreprise"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "obligations-chezlepro-verification-facturation-clients" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-CHEZLEPRO" ]
notes = "Vérifier la facturation et son suivi"
vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-facturation-clients"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "obligations-chezlepro-verification-paiements-fournisseurs" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-CHEZLEPRO" ]
notes = "Vérifier les paiements fournisseurs"
vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-paiements-fournisseurs"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "obligations-chezlepro-verification-contrats-ententes" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-CHEZLEPRO" ]
notes = "Vérifier contrats, ententes et obligations associées"
vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-contrats-ententes"
assign where host.name == "life-noc"
}
@ -71,6 +77,7 @@ apply Service "obligations-chezlepro-verification-assurances-entreprise" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-CHEZLEPRO" ]
notes = "Vérifier les assurances de l'entreprise"
vars.instructions_url = "/life-noc/item/obligations-chezlepro/verification-assurances-entreprise"
assign where host.name == "life-noc"
}
@ -81,5 +88,6 @@ apply Service "obligations-chezlepro-archivage-documents-entreprise" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-CHEZLEPRO" ]
notes = "Archiver les documents administratifs et financiers de l'entreprise"
vars.instructions_url = "/life-noc/item/obligations-chezlepro/archivage-documents-entreprise"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "obligations-legales-personnelles-verification-testament" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ]
notes = "Vérifier la pertinence et l'actualité du testament"
vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-testament"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "obligations-legales-personnelles-verification-mandat-inaptitude"
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ]
notes = "Vérifier le mandat de protection et documents connexes"
vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-mandat-inaptitude"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "obligations-legales-personnelles-verification-directives-medicale
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ]
notes = "Vérifier les directives médicales anticipées"
vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-directives-medicales"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "obligations-legales-personnelles-verification-papiers-identite" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ]
notes = "Vérifier l'expiration des papiers d'identité"
vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-papiers-identite"
assign where host.name == "life-noc"
}
@ -60,6 +64,7 @@ apply Service "obligations-legales-personnelles-renouvellement-permis-conduire"
vars.life_noc_threshold_critical_lt = "7"
vars.life_noc_on_error = "critical"
notes = "Vérifier la proximité de léchéance du permis"
vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/renouvellement-permis-conduire"
assign where host.name == "life-noc"
}
@ -70,6 +75,7 @@ apply Service "obligations-legales-personnelles-verification-carte-assurance-mal
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ]
notes = "Vérifier l'expiration de la carte d'assurance maladie"
vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-carte-assurance-maladie"
assign where host.name == "life-noc"
}
@ -80,5 +86,6 @@ apply Service "obligations-legales-personnelles-verification-passeport" {
vars.mock_message = "Sous contrôle"
groups = [ "OBLIGATIONS-LEGALES-PERSONNELLES" ]
notes = "Vérifier l'expiration du passeport"
vars.instructions_url = "/life-noc/item/obligations-legales-personnelles/verification-passeport"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "outils-et-equipements-entretien-outils-jardin" {
vars.mock_message = "Sous contrôle"
groups = [ "OUTILS-ET-EQUIPEMENTS" ]
notes = "Nettoyage et entretien des outils de jardin"
vars.instructions_url = "/life-noc/item/outils-et-equipements/entretien-outils-jardin"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "outils-et-equipements-inspection-outils-electriques" {
vars.mock_message = "Sous contrôle"
groups = [ "OUTILS-ET-EQUIPEMENTS" ]
notes = "Vérification des outils électriques"
vars.instructions_url = "/life-noc/item/outils-et-equipements/inspection-outils-electriques"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "outils-et-equipements-verification-rallonges-et-connecteurs" {
vars.mock_message = "Sous contrôle"
groups = [ "OUTILS-ET-EQUIPEMENTS" ]
notes = "Vérification des rallonges et connecteurs"
vars.instructions_url = "/life-noc/item/outils-et-equipements/verification-rallonges-et-connecteurs"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "outils-et-equipements-verification-compresseur-et-accessoires" {
vars.mock_message = "Sous contrôle"
groups = [ "OUTILS-ET-EQUIPEMENTS" ]
notes = "Vérification du compresseur et accessoires"
vars.instructions_url = "/life-noc/item/outils-et-equipements/verification-compresseur-et-accessoires"
assign where host.name == "life-noc"
}
@ -51,5 +55,6 @@ apply Service "outils-et-equipements-revision-inventaire-outillage" {
vars.mock_message = "Sous contrôle"
groups = [ "OUTILS-ET-EQUIPEMENTS" ]
notes = "Revue de l'inventaire d'outillage"
vars.instructions_url = "/life-noc/item/outils-et-equipements/revision-inventaire-outillage"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "projets-revision-life-noc" {
vars.mock_message = "Sous contrôle"
groups = [ "PROJETS" ]
notes = "Révision du projet Life-NOC"
vars.instructions_url = "/life-noc/item/projets/revision-life-noc"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "projets-revision-alliance-boreale" {
vars.mock_message = "Sous contrôle"
groups = [ "PROJETS" ]
notes = "Révision du projet Alliance Boréale"
vars.instructions_url = "/life-noc/item/projets/revision-alliance-boreale"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "projets-revision-erplibre" {
vars.mock_message = "Sous contrôle"
groups = [ "PROJETS" ]
notes = "Révision du projet ERPLibre"
vars.instructions_url = "/life-noc/item/projets/revision-erplibre"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "projets-revision-semence-numerique" {
vars.mock_message = "Sous contrôle"
groups = [ "PROJETS" ]
notes = "Révision du projet semence numérique"
vars.instructions_url = "/life-noc/item/projets/revision-semence-numerique"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "projets-revision-ortrux-1" {
vars.mock_message = "Sous contrôle"
groups = [ "PROJETS" ]
notes = "Révision du système Ortrux-1"
vars.instructions_url = "/life-noc/item/projets/revision-ortrux-1"
assign where host.name == "life-noc"
}
@ -61,5 +66,6 @@ apply Service "projets-revision-district16" {
vars.mock_message = "Sous contrôle"
groups = [ "PROJETS" ]
notes = "Révision du projet DISTRICT16"
vars.instructions_url = "/life-noc/item/projets/revision-district16"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "reseau-chezlepro-verification-firewalls" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Vérification des firewalls"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-firewalls"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "reseau-chezlepro-verification-vpn-openvpn" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Vérification des tunnels OpenVPN"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-vpn-openvpn"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "reseau-chezlepro-verification-crl-certificats" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Vérification de la CRL et des certificats"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-crl-certificats"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "reseau-chezlepro-verification-switches" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Vérification des switches et uplinks"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-switches"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "reseau-chezlepro-verification-vlans-segmentation" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Vérification de la segmentation réseau"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-vlans-segmentation"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "reseau-chezlepro-verification-dns" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Vérification DNS interne et externe"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-dns"
assign where host.name == "life-noc"
}
@ -71,6 +77,7 @@ apply Service "reseau-chezlepro-verification-dynamic-dns" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Vérification du DNS dynamique si utilisé"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-dynamic-dns"
assign where host.name == "life-noc"
}
@ -81,6 +88,7 @@ apply Service "reseau-chezlepro-audit-regles-firewall" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Audit périodique des règles de firewall"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/audit-regles-firewall"
assign where host.name == "life-noc"
}
@ -91,6 +99,7 @@ apply Service "reseau-chezlepro-verification-geoblocking" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Vérifier le geoblocking et son effet attendu"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-geoblocking"
assign where host.name == "life-noc"
}
@ -101,5 +110,6 @@ apply Service "reseau-chezlepro-verification-certificats-publics" {
vars.mock_message = "Sous contrôle"
groups = [ "RESEAU-CHEZLEPRO" ]
notes = "Vérifier les expirations de certificats publics"
vars.instructions_url = "/life-noc/item/reseau-chezlepro/verification-certificats-publics"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "resilience-test-generatrice-propane" {
vars.mock_message = "Sous contrôle"
groups = [ "RESILIENCE" ]
notes = "Test mensuel de la génératrice au propane"
vars.instructions_url = "/life-noc/item/resilience/test-generatrice-propane"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "resilience-verification-carburant-generatrice" {
vars.mock_message = "Sous contrôle"
groups = [ "RESILIENCE" ]
notes = "Vérification de l'approvisionnement propane lié à la génératrice"
vars.instructions_url = "/life-noc/item/resilience/verification-carburant-generatrice"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "resilience-verification-procedure-bascule" {
vars.mock_message = "Sous contrôle"
groups = [ "RESILIENCE" ]
notes = "Réviser la procédure de bascule en mode secours"
vars.instructions_url = "/life-noc/item/resilience/verification-procedure-bascule"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "resilience-verification-stock-lampes-piles" {
vars.mock_message = "Sous contrôle"
groups = [ "RESILIENCE" ]
notes = "Vérification des lampes, piles et éclairage d'urgence"
vars.instructions_url = "/life-noc/item/resilience/verification-stock-lampes-piles"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "resilience-verification-trousses-urgence" {
vars.mock_message = "Sous contrôle"
groups = [ "RESILIENCE" ]
notes = "Vérification des trousses d'urgence"
vars.instructions_url = "/life-noc/item/resilience/verification-trousses-urgence"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "resilience-revision-plan-urgence" {
vars.mock_message = "Sous contrôle"
groups = [ "RESILIENCE" ]
notes = "Réviser le plan d'urgence domestique"
vars.instructions_url = "/life-noc/item/resilience/revision-plan-urgence"
assign where host.name == "life-noc"
}
@ -71,6 +77,7 @@ apply Service "resilience-test-autonomie-base" {
vars.mock_message = "Sous contrôle"
groups = [ "RESILIENCE" ]
notes = "Vérifier l'autonomie énergétique minimale attendue"
vars.instructions_url = "/life-noc/item/resilience/test-autonomie-base"
assign where host.name == "life-noc"
}
@ -81,5 +88,6 @@ apply Service "resilience-verification-moyens-cuisson-secours" {
vars.mock_message = "Sous contrôle"
groups = [ "RESILIENCE" ]
notes = "Vérification des moyens de cuisson de secours"
vars.instructions_url = "/life-noc/item/resilience/verification-moyens-cuisson-secours"
assign where host.name == "life-noc"
}

View file

@ -20,6 +20,7 @@ apply Service "revue-revue-quotidienne-life-noc" {
vars.life_noc_threshold_critical_gte = "3"
vars.life_noc_on_error = "critical"
notes = "Vérifier le tableau de bord Life-NOC"
vars.instructions_url = "/life-noc/item/revue/revue-quotidienne-life-noc"
assign where host.name == "life-noc"
}
@ -39,6 +40,7 @@ apply Service "revue-revue-hebdomadaire-priorites" {
vars.life_noc_threshold_critical_gte = "10"
vars.life_noc_on_error = "critical"
notes = "Revoir les priorités de la semaine"
vars.instructions_url = "https://chezlepro.ca/life-noc/test-instructions"
assign where host.name == "life-noc"
}
@ -49,6 +51,7 @@ apply Service "revue-revue-mensuelle-systeme-vie" {
vars.mock_message = "Sous contrôle"
groups = [ "REVUE" ]
notes = "Revue mensuelle de l'ensemble du système Life-NOC"
vars.instructions_url = "/life-noc/item/revue/revue-mensuelle-systeme-vie"
assign where host.name == "life-noc"
}
@ -59,5 +62,6 @@ apply Service "revue-revue-trimestrielle-orientation" {
vars.mock_message = "Sous contrôle"
groups = [ "REVUE" ]
notes = "Revue trimestrielle des orientations de vie et de Chezlepro"
vars.instructions_url = "/life-noc/item/revue/revue-trimestrielle-orientation"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "sante-verification-trousse-premiers-soins" {
vars.mock_message = "Sous contrôle"
groups = [ "SANTE" ]
notes = "Vérification de la trousse de premiers soins"
vars.instructions_url = "/life-noc/item/sante/verification-trousse-premiers-soins"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "sante-verification-expiration-medicaments" {
vars.mock_message = "Sous contrôle"
groups = [ "SANTE" ]
notes = "Vérification des dates d'expiration des médicaments"
vars.instructions_url = "/life-noc/item/sante/verification-expiration-medicaments"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "sante-revision-rendez-vous-medicaux" {
vars.mock_message = "Sous contrôle"
groups = [ "SANTE" ]
notes = "Revoir les rendez-vous médicaux requis"
vars.instructions_url = "/life-noc/item/sante/revision-rendez-vous-medicaux"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "sante-verification-lunettes-prescriptions" {
vars.mock_message = "Sous contrôle"
groups = [ "SANTE" ]
notes = "Vérifier les prescriptions et besoins de renouvellement"
vars.instructions_url = "/life-noc/item/sante/verification-lunettes-prescriptions"
assign where host.name == "life-noc"
}
@ -51,5 +55,6 @@ apply Service "sante-verification-dossier-sante" {
vars.mock_message = "Sous contrôle"
groups = [ "SANTE" ]
notes = "Vérifier l'ordre et l'accessibilité des documents de santé"
vars.instructions_url = "/life-noc/item/sante/verification-dossier-sante"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "securite-chezlepro-verification-mises-a-jour-securite" {
vars.mock_message = "Sous contrôle"
groups = [ "SECURITE-CHEZLEPRO" ]
notes = "Vérification des mises à jour de sécurité"
vars.instructions_url = "/life-noc/item/securite-chezlepro/verification-mises-a-jour-securite"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "securite-chezlepro-audit-comptes-acces" {
vars.mock_message = "Sous contrôle"
groups = [ "SECURITE-CHEZLEPRO" ]
notes = "Audit des comptes et accès privilégiés"
vars.instructions_url = "/life-noc/item/securite-chezlepro/audit-comptes-acces"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "securite-chezlepro-verification-ids-snort" {
vars.mock_message = "Sous contrôle"
groups = [ "SECURITE-CHEZLEPRO" ]
notes = "Vérification des IDS et de leur état"
vars.instructions_url = "/life-noc/item/securite-chezlepro/verification-ids-snort"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "securite-chezlepro-revue-pki-privee" {
vars.mock_message = "Sous contrôle"
groups = [ "SECURITE-CHEZLEPRO" ]
notes = "Revue de la PKI privée et des autorités intermédiaires"
vars.instructions_url = "/life-noc/item/securite-chezlepro/revue-pki-privee"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "securite-chezlepro-revue-politiques-securite" {
vars.mock_message = "Sous contrôle"
groups = [ "SECURITE-CHEZLEPRO" ]
notes = "Revue des politiques et pratiques de sécurité numérique"
vars.instructions_url = "/life-noc/item/securite-chezlepro/revue-politiques-securite"
assign where host.name == "life-noc"
}
@ -61,5 +66,6 @@ apply Service "securite-chezlepro-verification-sauvegardes-configuration" {
vars.mock_message = "Sous contrôle"
groups = [ "SECURITE-CHEZLEPRO" ]
notes = "Vérifier les sauvegardes des configurations critiques"
vars.instructions_url = "/life-noc/item/securite-chezlepro/verification-sauvegardes-configuration"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "stock-alimentaire-rotation-nourriture-seche" {
vars.mock_message = "Sous contrôle"
groups = [ "STOCK-ALIMENTAIRE" ]
notes = "Rotation des stocks de nourriture sèche"
vars.instructions_url = "/life-noc/item/stock-alimentaire/rotation-nourriture-seche"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "stock-alimentaire-verification-reserve-eau" {
vars.mock_message = "Sous contrôle"
groups = [ "STOCK-ALIMENTAIRE" ]
notes = "Vérification de la réserve d'eau potable"
vars.instructions_url = "/life-noc/item/stock-alimentaire/verification-reserve-eau"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "stock-alimentaire-verification-mylar-absorbeurs" {
vars.mock_message = "Sous contrôle"
groups = [ "STOCK-ALIMENTAIRE" ]
notes = "Vérifier l'intégrité des emballages Mylar et absorbeurs"
vars.instructions_url = "/life-noc/item/stock-alimentaire/verification-mylar-absorbeurs"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "stock-alimentaire-revue-inventaire-conserves" {
vars.mock_message = "Sous contrôle"
groups = [ "STOCK-ALIMENTAIRE" ]
notes = "Revue des conserves et dates de rotation"
vars.instructions_url = "/life-noc/item/stock-alimentaire/revue-inventaire-conserves"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "stock-alimentaire-revue-inventaire-farine-riz-pates" {
vars.mock_message = "Sous contrôle"
groups = [ "STOCK-ALIMENTAIRE" ]
notes = "Vérification des gros stocks alimentaires de base"
vars.instructions_url = "/life-noc/item/stock-alimentaire/revue-inventaire-farine-riz-pates"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "stock-alimentaire-verification-bacs-legumes-racines" {
vars.mock_message = "Sous contrôle"
groups = [ "STOCK-ALIMENTAIRE" ]
notes = "Vérification des bacs de stockage de légumes racines"
vars.instructions_url = "/life-noc/item/stock-alimentaire/verification-bacs-legumes-racines"
assign where host.name == "life-noc"
}
@ -71,5 +77,6 @@ apply Service "stock-alimentaire-revue-supplements-vitamines" {
vars.mock_message = "Sous contrôle"
groups = [ "STOCK-ALIMENTAIRE" ]
notes = "Vérification du stock de suppléments et dates utiles"
vars.instructions_url = "/life-noc/item/stock-alimentaire/revue-supplements-vitamines"
assign where host.name == "life-noc"
}

View file

@ -11,6 +11,7 @@ apply Service "voiture-verification-huile-moteur" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Vérification de l'huile moteur"
vars.instructions_url = "/life-noc/item/voiture/verification-huile-moteur"
assign where host.name == "life-noc"
}
@ -21,6 +22,7 @@ apply Service "voiture-inspection-freins" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Inspection des freins"
vars.instructions_url = "/life-noc/item/voiture/inspection-freins"
assign where host.name == "life-noc"
}
@ -31,6 +33,7 @@ apply Service "voiture-verification-pneus" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Vérification usure, pression et permutation des pneus"
vars.instructions_url = "/life-noc/item/voiture/verification-pneus"
assign where host.name == "life-noc"
}
@ -41,6 +44,7 @@ apply Service "voiture-ajustement-valves" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Vérification et ajustement des valves Honda Accord"
vars.instructions_url = "/life-noc/item/voiture/ajustement-valves"
assign where host.name == "life-noc"
}
@ -51,6 +55,7 @@ apply Service "voiture-verification-timing-belt" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Vérification de la courroie de distribution et pompe à eau"
vars.instructions_url = "/life-noc/item/voiture/verification-timing-belt"
assign where host.name == "life-noc"
}
@ -61,6 +66,7 @@ apply Service "voiture-verification-batterie-voiture" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Vérification de l'état de la batterie automobile"
vars.instructions_url = "/life-noc/item/voiture/verification-batterie-voiture"
assign where host.name == "life-noc"
}
@ -71,6 +77,7 @@ apply Service "voiture-verification-balais-essuie-glace" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Vérification des essuie-glaces"
vars.instructions_url = "/life-noc/item/voiture/verification-balais-essuie-glace"
assign where host.name == "life-noc"
}
@ -81,6 +88,7 @@ apply Service "voiture-verification-liquides" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Vérification des liquides de la voiture"
vars.instructions_url = "/life-noc/item/voiture/verification-liquides"
assign where host.name == "life-noc"
}
@ -91,6 +99,7 @@ apply Service "voiture-verification-eclairage" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Vérification de l'éclairage et des phares"
vars.instructions_url = "/life-noc/item/voiture/verification-eclairage"
assign where host.name == "life-noc"
}
@ -101,5 +110,6 @@ apply Service "voiture-verification-immatriculation-assurance" {
vars.mock_message = "Sous contrôle"
groups = [ "VOITURE" ]
notes = "Vérification papiers d'assurance et immatriculation"
vars.instructions_url = "/life-noc/item/voiture/verification-immatriculation-assurance"
assign where host.name == "life-noc"
}

View file

@ -5,6 +5,7 @@ from pathlib import Path
import yaml
INPUTS_DIR = Path("data/inputs")
DOMAINS_FILE = Path("domains.yaml")
def utc_now_iso() -> str:
@ -68,3 +69,76 @@ def set_item(domain: str, item_key: str, value: str, origin: str = "manual") ->
def complete_item(domain: str, item_key: str, origin: str = "manual") -> dict:
return set_item(domain, item_key, utc_today_date(), origin=origin)
def load_domains_definition() -> dict:
if not DOMAINS_FILE.exists():
raise ValueError(f"Fichier introuvable: {DOMAINS_FILE}")
data = yaml.safe_load(DOMAINS_FILE.read_text(encoding="utf-8"))
if not isinstance(data, dict):
raise ValueError("domains.yaml invalide")
domains = data.get("domains")
if not isinstance(domains, dict):
raise ValueError("domains.yaml invalide: clé 'domains' absente ou invalide")
return domains
def get_defined_service(domain: str, item_key: str) -> dict:
domains = load_domains_definition()
items = domains.get(domain)
if not isinstance(items, list):
raise KeyError(domain)
for item in items:
if isinstance(item, dict) and item.get("name") == item_key:
return item
raise KeyError(item_key)
def compute_state_for_item(domain: str, item_key: str) -> str:
service = get_defined_service(domain, item_key)
probe = service.get("probe")
if not isinstance(probe, dict):
return "MOCK"
probe_type = str(probe.get("type", "")).strip()
source = probe.get("source", {})
thresholds = probe.get("thresholds", {})
metric = probe.get("metric", {})
if str(source.get("type", "")).strip() != "manual_date":
return "CRITICAL"
if str(metric.get("unit", "")).strip() != "days":
return "CRITICAL"
item = get_item(domain, item_key)
value = str(item.get("value", "")).strip()
dt = datetime.strptime(value, "%Y-%m-%d").replace(tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
if probe_type == "elapsed_time":
days = (now - dt).total_seconds() / 86400.0
if "critical_gte" in thresholds and days >= float(thresholds["critical_gte"]):
return "CRITICAL"
if "warning_gte" in thresholds and days >= float(thresholds["warning_gte"]):
return "WARNING"
if "ok_gte" in thresholds and days >= float(thresholds["ok_gte"]):
return "OK"
if "unknown_lt" in thresholds and days < float(thresholds["unknown_lt"]):
return "UNKNOWN"
return "CRITICAL"
if probe_type == "days_until_due":
days = (dt - now).total_seconds() / 86400.0
if "critical_lt" in thresholds and days < float(thresholds["critical_lt"]):
return "CRITICAL"
if "warning_lt" in thresholds and days < float(thresholds["warning_lt"]):
return "WARNING"
if "ok_lt" in thresholds and days < float(thresholds["ok_lt"]):
return "OK"
if "unknown_gte" in thresholds and days >= float(thresholds["unknown_gte"]):
return "UNKNOWN"
return "CRITICAL"
return "CRITICAL"

View file

@ -1,260 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="${1:-.}"
need_file() {
local f="$1"
if [[ ! -f "$ROOT/$f" ]]; then
echo "Fichier introuvable: $f" >&2
exit 1
fi
}
echo "==> Vérification du dépôt"
need_file "domains.yaml"
need_file "scripts/generate_bpm.py"
need_file "Makefile"
need_file "ansible/roles/life_noc/defaults/main.yml"
need_file "ansible/roles/life_noc/tasks/main.yml"
echo "==> Sauvegarde minimale"
mkdir -p "$ROOT/.patch-backup"
cp -a "$ROOT/scripts/generate_bpm.py" "$ROOT/.patch-backup/generate_bpm.py.bak"
cp -a "$ROOT/Makefile" "$ROOT/.patch-backup/Makefile.bak"
cp -a "$ROOT/ansible/roles/life_noc/defaults/main.yml" "$ROOT/.patch-backup/life_noc_defaults_main.yml.bak"
cp -a "$ROOT/ansible/roles/life_noc/tasks/main.yml" "$ROOT/.patch-backup/life_noc_tasks_main.yml.bak"
[[ -f "$ROOT/preuve-de-reproductibilite.md" ]] && cp -a "$ROOT/preuve-de-reproductibilite.md" "$ROOT/.patch-backup/preuve-de-reproductibilite.md.bak"
[[ -f "$ROOT/decisions-techniques.md" ]] && cp -a "$ROOT/decisions-techniques.md" "$ROOT/.patch-backup/decisions-techniques.md.bak"
echo "==> Réécriture de scripts/generate_bpm.py"
cat > "$ROOT/scripts/generate_bpm.py" <<'PYEOF'
#!/usr/bin/env python3
from __future__ import annotations
import re
from pathlib import Path
import sys
import yaml
ROOT = Path(__file__).resolve().parents[1]
DOMAINS_YAML = ROOT / "domains.yaml"
OUTPUT_DIR = ROOT / "bpm"
OUTPUT_FILE = OUTPUT_DIR / "Life-NOC.conf"
HOST_NAME = "life-noc"
TITLE = "Life-NOC"
OWNER = "icingadmin"
def slugify(value: str) -> str:
value = value.strip().lower()
replacements = {
"à": "a", "â": "a", "ä": "a",
"ç": "c",
"é": "e", "è": "e", "ê": "e", "ë": "e",
"î": "i", "ï": "i",
"ô": "o", "ö": "o",
"ù": "u", "û": "u", "ü": "u",
"ÿ": "y",
"œ": "oe", "æ": "ae",
"'": "", "": "",
}
for old, new in replacements.items():
value = value.replace(old, new)
value = re.sub(r"[^a-z0-9\-]+", "-", value)
value = re.sub(r"-{2,}", "-", value)
return value.strip("-")
def aliasify(value: str) -> str:
value = value.strip().upper()
replacements = {
"À": "A", "Â": "A", "Ä": "A",
"Ç": "C",
"É": "E", "È": "E", "Ê": "E", "Ë": "E",
"Î": "I", "Ï": "I",
"Ô": "O", "Ö": "O",
"Ù": "U", "Û": "U", "Ü": "U",
"Ÿ": "Y",
"Œ": "OE", "Æ": "AE",
"'": "", "": "",
}
for old, new in replacements.items():
value = value.replace(old, new)
value = re.sub(r"[^A-Z0-9\-]+", "-", value)
value = re.sub(r"-{2,}", "-", value)
return value.strip("-")
def read_domains() -> dict:
data = yaml.safe_load(DOMAINS_YAML.read_text(encoding="utf-8"))
if not isinstance(data, dict) or "domains" not in data:
raise ValueError("domains.yaml doit contenir une clé racine 'domains'")
domains = data["domains"]
if not isinstance(domains, dict):
raise ValueError("domains.yaml: 'domains' doit être un mapping")
return domains
def collect_services(domain_data: dict, domain_name: str) -> list[str]:
items = domain_data.get("items", [])
if not isinstance(items, list):
raise ValueError(f"Domaine '{domain_name}': 'items' doit être une liste")
services: list[str] = []
for item in items:
if not isinstance(item, dict):
raise ValueError(f"Domaine '{domain_name}': item invalide (dict attendu)")
item_name = item.get("name")
if not item_name:
raise ValueError(f"Domaine '{domain_name}': item sans champ 'name'")
service_name = f"{slugify(domain_name)}-{slugify(str(item_name))}"
services.append(f"{HOST_NAME};{service_name}")
if not services:
raise ValueError(f"Domaine '{domain_name}' ne contient aucun item")
return services
def header() -> str:
return f"""### Business Process Config File ###
#
# Title : {TITLE}
# Description :
# Owner : {OWNER}
# AddToMenu : yes
# Backend :
# Statetype : soft
#
###################################
"""
def main() -> int:
domains = read_domains()
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
lines: list[str] = [header()]
aliases: list[tuple[str, str]] = []
for domain_name, domain_data in domains.items():
if not isinstance(domain_data, dict):
raise ValueError(f"Domaine '{domain_name}': structure invalide")
alias = aliasify(domain_name)
label = domain_data.get("label", domain_name)
services = collect_services(domain_data, domain_name)
expr = " & ".join(services)
lines.append(f"{alias} = {expr}\n")
aliases.append((alias, str(label)))
lines.append("\n")
root_expr = " & ".join(alias for alias, _label in aliases)
lines.append(f"LIFE-NOC = {root_expr}\n")
lines.append("display 1;LIFE-NOC;LIFE-NOC\n")
for alias, label in aliases:
lines.append(f"display 1;{alias};{label}\n")
OUTPUT_FILE.write_text("".join(lines), encoding="utf-8")
print(f"BPM généré: {OUTPUT_FILE.relative_to(ROOT)}")
print("Processus générés :")
print(" - LIFE-NOC")
for alias, _label in aliases:
print(f" - {alias}")
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except Exception as exc:
print(f"ERREUR: {exc}", file=sys.stderr)
raise
PYEOF
chmod +x "$ROOT/scripts/generate_bpm.py"
echo "==> Ajustements texte ciblés"
python3 - "$ROOT" <<'PYEOF'
from pathlib import Path
import re
import sys
root = Path(sys.argv[1])
def replace_text(path_str, replacements):
path = root / path_str
if not path.exists():
return
text = path.read_text(encoding="utf-8")
original = text
for old, new in replacements:
text = text.replace(old, new)
if text != original:
path.write_text(text, encoding="utf-8")
print(f"Modifié: {path_str}")
def regex_replace(path_str, pattern, repl, must_match=False):
path = root / path_str
if not path.exists():
return
text = path.read_text(encoding="utf-8")
new_text, count = re.subn(pattern, repl, text, flags=re.MULTILINE)
if must_match and count == 0:
raise SystemExit(f"Aucun match dans {path_str} pour: {pattern}")
if new_text != text:
path.write_text(new_text, encoding="utf-8")
print(f"Modifié: {path_str}")
# Makefile
replace_text("Makefile", [
("bpm/life-noc.json", "bpm/Life-NOC.conf"),
])
# defaults main.yml
replace_text("ansible/roles/life_noc/defaults/main.yml", [
("/etc/icingaweb2/modules/businessprocess/processes/life-noc.json",
"/etc/icingaweb2/modules/businessprocess/processes/Life-NOC.conf"),
])
# tasks main.yml
replace_text("ansible/roles/life_noc/tasks/main.yml", [
("bpm/life-noc.json", "bpm/Life-NOC.conf"),
("life-noc.json", "Life-NOC.conf"),
])
# docs
for doc in ["preuve-de-reproductibilite.md", "decisions-techniques.md"]:
replace_text(doc, [
("bpm/life-noc.json", "bpm/Life-NOC.conf"),
("life-noc.json", "Life-NOC.conf"),
("export JSON", "export natif Business Process"),
("artefact JSON", "artefact natif Business Process"),
])
PYEOF
echo "==> Nettoyage de l'ancien artefact si présent"
rm -f "$ROOT/bpm/life-noc.json"
echo "==> Génération du nouveau BPM"
(
cd "$ROOT"
python3 scripts/generate_bpm.py
)
echo "==> Vérification rapide"
if [[ -f "$ROOT/bpm/Life-NOC.conf" ]]; then
echo "OK: bpm/Life-NOC.conf généré"
else
echo "ERREUR: bpm/Life-NOC.conf absent" >&2
exit 1
fi
echo
echo "Patch terminé."
echo "Fichiers sauvegardés dans: $ROOT/.patch-backup"
echo "Prochaine étape recommandée:"
echo " make check"
echo "Puis redéploiement avec BPM activé."

View file

@ -1,283 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="${1:-.}"
need_file() {
local f="$1"
if [[ ! -f "$ROOT/$f" ]]; then
echo "Fichier introuvable: $f" >&2
exit 1
fi
}
echo "==> Vérification du dépôt"
need_file "checks/check_life_noc_probe.py"
need_file "icinga/commands/check_life_noc_probe.conf"
need_file "scripts/generate_services.py"
need_file "domains.yaml"
echo "==> Sauvegarde"
mkdir -p "$ROOT/.patch-backup-days-until-due"
cp -a "$ROOT/checks/check_life_noc_probe.py" "$ROOT/.patch-backup-days-until-due/check_life_noc_probe.py.bak"
cp -a "$ROOT/icinga/commands/check_life_noc_probe.conf" "$ROOT/.patch-backup-days-until-due/check_life_noc_probe.conf.bak"
cp -a "$ROOT/scripts/generate_services.py" "$ROOT/.patch-backup-days-until-due/generate_services.py.bak"
cp -a "$ROOT/domains.yaml" "$ROOT/.patch-backup-days-until-due/domains.yaml.bak"
echo "==> Réécriture de checks/check_life_noc_probe.py"
cat > "$ROOT/checks/check_life_noc_probe.py" <<'PYEOF'
#!/usr/bin/env python3
import argparse
from datetime import datetime, timezone
from pathlib import Path
import yaml
def exit_with(code: int, text: str) -> None:
print(text)
raise SystemExit(code)
def parse_date(value: str) -> datetime:
return datetime.strptime(value, "%Y-%m-%d").replace(tzinfo=timezone.utc)
def load_input_value(inputs_file: str, item_key: str) -> str:
path = Path(inputs_file)
if not path.exists():
raise ValueError(f"inputs file introuvable: {inputs_file}")
data = yaml.safe_load(path.read_text(encoding="utf-8"))
if not isinstance(data, dict):
raise ValueError(f"inputs file invalide: {inputs_file}")
if item_key not in data:
raise ValueError(f"item_key introuvable dans le store: {item_key}")
item = data[item_key]
if not isinstance(item, dict):
raise ValueError(f"entrée invalide pour item_key: {item_key}")
if "value" not in item:
raise ValueError(f"champ 'value' absent pour item_key: {item_key}")
return str(item["value"]).strip()
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--probe-type", required=True)
ap.add_argument("--source-type", required=True)
ap.add_argument("--source-value")
ap.add_argument("--inputs-file")
ap.add_argument("--item-key")
ap.add_argument("--unit", required=True)
ap.add_argument("--unknown-lt", type=float)
ap.add_argument("--unknown-gte", type=float)
ap.add_argument("--ok-gte", type=float)
ap.add_argument("--ok-lt", type=float)
ap.add_argument("--warning-gte", type=float)
ap.add_argument("--warning-lt", type=float)
ap.add_argument("--critical-gte", type=float)
ap.add_argument("--critical-lt", type=float)
ap.add_argument("--on-error", default="critical")
ap.add_argument("--label", default="life-noc")
args = ap.parse_args()
try:
if args.source_type != "manual_date":
exit_with(2, f"CRITICAL - source_type non supporté: {args.source_type}")
if args.unit != "days":
exit_with(2, f"CRITICAL - unité non supportée: {args.unit}")
if args.inputs_file and args.item_key:
source_value = load_input_value(args.inputs_file, args.item_key)
elif args.source_value:
source_value = args.source_value
else:
raise ValueError("aucune source fournie: inputs-file/item-key ou source-value requis")
dt = parse_date(source_value)
now = datetime.now(timezone.utc)
if args.probe_type == "elapsed_time":
metric = (now - dt).total_seconds() / 86400.0
if args.critical_gte is not None and metric >= args.critical_gte:
exit_with(2, f"CRITICAL - {args.label}: {metric:.1f} jours")
if args.warning_gte is not None and metric >= args.warning_gte:
exit_with(1, f"WARNING - {args.label}: {metric:.1f} jours")
if args.ok_gte is not None and metric >= args.ok_gte:
exit_with(0, f"OK - {args.label}: {metric:.1f} jours")
if args.unknown_lt is not None and metric < args.unknown_lt:
exit_with(3, f"UNKNOWN - {args.label}: {metric:.1f} jours")
exit_with(2, f"CRITICAL - {args.label}: seuils incohérents")
elif args.probe_type == "days_until_due":
metric = (dt - now).total_seconds() / 86400.0
if args.critical_lt is not None and metric < args.critical_lt:
exit_with(2, f"CRITICAL - {args.label}: {metric:.1f} jours restants")
if args.warning_lt is not None and metric < args.warning_lt:
exit_with(1, f"WARNING - {args.label}: {metric:.1f} jours restants")
if args.ok_lt is not None and metric < args.ok_lt:
exit_with(0, f"OK - {args.label}: {metric:.1f} jours restants")
if args.unknown_gte is not None and metric >= args.unknown_gte:
exit_with(3, f"UNKNOWN - {args.label}: {metric:.1f} jours restants")
exit_with(2, f"CRITICAL - {args.label}: seuils incohérents")
else:
exit_with(2, f"CRITICAL - probe_type non supporté: {args.probe_type}")
except Exception as exc:
if args.on_error == "critical":
exit_with(2, f"CRITICAL - erreur sonde: {exc}")
exit_with(3, f"UNKNOWN - erreur sonde: {exc}")
if __name__ == "__main__":
raise SystemExit(main())
PYEOF
chmod +x "$ROOT/checks/check_life_noc_probe.py"
echo "==> Réécriture de icinga/commands/check_life_noc_probe.conf"
cat > "$ROOT/icinga/commands/check_life_noc_probe.conf" <<'CONFEOF'
object CheckCommand "check_life_noc_probe" {
command = [ PluginDir + "/check_life_noc_probe.py" ]
arguments = {
"--probe-type" = "$life_noc_probe_type$"
"--source-type" = "$life_noc_source_type$"
"--source-value" = "$life_noc_source_value$"
"--inputs-file" = "$life_noc_inputs_file$"
"--item-key" = "$life_noc_item_key$"
"--unit" = "$life_noc_metric_unit$"
"--unknown-lt" = "$life_noc_threshold_unknown_lt$"
"--unknown-gte" = "$life_noc_threshold_unknown_gte$"
"--ok-gte" = "$life_noc_threshold_ok_gte$"
"--ok-lt" = "$life_noc_threshold_ok_lt$"
"--warning-gte" = "$life_noc_threshold_warning_gte$"
"--warning-lt" = "$life_noc_threshold_warning_lt$"
"--critical-gte" = "$life_noc_threshold_critical_gte$"
"--critical-lt" = "$life_noc_threshold_critical_lt$"
"--on-error" = "$life_noc_on_error$"
"--label" = "$life_noc_label$"
}
}
CONFEOF
echo "==> Patch de scripts/generate_services.py"
python3 - "$ROOT/scripts/generate_services.py" <<'PYEOF'
from pathlib import Path
import sys
path = Path(sys.argv[1])
text = path.read_text(encoding="utf-8")
old = ' if "critical_gte" in thresholds:\n lines.append(f\' vars.life_noc_threshold_critical_gte = "{icinga_escape(str(thresholds["critical_gte"]))}"\')\n'
new = old + ' if "unknown_gte" in thresholds:\n lines.append(f\' vars.life_noc_threshold_unknown_gte = "{icinga_escape(str(thresholds["unknown_gte"]))}"\')\n if "ok_lt" in thresholds:\n lines.append(f\' vars.life_noc_threshold_ok_lt = "{icinga_escape(str(thresholds["ok_lt"]))}"\')\n if "warning_lt" in thresholds:\n lines.append(f\' vars.life_noc_threshold_warning_lt = "{icinga_escape(str(thresholds["warning_lt"]))}"\')\n if "critical_lt" in thresholds:\n lines.append(f\' vars.life_noc_threshold_critical_lt = "{icinga_escape(str(thresholds["critical_lt"]))}"\')\n'
if old not in text:
raise SystemExit("Bloc seuils introuvable dans generate_services.py")
text = text.replace(old, new, 1)
old2 = ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time"}:\n raise ValueError("seul probe.type=elapsed_time est supporté en v1")\n'
new2 = ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time", "days_until_due"}:\n raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés")\n'
if old2 in text:
text = text.replace(old2, new2, 1)
else:
old3 = ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time", "days_until_due"}:\n raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés")\n'
if old3 not in text:
raise SystemExit("Bloc validate_probe introuvable ou inattendu dans generate_services.py")
path.write_text(text, encoding="utf-8")
print("generate_services.py patché")
PYEOF
echo "==> Création du store data/inputs/obligations-legales-personnelles.yaml"
mkdir -p "$ROOT/data/inputs"
cat > "$ROOT/data/inputs/obligations-legales-personnelles.yaml" <<'YAMLEOF'
renouvellement-permis-conduire:
value: "2026-09-20"
captured_at: "2026-03-14T23:30:00Z"
origin: manual
YAMLEOF
echo "==> Mise à jour de domains.yaml"
python3 - "$ROOT/domains.yaml" <<'PYEOF'
from pathlib import Path
import sys
import yaml
path = Path(sys.argv[1])
data = yaml.safe_load(path.read_text(encoding="utf-8"))
domain = "obligations-legales-personnelles"
items = data.get("domains", {}).get(domain)
if not isinstance(items, list):
raise SystemExit(f"Domaine introuvable ou invalide: {domain}")
target = None
for item in items:
if isinstance(item, dict) and item.get("name") == "renouvellement-permis-conduire":
target = item
break
if target is None:
raise SystemExit("Item 'renouvellement-permis-conduire' introuvable dans domains.yaml")
target["date"] = "2026-09-20"
target["notes"] = "Vérifier la proximité de léchéance du permis"
target["probe"] = {
"type": "days_until_due",
"source": {
"type": "manual_date",
"inputs_file": "/opt/life-noc/data/inputs/obligations-legales-personnelles.yaml",
"item_key": "renouvellement-permis-conduire",
},
"metric": {
"unit": "days",
},
"thresholds": {
"critical_lt": 7,
"warning_lt": 30,
"ok_lt": 90,
"unknown_gte": 90,
},
"policy": {
"on_error": "critical",
},
}
path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True), encoding="utf-8")
print("domains.yaml mis à jour")
PYEOF
echo "==> Régénération"
(
cd "$ROOT"
python3 scripts/generate_services.py
)
echo
echo "Patch terminé."
echo "Étapes suivantes :"
echo " python3 scripts/generate_services.py"
echo " make check"
echo " make deploy-with-bpm"
echo
echo "Test utile ensuite :"
echo " python3 scripts/life_noc_input.py set obligations-legales-personnelles renouvellement-permis-conduire 2026-03-20"

View file

@ -1,145 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="${1:-.}"
need_file() {
local f="$1"
if [[ ! -f "$ROOT/$f" ]]; then
echo "Fichier introuvable: $f" >&2
exit 1
fi
}
echo "==> Vérification"
need_file "scripts/generate_services.py"
need_file "domains.yaml"
echo "==> Sauvegarde"
mkdir -p "$ROOT/.patch-backup-days-until-due-v2"
cp -a "$ROOT/scripts/generate_services.py" "$ROOT/.patch-backup-days-until-due-v2/generate_services.py.bak"
cp -a "$ROOT/domains.yaml" "$ROOT/.patch-backup-days-until-due-v2/domains.yaml.bak"
echo "==> Patch de scripts/generate_services.py"
python3 - "$ROOT/scripts/generate_services.py" <<'PYEOF'
from pathlib import Path
import sys
path = Path(sys.argv[1])
text = path.read_text(encoding="utf-8")
# 1) Ajouter les seuils *_lt / unknown_gte si absents
needle = ' if "critical_gte" in thresholds:\n lines.append(f\' vars.life_noc_threshold_critical_gte = "{icinga_escape(str(thresholds["critical_gte"]))}"\')\n'
addition = (
' if "unknown_gte" in thresholds:\n'
' lines.append(f\' vars.life_noc_threshold_unknown_gte = "{icinga_escape(str(thresholds["unknown_gte"]))}"\')\n'
' if "ok_lt" in thresholds:\n'
' lines.append(f\' vars.life_noc_threshold_ok_lt = "{icinga_escape(str(thresholds["ok_lt"]))}"\')\n'
' if "warning_lt" in thresholds:\n'
' lines.append(f\' vars.life_noc_threshold_warning_lt = "{icinga_escape(str(thresholds["warning_lt"]))}"\')\n'
' if "critical_lt" in thresholds:\n'
' lines.append(f\' vars.life_noc_threshold_critical_lt = "{icinga_escape(str(thresholds["critical_lt"]))}"\')\n'
)
if 'vars.life_noc_threshold_unknown_gte' not in text:
if needle not in text:
raise SystemExit("Impossible de trouver le bloc des seuils dans generate_services.py")
text = text.replace(needle, needle + addition, 1)
# 2) Élargir validate_probe aux deux types
old_variants = [
' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time"}:\n raise ValueError("seul probe.type=elapsed_time est supporté en v1")\n',
' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time"}:\n raise ValueError("seul probe.type=elapsed_time est supporté en v1")\r\n',
]
new_block = ' probe_type = str(probe.get("type", "")).strip()\n if probe_type not in {"elapsed_time", "days_until_due"}:\n raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés")\n'
replaced = False
for old in old_variants:
if old in text:
text = text.replace(old, new_block, 1)
replaced = True
break
if not replaced:
# si déjà patché, on accepte
if new_block.strip() not in text:
raise SystemExit("Bloc validate_probe introuvable ou déjà trop différent dans generate_services.py")
path.write_text(text, encoding="utf-8")
print("generate_services.py patché")
PYEOF
echo "==> Création du store data/inputs/obligations-legales-personnelles.yaml"
mkdir -p "$ROOT/data/inputs"
cat > "$ROOT/data/inputs/obligations-legales-personnelles.yaml" <<'YAMLEOF'
renouvellement-permis-conduire:
value: "2026-09-20"
captured_at: "2026-03-14T23:30:00Z"
origin: manual
YAMLEOF
echo "==> Mise à jour de domains.yaml"
python3 - "$ROOT/domains.yaml" <<'PYEOF'
from pathlib import Path
import sys
import yaml
path = Path(sys.argv[1])
data = yaml.safe_load(path.read_text(encoding="utf-8"))
domain = "obligations-legales-personnelles"
items = data.get("domains", {}).get(domain)
if not isinstance(items, list):
raise SystemExit(f"Domaine introuvable ou invalide: {domain}")
target = None
for item in items:
if isinstance(item, dict) and item.get("name") == "renouvellement-permis-conduire":
target = item
break
if target is None:
raise SystemExit("Item 'renouvellement-permis-conduire' introuvable")
target["date"] = "2026-09-20"
target["notes"] = "Vérifier la proximité de léchéance du permis"
target["probe"] = {
"type": "days_until_due",
"source": {
"type": "manual_date",
"inputs_file": "/opt/life-noc/data/inputs/obligations-legales-personnelles.yaml",
"item_key": "renouvellement-permis-conduire",
},
"metric": {
"unit": "days",
},
"thresholds": {
"critical_lt": 7,
"warning_lt": 30,
"ok_lt": 90,
"unknown_gte": 90,
},
"policy": {
"on_error": "critical",
},
}
path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True), encoding="utf-8")
print("domains.yaml mis à jour")
PYEOF
echo "==> Régénération"
(
cd "$ROOT"
python3 scripts/generate_services.py
)
echo
echo "Patch terminé."
echo "Étapes suivantes :"
echo " python3 scripts/generate_services.py"
echo " make check"
echo " make deploy-with-bpm"
echo
echo "Test utile ensuite :"
echo " python3 scripts/life_noc_input.py set obligations-legales-personnelles renouvellement-permis-conduire 2026-03-20"

View file

@ -1,161 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="${1:-.}"
need_file() {
local f="$1"
if [[ ! -f "$ROOT/$f" ]]; then
echo "Fichier introuvable: $f" >&2
exit 1
fi
}
echo "==> Vérification"
need_file "scripts/generate_services.py"
need_file "domains.yaml"
echo "==> Sauvegarde"
mkdir -p "$ROOT/.patch-backup-days-until-due-v3"
cp -a "$ROOT/scripts/generate_services.py" "$ROOT/.patch-backup-days-until-due-v3/generate_services.py.bak"
cp -a "$ROOT/domains.yaml" "$ROOT/.patch-backup-days-until-due-v3/domains.yaml.bak"
echo "==> Patch robuste de scripts/generate_services.py"
python3 - "$ROOT/scripts/generate_services.py" <<'PYEOF'
from pathlib import Path
import re
import sys
path = Path(sys.argv[1])
text = path.read_text(encoding="utf-8")
# 1) Ajouter les seuils lt/gte manquants dans render_probe_service, juste avant on_error
if 'vars.life_noc_threshold_unknown_gte' not in text:
marker = ' on_error = str(policy.get("on_error", "critical")).strip()\n'
inject = (
' if "unknown_gte" in thresholds:\n'
' lines.append(f\' vars.life_noc_threshold_unknown_gte = "{icinga_escape(str(thresholds["unknown_gte"]))}"\')\n'
' if "ok_lt" in thresholds:\n'
' lines.append(f\' vars.life_noc_threshold_ok_lt = "{icinga_escape(str(thresholds["ok_lt"]))}"\')\n'
' if "warning_lt" in thresholds:\n'
' lines.append(f\' vars.life_noc_threshold_warning_lt = "{icinga_escape(str(thresholds["warning_lt"]))}"\')\n'
' if "critical_lt" in thresholds:\n'
' lines.append(f\' vars.life_noc_threshold_critical_lt = "{icinga_escape(str(thresholds["critical_lt"]))}"\')\n'
)
if marker not in text:
raise SystemExit("Impossible de trouver le point d'injection des seuils dans render_probe_service")
text = text.replace(marker, inject + marker, 1)
# 2) Remplacer entièrement validate_probe(...)
new_validate = '''def validate_probe(item: dict) -> None:
probe = item["probe"]
if not isinstance(probe, dict):
raise ValueError("probe doit être un objet")
probe_type = str(probe.get("type", "")).strip()
if probe_type not in {"elapsed_time", "days_until_due"}:
raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés")
source = probe.get("source")
if not isinstance(source, dict):
raise ValueError("probe.source doit être un objet")
if str(source.get("type", "")).strip() != "manual_date":
raise ValueError("seul probe.source.type=manual_date est supporté en v1")
has_inline_value = "value" in source
has_store_ref = "inputs_file" in source and "item_key" in source
if not has_inline_value and not has_store_ref:
raise ValueError("probe.source.value ou probe.source.inputs_file + item_key requis")
metric = probe.get("metric")
if not isinstance(metric, dict):
raise ValueError("probe.metric doit être un objet")
if str(metric.get("unit", "")).strip() != "days":
raise ValueError("seul probe.metric.unit=days est supporté en v1")
thresholds = probe.get("thresholds")
if not isinstance(thresholds, dict):
raise ValueError("probe.thresholds doit être un objet")
'''
pattern = r'def validate_probe\(item: dict\) -> None:\n(?: .*\n)+?(?=\ndef render_servicegroup|\ndef main\()'
new_text, count = re.subn(pattern, new_validate + "\n", text, flags=re.MULTILINE)
if count == 0:
raise SystemExit("Impossible de remplacer validate_probe(...) dans generate_services.py")
text = new_text
path.write_text(text, encoding="utf-8")
print("generate_services.py patché")
PYEOF
echo "==> Création du store"
mkdir -p "$ROOT/data/inputs"
cat > "$ROOT/data/inputs/obligations-legales-personnelles.yaml" <<'YAMLEOF'
renouvellement-permis-conduire:
value: "2026-09-20"
captured_at: "2026-03-14T23:30:00Z"
origin: manual
YAMLEOF
echo "==> Mise à jour de domains.yaml"
python3 - "$ROOT/domains.yaml" <<'PYEOF'
from pathlib import Path
import sys
import yaml
path = Path(sys.argv[1])
data = yaml.safe_load(path.read_text(encoding="utf-8"))
domain = "obligations-legales-personnelles"
items = data.get("domains", {}).get(domain)
if not isinstance(items, list):
raise SystemExit(f"Domaine introuvable ou invalide: {domain}")
target = None
for item in items:
if isinstance(item, dict) and item.get("name") == "renouvellement-permis-conduire":
target = item
break
if target is None:
raise SystemExit("Item 'renouvellement-permis-conduire' introuvable")
target["date"] = "2026-09-20"
target["notes"] = "Vérifier la proximité de léchéance du permis"
target["probe"] = {
"type": "days_until_due",
"source": {
"type": "manual_date",
"inputs_file": "/opt/life-noc/data/inputs/obligations-legales-personnelles.yaml",
"item_key": "renouvellement-permis-conduire",
},
"metric": {
"unit": "days",
},
"thresholds": {
"critical_lt": 7,
"warning_lt": 30,
"ok_lt": 90,
"unknown_gte": 90,
},
"policy": {
"on_error": "critical",
},
}
path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True), encoding="utf-8")
print("domains.yaml mis à jour")
PYEOF
echo "==> Régénération"
(
cd "$ROOT"
python3 scripts/generate_services.py
)
echo
echo "Patch terminé."
echo "Étapes suivantes :"
echo " python3 scripts/generate_services.py"
echo " make check"
echo " make deploy-with-bpm"

View file

@ -79,6 +79,9 @@ def render_mock_service(domain: str, group_name: str, item: dict) -> str:
date = str(item["date"]).strip()
notes = item.get("notes", "").strip()
notes_url = item.get("notes_url", "").strip()
instructions_url = item.get("instructions_url", "").strip()
if not instructions_url:
instructions_url = f"/life-noc/item/{domain}/{name}"
action_url = item.get("action_url", "").strip()
mock_state = normalize_mock_state(item.get("mock_state", DEFAULT_MOCK_STATE))
mock_message = item.get("mock_message", DEFAULT_MOCK_MESSAGE).strip() or DEFAULT_MOCK_MESSAGE
@ -98,6 +101,8 @@ def render_mock_service(domain: str, group_name: str, item: dict) -> str:
lines.append(f' notes = "{icinga_escape(notes)}"')
if notes_url:
lines.append(f' notes_url = "{icinga_escape(notes_url)}"')
if instructions_url:
lines.append(f' vars.instructions_url = "{icinga_escape(instructions_url)}"')
if action_url:
lines.append(f' action_url = "{icinga_escape(action_url)}"')
@ -112,6 +117,9 @@ def render_probe_service(domain: str, group_name: str, item: dict) -> str:
name = item["name"].strip()
notes = item.get("notes", "").strip()
notes_url = item.get("notes_url", "").strip()
instructions_url = item.get("instructions_url", "").strip()
if not instructions_url:
instructions_url = f"/life-noc/item/{domain}/{name}"
action_url = item.get("action_url", "").strip()
probe = item["probe"]
@ -160,6 +168,8 @@ def render_probe_service(domain: str, group_name: str, item: dict) -> str:
lines.append(f' notes = "{icinga_escape(notes)}"')
if notes_url:
lines.append(f' notes_url = "{icinga_escape(notes_url)}"')
if instructions_url:
lines.append(f' vars.instructions_url = "{icinga_escape(instructions_url)}"')
if action_url:
lines.append(f' action_url = "{icinga_escape(action_url)}"')

View file

@ -0,0 +1,307 @@
#!/usr/bin/env python3
from pathlib import Path
import re
import sys
import yaml
INPUT_FILE = Path("domains.yaml")
OUTPUT_DIR = Path("icinga/services")
SERVICEGROUPS_DIR = Path("icinga/servicegroups")
SERVICEGROUPS_FILE = SERVICEGROUPS_DIR / "life-noc.conf"
HOST_NAME = "life-noc"
SERVICE_TEMPLATE = "service_echeance"
PROBE_TEMPLATE = "service_life_noc_probe"
DEFAULT_MOCK_STATE = "OK"
DEFAULT_MOCK_MESSAGE = "Sous contrôle"
def slugify(value: str) -> str:
value = value.strip().lower()
replacements = {
"à": "a", "â": "a", "ä": "a",
"ç": "c",
"é": "e", "è": "e", "ê": "e", "ë": "e",
"î": "i", "ï": "i",
"ô": "o", "ö": "o",
"ù": "u", "û": "u", "ü": "u",
"ÿ": "y",
"œ": "oe",
"æ": "ae",
"'": "",
"": "",
}
for old, new in replacements.items():
value = value.replace(old, new)
value = re.sub(r"[^a-z0-9\-]+", "-", value)
value = re.sub(r"-{2,}", "-", value)
return value.strip("-")
def aliasify(value: str) -> str:
value = value.strip().upper()
replacements = {
"À": "A", "Â": "A", "Ä": "A",
"Ç": "C",
"É": "E", "È": "E", "Ê": "E", "Ë": "E",
"Î": "I", "Ï": "I",
"Ô": "O", "Ö": "O",
"Ù": "U", "Û": "U", "Ü": "U",
"Ÿ": "Y",
"Œ": "OE",
"Æ": "AE",
"'": "",
"": "",
}
for old, new in replacements.items():
value = value.replace(old, new)
value = re.sub(r"[^A-Z0-9\-]+", "-", value)
value = re.sub(r"-{2,}", "-", value)
return value.strip("-")
def icinga_escape(value: str) -> str:
return value.replace("\\", "\\\\").replace('"', '\\"')
def normalize_mock_state(value: str) -> str:
state = str(value).strip().upper()
allowed = {"OK", "WARNING", "CRITICAL"}
if state not in allowed:
raise ValueError(
f"mock_state invalide: {value}. Valeurs permises: {', '.join(sorted(allowed))}"
)
return state
def render_mock_service(domain: str, group_name: str, item: dict) -> str:
name = item["name"].strip()
date = str(item["date"]).strip()
notes = item.get("notes", "").strip()
notes_url = item.get("notes_url", "").strip()
action_url = item.get("action_url", "").strip()
mock_state = normalize_mock_state(item.get("mock_state", DEFAULT_MOCK_STATE))
mock_message = item.get("mock_message", DEFAULT_MOCK_MESSAGE).strip() or DEFAULT_MOCK_MESSAGE
service_name = f"{domain}-{name}"
lines = [
f'apply Service "{icinga_escape(service_name)}" {{',
f' import "{SERVICE_TEMPLATE}"',
f' vars.date_echeance = "{icinga_escape(date)}"',
f' vars.mock_state = "{icinga_escape(mock_state)}"',
f' vars.mock_message = "{icinga_escape(mock_message)}"',
f' groups = [ "{icinga_escape(group_name)}" ]',
]
if notes:
lines.append(f' notes = "{icinga_escape(notes)}"')
if notes_url:
lines.append(f' notes_url = "{icinga_escape(notes_url)}"')
if action_url:
lines.append(f' action_url = "{icinga_escape(action_url)}"')
lines.append(f' assign where host.name == "{HOST_NAME}"')
lines.append("}")
lines.append("")
return "\n".join(lines)
def render_probe_service(domain: str, group_name: str, item: dict) -> str:
name = item["name"].strip()
notes = item.get("notes", "").strip()
notes_url = item.get("notes_url", "").strip()
action_url = item.get("action_url", "").strip()
probe = item["probe"]
probe_type = str(probe["type"]).strip()
source = probe["source"]
metric = probe["metric"]
thresholds = probe["thresholds"]
policy = probe.get("policy", {})
service_name = f"{domain}-{name}"
lines = [
f'apply Service "{icinga_escape(service_name)}" {{',
f' import "{PROBE_TEMPLATE}"',
f' vars.life_noc_probe_type = "{icinga_escape(probe_type)}"',
f' vars.life_noc_source_type = "{icinga_escape(str(source["type"]).strip())}"',
f' vars.life_noc_source_value = "{icinga_escape(str(source.get("value", "")).strip())}"',
f' vars.life_noc_inputs_file = "{icinga_escape(str(source.get("inputs_file", "")).strip())}"',
f' vars.life_noc_item_key = "{icinga_escape(str(source.get("item_key", name)).strip())}"',
f' vars.life_noc_metric_unit = "{icinga_escape(str(metric["unit"]).strip())}"',
f' vars.life_noc_label = "{icinga_escape(name)}"',
f' groups = [ "{icinga_escape(group_name)}" ]',
]
if "unknown_lt" in thresholds:
lines.append(f' vars.life_noc_threshold_unknown_lt = "{icinga_escape(str(thresholds["unknown_lt"]))}"')
if "ok_gte" in thresholds:
lines.append(f' vars.life_noc_threshold_ok_gte = "{icinga_escape(str(thresholds["ok_gte"]))}"')
if "warning_gte" in thresholds:
lines.append(f' vars.life_noc_threshold_warning_gte = "{icinga_escape(str(thresholds["warning_gte"]))}"')
if "critical_gte" in thresholds:
lines.append(f' vars.life_noc_threshold_critical_gte = "{icinga_escape(str(thresholds["critical_gte"]))}"')
if "unknown_gte" in thresholds:
lines.append(f' vars.life_noc_threshold_unknown_gte = "{icinga_escape(str(thresholds["unknown_gte"]))}"')
if "ok_lt" in thresholds:
lines.append(f' vars.life_noc_threshold_ok_lt = "{icinga_escape(str(thresholds["ok_lt"]))}"')
if "warning_lt" in thresholds:
lines.append(f' vars.life_noc_threshold_warning_lt = "{icinga_escape(str(thresholds["warning_lt"]))}"')
if "critical_lt" in thresholds:
lines.append(f' vars.life_noc_threshold_critical_lt = "{icinga_escape(str(thresholds["critical_lt"]))}"')
on_error = str(policy.get("on_error", "critical")).strip()
lines.append(f' vars.life_noc_on_error = "{icinga_escape(on_error)}"')
if notes:
lines.append(f' notes = "{icinga_escape(notes)}"')
if notes_url:
lines.append(f' notes_url = "{icinga_escape(notes_url)}"')
if action_url:
lines.append(f' action_url = "{icinga_escape(action_url)}"')
lines.append(f' assign where host.name == "{HOST_NAME}"')
lines.append("}")
lines.append("")
return "\n".join(lines)
def validate_probe(item: dict) -> None:
probe = item["probe"]
if not isinstance(probe, dict):
raise ValueError("probe doit être un objet")
probe_type = str(probe.get("type", "")).strip()
if probe_type not in {"elapsed_time", "days_until_due"}:
raise ValueError("seuls probe.type=elapsed_time et days_until_due sont supportés")
source = probe.get("source")
if not isinstance(source, dict):
raise ValueError("probe.source doit être un objet")
if str(source.get("type", "")).strip() != "manual_date":
raise ValueError("seul probe.source.type=manual_date est supporté en v1")
has_inline_value = "value" in source
has_store_ref = "inputs_file" in source and "item_key" in source
if not has_inline_value and not has_store_ref:
raise ValueError("probe.source.value ou probe.source.inputs_file + item_key requis")
metric = probe.get("metric")
if not isinstance(metric, dict):
raise ValueError("probe.metric doit être un objet")
if str(metric.get("unit", "")).strip() != "days":
raise ValueError("seul probe.metric.unit=days est supporté en v1")
thresholds = probe.get("thresholds")
if not isinstance(thresholds, dict):
raise ValueError("probe.thresholds doit être un objet")
def render_servicegroup(group_name: str) -> str:
return "\n".join([
f'object ServiceGroup "{icinga_escape(group_name)}" {{',
f' display_name = "{icinga_escape(group_name)}"',
"}",
"",
])
def main() -> int:
if not INPUT_FILE.exists():
print(f"Erreur: fichier introuvable: {INPUT_FILE}", file=sys.stderr)
return 1
with INPUT_FILE.open("r", encoding="utf-8") as f:
data = yaml.safe_load(f)
if not isinstance(data, dict) or "domains" not in data:
print("Erreur: le YAML doit contenir une clé racine 'domains'.", file=sys.stderr)
return 1
domains = data["domains"]
if not isinstance(domains, dict):
print("Erreur: 'domains' doit être un objet YAML.", file=sys.stderr)
return 1
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
SERVICEGROUPS_DIR.mkdir(parents=True, exist_ok=True)
for existing_conf in OUTPUT_DIR.glob("*.conf"):
existing_conf.unlink()
if SERVICEGROUPS_FILE.exists():
SERVICEGROUPS_FILE.unlink()
generated_files = []
servicegroup_content = [
"/*",
" AUTO-GENERATED FILE - SERVICE GROUPS",
" Ne pas modifier manuellement.",
" Source: domains.yaml",
"*/",
"",
]
for raw_domain, services in domains.items():
domain = slugify(str(raw_domain))
group_name = aliasify(str(raw_domain))
if not isinstance(services, list):
print(f"Erreur: le domaine '{raw_domain}' doit contenir une liste.", file=sys.stderr)
return 1
output_file = OUTPUT_DIR / f"{domain}.conf"
content = [
"/*",
f" AUTO-GENERATED FILE - DOMAIN: {raw_domain}",
" Ne pas modifier manuellement.",
" Source: domains.yaml",
"*/",
"",
]
servicegroup_content.append(render_servicegroup(group_name))
for idx, item in enumerate(services, start=1):
if not isinstance(item, dict):
print(f"Erreur: entrée invalide dans '{raw_domain}' à la position {idx}.", file=sys.stderr)
return 1
missing = [key for key in ("name", "date", "notes") if key not in item]
if missing:
print(
f"Erreur: dans le domaine '{raw_domain}', entrée {idx}, champs manquants: {', '.join(missing)}",
file=sys.stderr,
)
return 1
try:
if "probe" in item:
validate_probe(item)
content.append(render_probe_service(domain, group_name, item))
else:
content.append(render_mock_service(domain, group_name, item))
except ValueError as exc:
print(f"Erreur dans '{raw_domain}', entrée {idx}: {exc}", file=sys.stderr)
return 1
output_file.write_text("\n".join(content).rstrip() + "\n", encoding="utf-8")
generated_files.append(str(output_file))
SERVICEGROUPS_FILE.write_text("\n".join(servicegroup_content).rstrip() + "\n", encoding="utf-8")
generated_files.append(str(SERVICEGROUPS_FILE))
print("Fichiers générés :")
for file_path in generated_files:
print(f" - {file_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -5,14 +5,22 @@ from __future__ import annotations
from pathlib import Path
import sys
from fastapi import FastAPI, HTTPException
from fastapi import FastAPI, HTTPException, Form
from fastapi.responses import HTMLResponse, RedirectResponse
from pydantic import BaseModel
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from lib.life_noc_inputs import list_items, get_item, set_item, complete_item # noqa: E402
from lib.life_noc_inputs import ( # noqa: E402
list_items,
get_item,
set_item,
complete_item,
get_defined_service,
compute_state_for_item,
)
class SetInputRequest(BaseModel):
@ -27,6 +35,31 @@ class CompleteInputRequest(BaseModel):
app = FastAPI(title="Life-NOC Input API", version="1.0.0")
def html_escape(value: str) -> str:
return (
str(value)
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace('"', "&quot;")
)
def link_row(label: str, url: str) -> str:
if not url:
return ""
u = html_escape(url)
l = html_escape(label)
return f'<li><a href="{u}" target="_blank" rel="noopener">{l}</a></li>'
def resolve_instructions_url(domain: str, item_key: str, service: dict) -> str:
explicit = str(service.get("instructions_url", "")).strip()
if explicit:
return explicit
return f"/life-noc/item/{domain}/{item_key}"
@app.get("/health")
def health() -> dict:
return {"status": "ok"}
@ -59,3 +92,191 @@ def api_complete_input(domain: str, item_key: str, req: CompleteInputRequest | N
origin = "manual" if req is None else req.origin
item = complete_item(domain, item_key, origin=origin)
return {"domain": domain, "item_key": item_key, **item}
@app.get("/life-noc/item/{domain}/{item_key}", response_class=HTMLResponse)
def page_item(domain: str, item_key: str) -> HTMLResponse:
try:
service = get_defined_service(domain, item_key)
current_input = get_item(domain, item_key)
state = compute_state_for_item(domain, item_key)
except KeyError:
raise HTTPException(status_code=404, detail="item introuvable")
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
title = service.get("title") or service.get("display_name") or item_key.replace("-", " ").capitalize()
summary = service.get("summary", "")
notes = service.get("notes", "")
instructions = service.get("instructions", "")
notes_url = service.get("notes_url", "")
instructions_url = resolve_instructions_url(domain, item_key, service)
action_url = service.get("action_url", "")
probe_type = service.get("probe", {}).get("type", "unknown")
ui = service.get("ui", {}) if isinstance(service.get("ui", {}), dict) else {}
form_mode = ui.get("form_mode", "")
allow_complete = bool(ui.get("allow_complete", probe_type == "elapsed_time"))
allow_manual_edit = bool(ui.get("allow_manual_edit", True))
links_html = ""
for fragment in [
link_row("Notes", notes_url),
link_row("Instructions", instructions_url),
link_row("Action", action_url),
]:
if fragment:
links_html += fragment
summary_html = ""
if summary:
summary_html = f'<p><strong>Résumé :</strong> {html_escape(summary)}</p>'
instructions_html = ""
if instructions:
instructions_html = f"""
<section class="card">
<h2>Instructions</h2>
<pre style="white-space: pre-wrap; font-family: Arial, sans-serif;">{html_escape(instructions)}</pre>
</section>
"""
manual_form_html = ""
if allow_manual_edit and form_mode == "complete_date":
manual_form_html = f"""
<section class="card">
<h2>Mettre à jour la date</h2>
<form method="post" action="/life-noc/item/{html_escape(domain)}/{html_escape(item_key)}/set">
<input type="date" name="value" value="{html_escape(current_input.get("value", ""))}" required>
<input type="hidden" name="origin" value="manual">
<button type="submit">Enregistrer</button>
</form>
</section>
"""
complete_form_html = ""
if allow_complete:
complete_form_html = f"""
<section class="card">
<h2>Action rapide</h2>
<form method="post" action="/life-noc/item/{html_escape(domain)}/{html_escape(item_key)}/complete">
<button type="submit">Compléter</button>
</form>
</section>
"""
html = f"""<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Life-NOC {html_escape(title)}</title>
<style>
body {{
font-family: Arial, sans-serif;
max-width: 760px;
margin: 0 auto;
padding: 1rem;
line-height: 1.45;
}}
.card {{
border: 1px solid #ccc;
border-radius: 10px;
padding: 1rem;
margin-bottom: 1rem;
}}
.state {{
font-size: 1.2rem;
font-weight: bold;
}}
button {{
font-size: 1rem;
padding: 0.9rem 1.2rem;
border-radius: 8px;
border: 1px solid #444;
cursor: pointer;
}}
input[type="date"] {{
font-size: 1rem;
padding: 0.7rem;
border-radius: 8px;
border: 1px solid #666;
margin-right: 0.5rem;
}}
ul {{
padding-left: 1.25rem;
}}
code {{
background: #f3f3f3;
padding: 0.1rem 0.3rem;
border-radius: 4px;
}}
</style>
</head>
<body>
<main>
<section class="card">
<h1>{html_escape(title)}</h1>
<p class="state">État actuel : {html_escape(state)}</p>
<p><strong>Type de sonde :</strong> {html_escape(probe_type)}</p>
{summary_html}
</section>
<section class="card">
<h2>Dernier intrant</h2>
<p><strong>Valeur :</strong> {html_escape(current_input.get("value", ""))}</p>
<p><strong>Capturé le :</strong> {html_escape(current_input.get("captured_at", ""))}</p>
<p><strong>Origine :</strong> {html_escape(current_input.get("origin", ""))}</p>
</section>
<section class="card">
<h2>Contexte</h2>
<p><strong>Domaine :</strong> {html_escape(domain)}</p>
<p><strong>Item key :</strong> <code>{html_escape(item_key)}</code></p>
<p><strong>Notes :</strong> {html_escape(notes)}</p>
</section>
{instructions_html}
<section class="card">
<h2>Liens utiles</h2>
<ul>
{links_html}
</ul>
</section>
{manual_form_html}
{complete_form_html}
</main>
</body>
</html>"""
return HTMLResponse(content=html)
@app.post("/life-noc/item/{domain}/{item_key}/set")
def page_set_item(domain: str, item_key: str, value: str = Form(...), origin: str = Form(default="manual")) -> RedirectResponse:
try:
set_item(domain, item_key, value=value, origin=origin)
except KeyError:
raise HTTPException(status_code=404, detail="item introuvable")
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
return RedirectResponse(
url=f"/life-noc/item/{domain}/{item_key}",
status_code=303,
)
@app.post("/life-noc/item/{domain}/{item_key}/complete")
def page_complete_item(domain: str, item_key: str, origin: str = Form(default="manual")) -> RedirectResponse:
try:
complete_item(domain, item_key, origin=origin)
except KeyError:
raise HTTPException(status_code=404, detail="item introuvable")
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
return RedirectResponse(
url=f"/life-noc/item/{domain}/{item_key}",
status_code=303,
)

View file

@ -0,0 +1,227 @@
#!/usr/bin/env python3
from __future__ import annotations
from pathlib import Path
import sys
from fastapi import FastAPI, HTTPException, Form
from fastapi.responses import HTMLResponse, RedirectResponse
from pydantic import BaseModel
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from lib.life_noc_inputs import ( # noqa: E402
list_items,
get_item,
set_item,
complete_item,
get_defined_service,
compute_state_for_item,
)
class SetInputRequest(BaseModel):
value: str
origin: str = "manual"
class CompleteInputRequest(BaseModel):
origin: str = "manual"
app = FastAPI(title="Life-NOC Input API", version="1.0.0")
def html_escape(value: str) -> str:
return (
str(value)
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace('"', "&quot;")
)
def link_row(label: str, url: str) -> str:
if not url:
return ""
u = html_escape(url)
l = html_escape(label)
return f'<li><a href="{u}" target="_blank" rel="noopener">{l}</a></li>'
def resolve_instructions_url(domain: str, item_key: str, service: dict) -> str:
explicit = str(service.get("instructions_url", "")).strip()
if explicit:
return explicit
return f"/life-noc/item/{domain}/{item_key}"
@app.get("/health")
def health() -> dict:
return {"status": "ok"}
@app.get("/inputs/{domain}")
def api_list_inputs(domain: str) -> dict:
return {"domain": domain, "items": list_items(domain)}
@app.get("/inputs/{domain}/{item_key}")
def api_get_input(domain: str, item_key: str) -> dict:
try:
item = get_item(domain, item_key)
except KeyError:
raise HTTPException(status_code=404, detail="item introuvable")
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
return {"domain": domain, "item_key": item_key, **item}
@app.post("/inputs/{domain}/{item_key}")
def api_set_input(domain: str, item_key: str, req: SetInputRequest) -> dict:
item = set_item(domain, item_key, req.value, origin=req.origin)
return {"domain": domain, "item_key": item_key, **item}
@app.post("/inputs/{domain}/{item_key}/complete")
def api_complete_input(domain: str, item_key: str, req: CompleteInputRequest | None = None) -> dict:
origin = "manual" if req is None else req.origin
item = complete_item(domain, item_key, origin=origin)
return {"domain": domain, "item_key": item_key, **item}
@app.get("/life-noc/item/{domain}/{item_key}", response_class=HTMLResponse)
def page_item(domain: str, item_key: str) -> HTMLResponse:
try:
service = get_defined_service(domain, item_key)
current_input = get_item(domain, item_key)
state = compute_state_for_item(domain, item_key)
except KeyError:
raise HTTPException(status_code=404, detail="item introuvable")
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
title = service.get("title") or service.get("display_name") or item_key.replace("-", " ").capitalize()
summary = service.get("summary", "")
notes = service.get("notes", "")
instructions = service.get("instructions", "")
notes_url = service.get("notes_url", "")
instructions_url = resolve_instructions_url(domain, item_key, service)
action_url = service.get("action_url", "")
probe_type = service.get("probe", {}).get("type", "unknown")
ui = service.get("ui", {}) if isinstance(service.get("ui", {}), dict) else {}
form_mode = ui.get("form_mode", "")
allow_complete = bool(ui.get("allow_complete", probe_type == "elapsed_time"))
allow_manual_edit = bool(ui.get("allow_manual_edit", True))
html = f"""<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Life-NOC {html_escape(title)}</title>
<style>
body {{
font-family: Arial, sans-serif;
max-width: 760px;
margin: 0 auto;
padding: 1rem;
line-height: 1.45;
}}
.card {{
border: 1px solid #ccc;
border-radius: 10px;
padding: 1rem;
margin-bottom: 1rem;
}}
.state {{
font-size: 1.2rem;
font-weight: bold;
}}
button {{
font-size: 1rem;
padding: 0.9rem 1.2rem;
border-radius: 8px;
border: 1px solid #444;
cursor: pointer;
}}
ul {{
padding-left: 1.25rem;
}}
code {{
background: #f3f3f3;
padding: 0.1rem 0.3rem;
border-radius: 4px;
}}
</style>
</head>
<body>
<main>
<section class="card">
<h1>{html_escape(title)}</h1>
<p class="state">État actuel : {html_escape(state)}</p>
<p><strong>Type de sonde :</strong> {html_escape(probe_type)}</p>
</section>
<section class="card">
<h2>Dernier intrant</h2>
<p><strong>Valeur :</strong> {html_escape(current_input.get("value", ""))}</p>
<p><strong>Capturé le :</strong> {html_escape(current_input.get("captured_at", ""))}</p>
<p><strong>Origine :</strong> {html_escape(current_input.get("origin", ""))}</p>
</section>
<section class="card">
<h2>Contexte</h2>
<p><strong>Domaine :</strong> {html_escape(domain)}</p>
<p><strong>Item key :</strong> <code>{html_escape(item_key)}</code></p>
<p><strong>Notes :</strong> {html_escape(notes)}</p>
</section>
<section class="card">
<h2>Liens utiles</h2>
<ul>
{link_row("Notes", notes_url)}
{link_row("Instructions", instructions_url)}
{link_row("Action", action_url)}
</ul>
</section>
<section class="card">
<h2>Action</h2>
<form method="post" action="/life-noc/item/{html_escape(domain)}/{html_escape(item_key)}/complete">
<button type="submit">Compléter</button>
</form>
</section>
</main>
</body>
</html>"""
return HTMLResponse(content=html)
@app.post("/life-noc/item/{domain}/{item_key}/set")
def page_set_item(domain: str, item_key: str, value: str = Form(...), origin: str = Form(default="manual")) -> RedirectResponse:
try:
set_item(domain, item_key, value=value, origin=origin)
except KeyError:
raise HTTPException(status_code=404, detail="item introuvable")
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
return RedirectResponse(
url=f"/life-noc/item/{domain}/{item_key}",
status_code=303,
)
@app.post("/life-noc/item/{domain}/{item_key}/complete")
def page_complete_item(domain: str, item_key: str, origin: str = Form(default="manual")) -> RedirectResponse:
try:
complete_item(domain, item_key, origin=origin)
except KeyError:
raise HTTPException(status_code=404, detail="item introuvable")
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
return RedirectResponse(
url=f"/life-noc/item/{domain}/{item_key}",
status_code=303,
)