diff --git a/package.json b/package.json index 7064d2b..2949253 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/committee", - "version": "0.1.18", + "version": "0.1.19", "private": true, "description": "GovOPlaN Committee platform module seed.", "type": "module", diff --git a/pyproject.toml b/pyproject.toml index b6759c8..83d0950 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "govoplan-committee" -version = "0.1.18" +version = "0.1.19" description = "GovOPlaN committee governance and formal-decision integration module." readme = "README.md" requires-python = ">=3.12" diff --git a/src/govoplan_committee/backend/manifest.py b/src/govoplan_committee/backend/manifest.py index 35c3f67..f38bc23 100644 --- a/src/govoplan_committee/backend/manifest.py +++ b/src/govoplan_committee/backend/manifest.py @@ -12,6 +12,7 @@ from govoplan_core.core.module_guards import ( ) from govoplan_core.core.modules import ( CapabilityDocumentation, + DocumentationCondition, DocumentationLink, DocumentationTopic, FrontendModule, @@ -58,7 +59,7 @@ from govoplan_core.db.base import Base MODULE_ID = "committee" MODULE_NAME = "Committee" -MODULE_VERSION = "0.1.18" +MODULE_VERSION = "0.1.19" READ_SCOPE = "committee:workspace:read" WRITE_SCOPE = "committee:workspace:write" BALLOT_SCOPE = "committee:ballot:finalize" @@ -235,6 +236,7 @@ DOCUMENTATION = ( layer="available", documentation_types=("admin", "user"), audience=("user", "operator", "module_admin", "product_owner"), + conditions=(DocumentationCondition(required_scopes=(READ_SCOPE,)),), order=100, related_modules=OPTIONAL_DEPENDENCIES, links=( @@ -245,6 +247,7 @@ DOCUMENTATION = ( ), ), metadata={ + "kind": "workflow", "seed": True, "help_contexts": [ "committee.navigation", @@ -271,6 +274,35 @@ DOCUMENTATION = ( "generic formal decision authority, reasoning, effect, review, correction, or revocation lifecycle" ], }, + translations={ + "de": { + "title": "Abgrenzung des Committee-Moduls", + "summary": ( + "Arbeitsabläufe für Ausschüsse, Vorstände, Räte und Senate mit Sitzungen, Tagesordnungen, Protokollen, Beratung, Abstimmung, formellen Entscheidungsverweisen und Folgeaufgaben." + ), + "body": ( + "Der dauerhafte Arbeitsbereich bewahrt unveränderliche Gremien, Sitzungen, Tagesordnungspunkte, gesteuerte " + "Abstimmungsergebnisse, Protokolle und Lebenszyklusereignisse. Der Entscheidungspfad konstruiert daraus, aus " + "einer wirksamen Mandatsauflösung, Genehmigung, versionierten Rechtsgrundlagen, Nachweisen, Begründung und " + "beantragten oder beobachteten Wirkungen eine rekonstruierbare formelle Entscheidung. Committee führt keine " + "generische Mandats- oder Entscheidungspersistenz; optionale Anbieter lösen diese Objekte auf und speichern " + "sie, während eine geschützte lokale Projektion den begrenzten Rückfall bewahrt. An einen Anbieter gebundene " + "Stimmabgaben werden über Adapterfähigkeiten abgeschlossen, ohne einzelne Stimmen aufzubewahren. Von Voting " + "gelieferte Ergebnisse bewahren bereinigte Anbieterzusicherungen und stufen den nicht zertifizierten lokalen " + "vertraulichen Referenzanbieter niemals zu einem zertifizierten Profil hoch." + ), + } + }, + structured_translation_version="1", + structured_translations={ + "de": { + "privacy_notes": [ + "Committee-Listen zeigen nur Datensätze, die im aktiven Mandanten- und Berechtigungskontext autorisiert sind.", + "Anbietergestützte Abstimmungen speichern aggregierte Ergebnisnachweise, keine einzelnen vertraulichen Stimmen.", + "Geschützte Entscheidungsbegründungen erfordern die besondere Berechtigung zum geschützten Lesen.", + ] + } + }, ), DocumentationTopic( id=f"{MODULE_ID}.reference.fields-and-consequences", @@ -298,6 +330,7 @@ DOCUMENTATION = ( ), ), metadata={ + "kind": "reference", "seed": True, "help_contexts": [ "committee.field.state", @@ -318,6 +351,36 @@ DOCUMENTATION = ( "cancel_or_retire": "Stops future use while retaining institutional and audit evidence.", }, }, + translations={ + "de": { + "title": "Committee-Felder und Folgen", + "summary": ( + "Feldherkunft, Lebenszyklusbeschränkungen, optionale Anbieterverweise und Nachweisfolgen für Committee-Datensätze." + ), + "body": ( + "Organisationseinheitsverweise eines Gremiums bestimmen den institutionellen Kontext, gewähren aber selbst " + "keinen Zugriff. Sitzungszeiten begründen den Tagesordnungskontext. Gegenstands-, Decision-, Approval-, " + "Records- und Abstimmungskennungen sind stabile modulübergreifende Verweise und bleiben nur optional, soweit " + "der aktuelle Lebenszyklus dies erlaubt. Zustandsübergänge erzeugen neue unveränderliche Revisionen; endgültige " + "Datensätze können nicht an Ort und Stelle bearbeitet werden. Der Abschluss einer Abstimmung oder die Annahme " + "eines Protokolls erfordert den zugehörigen gesteuerten Nachweis. Die anbietergebundene Finalisierung importiert " + "nur ein aggregiertes Ergebnis und Zusicherungsnachweise. Änderungsgründe bleiben mit jeder Revision erhalten. " + "Abbruch, Rücknahme oder Außerbetriebnahme stoppt künftige Arbeit, löscht aber keine bestehenden Revisionen, " + "Entscheidungen, Protokolle oder Prüfnachweise." + ), + } + }, + structured_translation_version="1", + structured_translations={ + "de": { + "consequence_classes": { + "save_revision": "Erzeugt eine neue unveränderliche Committee-Revision mit ihrem Änderungsgrund.", + "change_lifecycle": "Ändert die künftig möglichen Aktionen; endgültige Zustände sind unveränderlich.", + "finalize_ballot": "Importiert ein gesteuertes aggregiertes Ergebnis und Nachweise, ohne einzelne Stimmen aufzubewahren.", + "cancel_or_retire": "Stoppt die künftige Nutzung und bewahrt institutionelle sowie Prüfnachweise.", + } + } + }, ), ) @@ -515,6 +578,7 @@ manifest = ModuleManifest( audience=("user", "operator", "module_admin", "auditor"), related_modules=("core", "decisions", "voting", "records"), metadata={ + "kind": "reference", "help_contexts": [ "committee.workspace", "privacy.data-subject-requests", @@ -528,6 +592,33 @@ manifest = ModuleManifest( ), }, }, + translations={ + "de": { + "title": "Datenschutzanfragen zu Committee", + "summary": ( + "Minimierte Akteurszuordnungen aus unveränderlichen Committee-Datensätzen ausgeben, ohne Beratungs- oder Abstimmungsinhalte offenzulegen." + ), + "body": ( + "Committee gleicht innerhalb des aktiven Mandanten exakte Konto-, Mitgliedschafts-, Identitäts- oder " + "Akteurskennungen ab. Optionale Objektart-, Objekt- oder Decision-Kennungen schränken nur eine bereits " + "verifizierte Akteurssuche ein. Arbeitsbereichsergebnisse enthalten stabilen Objekt-, Eltern-, Revisions-, " + "Zustands- und Zeitkontext. Ereignis- und Rückfall-Decision-Ergebnisse enthalten ausschließlich " + "Lebenszykluszuordnungen sowie stabile Sitzungs- oder Tagesordnungsverweise. Suchtext, Protokolle, " + "Beratungsinhalte, einzelne Stimmen, Anfrageprüfsummen, Idempotenzwerte und referenzierte Anbieterdatensätze " + "werden ausgeschlossen und nie traversiert. Alle zurückgegebenen Zuordnungen bleiben unveränderliche " + "institutionelle Nachweise." + ), + } + }, + structured_translation_version="1", + structured_translations={ + "de": { + "consequence_classes": { + "export_actor_attribution": "Gibt stabilen Lebenszykluskontext ohne institutionelle Inhalte zurück.", + "retain_committee_history": "Bewahrt unveränderliche Verantwortungsnachweise zu Sitzungen und Decisions.", + } + } + }, ), *DOCUMENTATION, ), diff --git a/tests/test_documentation.py b/tests/test_documentation.py new file mode 100644 index 0000000..a9f2b77 --- /dev/null +++ b/tests/test_documentation.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +import unittest + +from govoplan_core.core.modules import ( + documentation_structured_translation_issues, + localizable_documentation_metadata_keys, +) +from govoplan_committee.backend.manifest import manifest + + +class CommitteeDocumentationTests(unittest.TestCase): + def test_german_reference_documentation_is_complete(self) -> None: + topics = manifest.documentation + self.assertEqual(3, len(topics)) + for topic in topics: + translation = topic.translations.get("de", {}) + self.assertTrue(translation.get("title"), topic.id) + self.assertTrue(translation.get("summary"), topic.id) + self.assertTrue(translation.get("body"), topic.id) + if localizable_documentation_metadata_keys(topic): + self.assertEqual("1", topic.structured_translation_version, topic.id) + self.assertIn("de", topic.structured_translations, topic.id) + self.assertEqual((), documentation_structured_translation_issues(topic)) + + kinds = {topic.metadata.get("kind") for topic in topics} + self.assertIn("workflow", kinds) + self.assertIn("reference", kinds) + workflow = next(topic for topic in topics if topic.metadata.get("kind") == "workflow") + self.assertTrue(workflow.conditions) + + +if __name__ == "__main__": + unittest.main() diff --git a/webui/package.json b/webui/package.json index b45078c..2811601 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/committee-webui", - "version": "0.1.18", + "version": "0.1.19", "private": true, "type": "module", "main": "src/index.ts",