From a0266b886c999538c0535ee22a58241302b5b039 Mon Sep 17 00:00:00 2001 From: Corentin JOGUET Date: Thu, 7 May 2026 18:15:21 +0200 Subject: [PATCH] feat(baserow): add formulas pass + related field naming to seed - schema.json : 17 formulas (rollups + heures_restantes) ajoutees + related_field_name explicite sur les 10 liens - seed.py : 3 nouveaux methodes (create_formula_field, rename_field, create_link_field returns dict) - seed.py : pass 5/6 renomme automatiquement les related fields apres link creation - seed.py : pass 6/6 cree les formulas (idempotent) - README.md : section formulas updated Iteration 2 du plan Fast-App couverte. Apres seed, les rollups (formation_heures_attribuees, personne_heures_restantes_total, etc.) sont automatiques. --- baserow/seed/README.md | 4 +++- baserow/seed/schema.json | 40 +++++++++++++++++++++++++++----------- baserow/seed/seed.py | 42 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 71 insertions(+), 15 deletions(-) diff --git a/baserow/seed/README.md b/baserow/seed/README.md index 203bb72..2c80613 100644 --- a/baserow/seed/README.md +++ b/baserow/seed/README.md @@ -69,7 +69,9 @@ Pour reset complet : drop la database via l'UI Baserow, puis relancer. `schema.json` decrit les 9 tables + 10 liens FK. Format JSON declaratif, modifiable. -Les **formulas** (rollups, heures_restantes, etc.) ne sont **pas** crees par ce seed (Phase 1 = structure seule). Elles seront ajoutees en iteration 2 du plan Fast-App via un seed-formulas.py separe. +Les **formulas** (rollups, heures_restantes, etc.) sont creees au pass 6/6. Le schema declaratif inclut 17 formulas dans `schema.json` (section `formulas`). + +Si une formula echoue (syntaxe Baserow strict), le seed s'arrete et affiche l'erreur — corrige le `schema.json` et re-run. ## Troubleshooting diff --git a/baserow/seed/schema.json b/baserow/seed/schema.json index b970d2d..8d1134c 100644 --- a/baserow/seed/schema.json +++ b/baserow/seed/schema.json @@ -187,16 +187,34 @@ } ], "links": [ - {"from_table": "bloc", "from_field": "bloc_formation", "to_table": "formation"}, - {"from_table": "module", "from_field": "module_bloc", "to_table": "bloc"}, - {"from_table": "attribution", "from_field": "attribution_module", "to_table": "module"}, - {"from_table": "attribution", "from_field": "attribution_personne", "to_table": "personne"}, - {"from_table": "projet", "from_field": "projet_client", "to_table": "client"}, - {"from_table": "projet", "from_field": "projet_formation_pedagogique", "to_table": "formation"}, - {"from_table": "tache", "from_field": "tache_projet", "to_table": "projet"}, - {"from_table": "tache", "from_field": "tache_assignee", "to_table": "personne"}, - {"from_table": "intervention", "from_field": "intervention_tache", "to_table": "tache"}, - {"from_table": "intervention", "from_field": "intervention_personne", "to_table": "personne"} + {"from_table": "bloc", "from_field": "bloc_formation", "to_table": "formation", "related_field_name": "formation_blocs"}, + {"from_table": "module", "from_field": "module_bloc", "to_table": "bloc", "related_field_name": "bloc_modules"}, + {"from_table": "attribution", "from_field": "attribution_module", "to_table": "module", "related_field_name": "module_attributions"}, + {"from_table": "attribution", "from_field": "attribution_personne", "to_table": "personne", "related_field_name": "personne_attributions"}, + {"from_table": "projet", "from_field": "projet_client", "to_table": "client", "related_field_name": "client_projets"}, + {"from_table": "projet", "from_field": "projet_formation_pedagogique", "to_table": "formation", "related_field_name": "formation_projets_pedagogiques"}, + {"from_table": "tache", "from_field": "tache_projet", "to_table": "projet", "related_field_name": "projet_taches"}, + {"from_table": "tache", "from_field": "tache_assignee", "to_table": "personne", "related_field_name": "personne_taches_assignees"}, + {"from_table": "intervention", "from_field": "intervention_tache", "to_table": "tache", "related_field_name": "tache_interventions"}, + {"from_table": "intervention", "from_field": "intervention_personne", "to_table": "personne", "related_field_name": "personne_interventions"} ], - "_note_phase_2": "Les formulas (rollups, heures_restantes) seront ajoutees en iteration 2 du plan Fast-App. Phase 1 = structure + liens seuls." + "formulas": [ + {"table": "module", "name": "module_heures_prevues_active", "expression": "if(field('module_statut') = 'annule', 0, field('module_heures_prevues'))"}, + {"table": "attribution", "name": "attribution_heures_attribuees_active", "expression": "if(field('attribution_statut') = 'annule', 0, field('attribution_heures_attribuees'))"}, + {"table": "intervention", "name": "intervention_heures_active", "expression": "if(field('intervention_statut') = 'annule', 0, field('intervention_heures'))"}, + {"table": "bloc", "name": "bloc_heures_attribuees", "expression": "sum(lookup('bloc_modules', 'module_heures_prevues_active'))"}, + {"table": "bloc", "name": "bloc_heures_restantes", "expression": "field('bloc_heures_prevues') - field('bloc_heures_attribuees')"}, + {"table": "module", "name": "module_heures_attribuees", "expression": "sum(lookup('module_attributions', 'attribution_heures_attribuees_active'))"}, + {"table": "module", "name": "module_heures_realisees", "expression": "sum(lookup('module_attributions', 'attribution_heures_realisees'))"}, + {"table": "formation", "name": "formation_heures_attribuees", "expression": "sum(lookup('formation_blocs', 'bloc_heures_prevues'))"}, + {"table": "formation", "name": "formation_heures_restantes", "expression": "field('formation_heures_totales') - field('formation_heures_attribuees')"}, + {"table": "personne", "name": "personne_heures_attribuees_formation", "expression": "sum(lookup('personne_attributions', 'attribution_heures_attribuees_active'))"}, + {"table": "personne", "name": "personne_heures_attribuees_agence", "expression": "sum(lookup('personne_interventions', 'intervention_heures_active'))"}, + {"table": "personne", "name": "personne_heures_restantes_formation", "expression": "(field('personne_capacite_annuelle') * field('personne_split_formation_pct') / 100) - field('personne_heures_attribuees_formation')"}, + {"table": "personne", "name": "personne_heures_restantes_agence", "expression": "(field('personne_capacite_annuelle') * field('personne_split_agence_pct') / 100) - field('personne_heures_attribuees_agence')"}, + {"table": "personne", "name": "personne_heures_restantes_total", "expression": "field('personne_capacite_annuelle') - field('personne_heures_attribuees_formation') - field('personne_heures_attribuees_agence')"}, + {"table": "tache", "name": "tache_heures_realisees", "expression": "sum(lookup('tache_interventions', 'intervention_heures_active'))"}, + {"table": "projet", "name": "projet_heures_realisees", "expression": "sum(lookup('projet_taches', 'tache_heures_realisees'))"}, + {"table": "projet", "name": "projet_heures_restantes", "expression": "field('projet_charge_heures') - field('projet_heures_realisees')"} + ] } diff --git a/baserow/seed/seed.py b/baserow/seed/seed.py index c8daf63..e9d866b 100644 --- a/baserow/seed/seed.py +++ b/baserow/seed/seed.py @@ -149,7 +149,7 @@ class BaserowSeed: payload["long_text_enable_rich_text"] = True return payload - def create_link_field(self, from_table_id: int, name: str, to_table_id: int) -> int: + def create_link_field(self, from_table_id: int, name: str, to_table_id: int) -> dict[str, Any]: r = self.session.post( f"{self.base_url}/api/database/fields/table/{from_table_id}/", headers=self._headers(), @@ -158,6 +158,25 @@ class BaserowSeed: if r.status_code >= 300: print(f" [ERROR] link {name} resp={r.text}") r.raise_for_status() + return r.json() + + def rename_field(self, field_id: int, new_name: str) -> None: + r = self.session.patch( + f"{self.base_url}/api/database/fields/{field_id}/", + headers=self._headers(), + json={"name": new_name}, + ) + r.raise_for_status() + + def create_formula_field(self, table_id: int, name: str, expression: str) -> int: + r = self.session.post( + f"{self.base_url}/api/database/fields/table/{table_id}/", + headers=self._headers(), + json={"name": name, "type": "formula", "formula": expression}, + ) + if r.status_code >= 300: + print(f" [ERROR] formula {name} expr={expression} resp={r.text}") + r.raise_for_status() return r.json()["id"] def seed(self, schema: dict[str, Any]) -> None: @@ -183,7 +202,7 @@ class BaserowSeed: table_ids[tname] = tid self._sync_fields(tid, table) - print(f"\n[5/5] Link fields (2nd pass)") + print(f"\n[5/6] Link fields (2nd pass)") for link in schema["links"]: from_id = table_ids[link["from_table"]] to_id = table_ids[link["to_table"]] @@ -191,14 +210,31 @@ class BaserowSeed: if link["from_field"] in existing_fields: print(f" [link] Reuse {link['from_table']}.{link['from_field']} -> {link['to_table']}") continue - self.create_link_field(from_id, link["from_field"], to_id) + link_field = self.create_link_field(from_id, link["from_field"], to_id) print(f" [link] Created {link['from_table']}.{link['from_field']} -> {link['to_table']}") + related_id = link_field.get("link_row_related_field_id") or link_field.get("link_row_related_field") + related_name = link.get("related_field_name") + if related_name and related_id: + self.rename_field(related_id, related_name) + print(f" [link] Renamed related field id={related_id} -> '{related_name}'") + + print(f"\n[6/6] Formula fields (3rd pass)") + for f in schema.get("formulas", []): + tname = f["table"] + tid = table_ids[tname] + existing_fields = {x["name"]: x for x in self.list_fields(tid)} + if f["name"] in existing_fields: + print(f" [formula] Reuse {tname}.{f['name']}") + continue + self.create_formula_field(tid, f["name"], f["expression"]) + print(f" [formula] Created {tname}.{f['name']}") print("\n=== Seed OK ===") print(f" Workspace: {schema['workspace_name']}") print(f" Database : {schema['database_name']}") print(f" Tables : {len(table_ids)}") print(f" Links : {len(schema['links'])}") + print(f" Formulas : {len(schema.get('formulas', []))}") def _sync_fields(self, table_id: int, table_def: dict[str, Any]) -> None: existing = {f["name"]: f for f in self.list_fields(table_id)}