docs(scheduling): complete German reference coverage
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "govoplan-scheduling"
|
||||
version = "0.1.18"
|
||||
version = "0.1.20"
|
||||
description = "GovOPlaN meeting scheduling and Terminfindung module seed."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
__all__ = ["__version__"]
|
||||
|
||||
__version__ = "0.1.18"
|
||||
__version__ = "0.1.20"
|
||||
|
||||
@@ -2,9 +2,15 @@ from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER
|
||||
from govoplan_core.core.access import (
|
||||
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
||||
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
||||
)
|
||||
from govoplan_core.core.calendar import CAPABILITY_CALENDAR_SCHEDULING
|
||||
from govoplan_core.core.module_guards import drop_table_retirement_provider, persistent_table_uninstall_guard
|
||||
from govoplan_core.core.module_guards import (
|
||||
drop_table_retirement_provider,
|
||||
persistent_table_uninstall_guard,
|
||||
)
|
||||
from govoplan_core.core.modules import (
|
||||
CapabilityDocumentation,
|
||||
DocumentationTopic,
|
||||
@@ -42,7 +48,7 @@ from govoplan_scheduling.backend.dsar_provider import SCHEDULING_DSAR_CAPABILITY
|
||||
|
||||
MODULE_ID = "scheduling"
|
||||
MODULE_NAME = "Scheduling"
|
||||
MODULE_VERSION = "0.1.19"
|
||||
MODULE_VERSION = "0.1.20"
|
||||
READ_SCOPE = "scheduling:schedule:read"
|
||||
WRITE_SCOPE = "scheduling:schedule:write"
|
||||
ADMIN_SCOPE = "scheduling:schedule:admin"
|
||||
@@ -64,10 +70,26 @@ def _permission(scope: str, label: str, description: str) -> PermissionDefinitio
|
||||
|
||||
|
||||
PERMISSIONS = (
|
||||
_permission(READ_SCOPE, "View scheduling", "Read scheduling polls, proposals, participant state, and selected outcomes."),
|
||||
_permission(WRITE_SCOPE, "Manage own scheduling", "Create scheduling polls and manage requests for which the account is the organizer."),
|
||||
_permission(ADMIN_SCOPE, "Administer scheduling", "Manage every tenant scheduling request and configure scheduling policies, external participation, and retention defaults."),
|
||||
_permission(RESPOND_SCOPE, "Respond to scheduling polls", "Submit and update own scheduling availability responses."),
|
||||
_permission(
|
||||
READ_SCOPE,
|
||||
"View scheduling",
|
||||
"Read scheduling polls, proposals, participant state, and selected outcomes.",
|
||||
),
|
||||
_permission(
|
||||
WRITE_SCOPE,
|
||||
"Manage own scheduling",
|
||||
"Create scheduling polls and manage requests for which the account is the organizer.",
|
||||
),
|
||||
_permission(
|
||||
ADMIN_SCOPE,
|
||||
"Administer scheduling",
|
||||
"Manage every tenant scheduling request and configure scheduling policies, external participation, and retention defaults.",
|
||||
),
|
||||
_permission(
|
||||
RESPOND_SCOPE,
|
||||
"Respond to scheduling polls",
|
||||
"Submit and update own scheduling availability responses.",
|
||||
),
|
||||
)
|
||||
|
||||
ROLE_TEMPLATES = (
|
||||
@@ -102,7 +124,28 @@ DOCUMENTATION = (
|
||||
layer="available",
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("user", "operator", "module_admin", "product_owner"),
|
||||
related_modules=("poll", "evaluation", "calendar", "appointments", "mail", "notifications", "portal"),
|
||||
related_modules=(
|
||||
"poll",
|
||||
"evaluation",
|
||||
"calendar",
|
||||
"appointments",
|
||||
"mail",
|
||||
"notifications",
|
||||
"portal",
|
||||
),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Modulgrenze von Scheduling",
|
||||
"summary": "Terminplanung und Terminfindung auf Grundlage wiederverwendbarer Verfügbarkeitsbausteine von Poll.",
|
||||
"body": (
|
||||
"Scheduling besitzt Abläufe zur Terminfindung, Terminkandidaten, Erfassung der Teilnehmendenverfügbarkeit, "
|
||||
"Konflikterklärung, Erinnerungen und Entscheidungsübergabe. Für wiederverwendbare Verfügbarkeitsmatrizen und den "
|
||||
"pollgestützten Workflow-Kontext verwendet es Poll und kann optional Evaluation für Rückmeldungen nach einem Termin "
|
||||
"auslösen. Access ist optional: Ist es installiert, kann Scheduling Hauptpersonenauflösung, Berechtigungsprüfung, Gruppen "
|
||||
"und Rollenvorlagen verwenden; ohne Access bleiben eingeschränkte Abläufe über signierte Links oder lokale Organisation möglich."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={"seed": True},
|
||||
),
|
||||
DocumentationTopic(
|
||||
@@ -116,7 +159,12 @@ DOCUMENTATION = (
|
||||
),
|
||||
layer="configured",
|
||||
documentation_types=("admin",),
|
||||
audience=("privacy_officer", "scheduling_manager", "records_manager", "operator"),
|
||||
audience=(
|
||||
"privacy_officer",
|
||||
"scheduling_manager",
|
||||
"records_manager",
|
||||
"operator",
|
||||
),
|
||||
order=5,
|
||||
conditions=(
|
||||
DocumentationCondition(
|
||||
@@ -141,6 +189,29 @@ DOCUMENTATION = (
|
||||
),
|
||||
),
|
||||
related_modules=("access", "audit", "poll", "calendar", "notifications"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Scheduling-Daten in einer Betroffenenanfrage prüfen",
|
||||
"summary": (
|
||||
"Mandantenbezogene Teilnahme- und Koordinationsmetadaten erfassen, während Poll-Antworten und Calendar-Wirkungen "
|
||||
"bei ihren zuständigen Modulen verbleiben."
|
||||
),
|
||||
"body": (
|
||||
"Der DSAR-Provider von Scheduling durchsucht den wirksamen Mandanten anhand normalisierter E-Mail-Adressen der "
|
||||
"Teilnehmenden, Mitgliedschafts-, Identitäts- oder gebundener Kontoverweise sowie namensraumgebundener Scheduling-Anfrage- "
|
||||
"oder Teilnehmendenverweise. Er isoliert die passende Person, ihren Anfrage- und Terminkandidatenkontext sowie passende "
|
||||
"Benachrichtigungshüllen. Nicht exportiert werden Poll- oder Einladungskennungen, Zustände des Teilnahme-Gateways, "
|
||||
"wiederverwendbare Einschreibelinks oder Nachweishashes, Hashes anonymer Passwörter, Calendar-Ereignis- oder Haltekennungen, "
|
||||
"Details zu Frei-/Belegt-Konflikten, Benachrichtigungsnutzdaten oder -fehler, Tokenmaterial, undurchsichtige Metadaten oder "
|
||||
"andere Teilnehmende. Poll bleibt maßgeblich für tatsächliche Verfügbarkeitsangaben und Nachweise zur Beendigung von Antworten; "
|
||||
"Calendar bleibt maßgeblich für Ereignisse, Vormerkungen und Synchronisationszustände. Entschiedene, übergebene, abgesagte, "
|
||||
"archivierte, beantwortete, benachrichtigte oder entfernte Zustände werden mit Begründung aufbewahrt. Aktive gemeinsame "
|
||||
"Terminplanungsinhalte erfordern eine koordinierte manuelle Prüfung. Der Provider darf nur Personen anonymisieren und "
|
||||
"ausmustern, zu denen keine Einladung, Antwort, Einschreibung, Benachrichtigung oder abschließende Entscheidung vorliegt, "
|
||||
"und prüft alle Bedingungen unter mandantengebundenen Zeilensperren erneut."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/admin?section=tenant-data-subject-requests",
|
||||
@@ -180,6 +251,24 @@ DOCUMENTATION = (
|
||||
),
|
||||
),
|
||||
related_modules=("poll", "calendar", "notifications", "mail"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Termin finden und entscheiden",
|
||||
"summary": (
|
||||
"Terminkandidaten anlegen, interne oder externe Personen einladen, Verfügbarkeiten vergleichen und den gewählten Termin "
|
||||
"bei verfügbarem Calendar in ein Kalenderereignis überführen."
|
||||
),
|
||||
"body": (
|
||||
"Scheduling erfasst Anforderungen an Teilnehmende, Quorum- und Gewichtungsregeln, Antwortfristen, Erinnerungen und "
|
||||
"Ja-/Nein-/Vielleicht-Verfügbarkeiten über Poll. Organisierende mit Calendar-Anbindung können vor der Entscheidung Konflikte "
|
||||
"prüfen und vorläufige Vormerkungen anlegen. Nach der Entscheidung gibt Scheduling nicht verwendete Vormerkungen frei, "
|
||||
"legt das endgültige Ereignis an oder verknüpft es und zeichnet die Übergabe an Notifications auf. Signierte externe Links "
|
||||
"zeigen nur die begrenzten Anfrageinformationen, welche die Teilnahme- und Datenschutzrichtlinie der Anfrage erlaubt. Die "
|
||||
"gesteuerte Poll-Einladung löst den Mandanten auf, bevor Scheduling ausgeführt wird. Dadurch kann die Mandanten-Modulrichtlinie "
|
||||
"die öffentliche Teilnahme zurückziehen, ohne die Tokenprüfung abzuschwächen."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/scheduling",
|
||||
@@ -216,9 +305,35 @@ DOCUMENTATION = (
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("organizer", "module_admin", "tenant_admin"),
|
||||
related_modules=("calendar", "access", "policy"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Calendar-Koordination für Scheduling konfigurieren",
|
||||
"summary": (
|
||||
"Die Calendar-Fähigkeit und Berechtigungen für Konfliktprüfungen, vorläufige Vormerkungen und die endgültige "
|
||||
"Ereignisübergabe verstehen."
|
||||
),
|
||||
"body": (
|
||||
"Die Calendar-Koordination bleibt optional. Sie ist nur verfügbar, wenn Calendar seine Auswahlfähigkeit bereitstellt und "
|
||||
"die handelnde Person Kalender und Verfügbarkeiten lesen sowie Ereignisse schreiben darf. Eine deaktivierte Calendar-Steuerung "
|
||||
"nennt daher die fehlende Integration oder Berechtigung, statt eine nicht ausführbare Konfiguration stillschweigend zu "
|
||||
"akzeptieren. Bei der Entscheidung wird eine ausgewählte vorläufige Vormerkung direkt bestätigt und jede ungenutzte "
|
||||
"Vormerkung zur dauerhaften Freigabe an Calendar übergeben. Auch eine Absage wartet, bis Calendar jede Freigabe angenommen hat. "
|
||||
"Teilweise oder nicht verfügbare Calendar-Wirkungen halten den Scheduling-Lebenszyklus unvollständig und zeigen einen "
|
||||
"bereinigungsbedürftigen Wiederholungszustand; eine identische Wiederholung verwendet die aufgezeichneten Ereignis- und "
|
||||
"Vorgangskennungen wieder, statt Wirkungen zu duplizieren. Administrierende beheben synchronisierte Fehler über Calendars "
|
||||
"Abgleich ausgehender Änderungen und wiederholen anschließend die Scheduling-Aktion. Scheduling speichert nur "
|
||||
"Vorgangskennungen, zuletzt bekannten Zustand und Verweise auf ausstehende Termine; Calendar bleibt maßgeblich für Ereignis- "
|
||||
"und Outbox-Nachweise. Administrierende sollten Calendar aktivieren und der organisierenden Person die begrenzten Kalender-, "
|
||||
"Verfügbarkeits- und Ereignisberechtigungen gewähren; Scheduling importiert keine Calendar-Interna."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"help_contexts": ["scheduling.calendar-integration", "scheduling.calendar-coordination"],
|
||||
"help_contexts": [
|
||||
"scheduling.calendar-integration",
|
||||
"scheduling.calendar-coordination",
|
||||
],
|
||||
},
|
||||
),
|
||||
DocumentationTopic(
|
||||
@@ -234,6 +349,21 @@ DOCUMENTATION = (
|
||||
documentation_types=("admin",),
|
||||
audience=("operator", "module_admin", "tenant_admin"),
|
||||
related_modules=("poll", "policy", "access"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Öffentliche Teilnahme an Terminplanungen steuern",
|
||||
"summary": (
|
||||
"Deaktivierte Gasteinladungen behandeln, ohne Datenschutz signierter Links oder Teilnahmerichtlinien abzuschwächen."
|
||||
),
|
||||
"body": (
|
||||
"Öffentliche Einladungslinks werden nur ausgestellt, wenn das Gateway für öffentliche Teilnahme die konfigurierten "
|
||||
"Antwort-, Datenschutz-, Passwort-, E-Mail- und Änderungsregeln durchsetzen kann. Ist diese Durchsetzung nicht verfügbar, "
|
||||
"dürfen angemeldete Teilnehmende weiterhin über ihre zugewiesene Anfrage antworten; das System stellt jedoch keinen "
|
||||
"schwächeren Gastlink aus. Eine System- oder Mandantenadministration muss die gesteuerte Poll-/Teilnahmefähigkeit "
|
||||
"wiederherstellen oder die Anfrage auf angemeldete Teilnahme beschränken."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "pattern",
|
||||
"help_contexts": [
|
||||
@@ -263,6 +393,30 @@ DOCUMENTATION = (
|
||||
),
|
||||
),
|
||||
related_modules=("poll", "access", "policy"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Gesteuerte öffentliche Selbsteinschreibelinks verwenden",
|
||||
"summary": (
|
||||
"Wiederverwendbare Terminplanungslinks mit ausdrücklicher Kapazität, Laufzeit, Identitäts-, Kontobindungs- und "
|
||||
"Missbrauchsschutzsteuerung ausstellen."
|
||||
),
|
||||
"body": (
|
||||
"Ein öffentlicher Selbsteinschreibelink unterscheidet sich von einer personenspezifischen Einladung. Organisierende müssen "
|
||||
"Kapazität und zukünftigen Ablauf festlegen und können anonyme sowie angemeldete Einschreibung unabhängig zulassen. Alle "
|
||||
"Teilnehmenden geben einen Anzeigenamen an; eine E-Mail-Adresse ist nur erforderlich, wenn die Anfragerichtlinie dies verlangt. "
|
||||
"Anonyme Teilnehmende erzeugen und verwahren einen getrennten Wiederherstellungsnachweis. Dieser wird im Anfragekörper "
|
||||
"übermittelt und niemals in Link, Protokollen, Analysen oder dauerhaftem Klartext abgelegt. Angemeldete Teilnehmende müssen "
|
||||
"die Kontobindung ausdrücklich bestätigen. Eine spätere angemeldete Einreichung darf eine anonyme Einschreibung nur mit "
|
||||
"deren Wiederherstellungsnachweis binden; die Bindung wird auditiert. Die Deployment-Richtlinie kann Selbsteinschreibung "
|
||||
"deaktivieren oder die maximale Kapazität begrenzen. Redis stellt bei Konfiguration eine gemeinsame Drosselung mit festem "
|
||||
"Zeitfenster bereit; in der Entwicklung gilt der begrenzte Einzelknoten-Rückfall. Vorhandene persönliche Einladungslinks "
|
||||
"bleiben unverändert. Widerruf oder Ablauf des wiederverwendbaren Links verhindert neuen Zugriff sofort. Die Kapazität wird "
|
||||
"gemeinsam mit dem Anlegen der Person serialisiert, Wiederholungen sind über den Idempotenzschlüssel idempotent und bestehende "
|
||||
"Nachweisinhaber dürfen nur ändern, solange die Anfragerichtlinie Änderungen erlaubt. Andere Teilnehmende erhalten nur die "
|
||||
"bereits vorhandene Aggregat- oder gesteuerte Teilnehmerlistenansicht der Anfrage."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/scheduling",
|
||||
@@ -283,27 +437,45 @@ DOCUMENTATION = (
|
||||
|
||||
|
||||
def _tenant_summary(session, tenant_id: str) -> dict[str, int]:
|
||||
from govoplan_scheduling.backend.db.models import SchedulingCandidateSlot, SchedulingNotification, SchedulingParticipant, SchedulingPublicEnrollmentLink, SchedulingRequest
|
||||
from govoplan_scheduling.backend.db.models import (
|
||||
SchedulingCandidateSlot,
|
||||
SchedulingNotification,
|
||||
SchedulingParticipant,
|
||||
SchedulingPublicEnrollmentLink,
|
||||
SchedulingRequest,
|
||||
)
|
||||
|
||||
return {
|
||||
"scheduling_requests": (
|
||||
session.query(SchedulingRequest)
|
||||
.filter(SchedulingRequest.tenant_id == tenant_id, SchedulingRequest.deleted_at.is_(None))
|
||||
.filter(
|
||||
SchedulingRequest.tenant_id == tenant_id,
|
||||
SchedulingRequest.deleted_at.is_(None),
|
||||
)
|
||||
.count()
|
||||
),
|
||||
"scheduling_candidate_slots": (
|
||||
session.query(SchedulingCandidateSlot)
|
||||
.filter(SchedulingCandidateSlot.tenant_id == tenant_id, SchedulingCandidateSlot.deleted_at.is_(None))
|
||||
.filter(
|
||||
SchedulingCandidateSlot.tenant_id == tenant_id,
|
||||
SchedulingCandidateSlot.deleted_at.is_(None),
|
||||
)
|
||||
.count()
|
||||
),
|
||||
"scheduling_participants": (
|
||||
session.query(SchedulingParticipant)
|
||||
.filter(SchedulingParticipant.tenant_id == tenant_id, SchedulingParticipant.deleted_at.is_(None))
|
||||
.filter(
|
||||
SchedulingParticipant.tenant_id == tenant_id,
|
||||
SchedulingParticipant.deleted_at.is_(None),
|
||||
)
|
||||
.count()
|
||||
),
|
||||
"scheduling_notifications": (
|
||||
session.query(SchedulingNotification)
|
||||
.filter(SchedulingNotification.tenant_id == tenant_id, SchedulingNotification.status == "pending")
|
||||
.filter(
|
||||
SchedulingNotification.tenant_id == tenant_id,
|
||||
SchedulingNotification.status == "pending",
|
||||
)
|
||||
.count()
|
||||
),
|
||||
"scheduling_public_enrollment_links": (
|
||||
@@ -340,7 +512,10 @@ def _public_tenant_resolver(request: object, session: object) -> str | None:
|
||||
if not request_id or not token:
|
||||
return None
|
||||
|
||||
from govoplan_scheduling.backend.db.models import SchedulingPublicEnrollmentLink, SchedulingRequest
|
||||
from govoplan_scheduling.backend.db.models import (
|
||||
SchedulingPublicEnrollmentLink,
|
||||
SchedulingRequest,
|
||||
)
|
||||
from govoplan_scheduling.backend.security import public_credential_hash
|
||||
from govoplan_core.db.base import utcnow
|
||||
|
||||
@@ -349,7 +524,8 @@ def _public_tenant_resolver(request: object, session: object) -> str | None:
|
||||
session.query(SchedulingPublicEnrollmentLink)
|
||||
.filter(
|
||||
SchedulingPublicEnrollmentLink.request_id == request_id,
|
||||
SchedulingPublicEnrollmentLink.token_hash == public_credential_hash(token),
|
||||
SchedulingPublicEnrollmentLink.token_hash
|
||||
== public_credential_hash(token),
|
||||
SchedulingPublicEnrollmentLink.revoked_at.is_(None),
|
||||
SchedulingPublicEnrollmentLink.expires_at > utcnow(),
|
||||
)
|
||||
@@ -395,7 +571,21 @@ manifest = ModuleManifest(
|
||||
name=MODULE_NAME,
|
||||
version=MODULE_VERSION,
|
||||
dependencies=("poll",),
|
||||
optional_dependencies=("access", "calendar", "appointments", "evaluation", "mail", "notifications", "policy", "portal", "workflow_engine", "tasks", "idm", "organizations", "addresses"),
|
||||
optional_dependencies=(
|
||||
"access",
|
||||
"calendar",
|
||||
"appointments",
|
||||
"evaluation",
|
||||
"mail",
|
||||
"notifications",
|
||||
"policy",
|
||||
"portal",
|
||||
"workflow_engine",
|
||||
"tasks",
|
||||
"idm",
|
||||
"organizations",
|
||||
"addresses",
|
||||
),
|
||||
optional_capabilities=(
|
||||
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
||||
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
||||
@@ -409,25 +599,82 @@ manifest = ModuleManifest(
|
||||
CAPABILITY_POLL_PARTICIPATION_GATEWAY,
|
||||
),
|
||||
provides_interfaces=(
|
||||
ModuleInterfaceProvider(name="scheduling.candidate_slots", version=MODULE_VERSION),
|
||||
ModuleInterfaceProvider(name="scheduling.decision_handoff", version=MODULE_VERSION),
|
||||
ModuleInterfaceProvider(
|
||||
name="scheduling.candidate_slots", version=MODULE_VERSION
|
||||
),
|
||||
ModuleInterfaceProvider(
|
||||
name="scheduling.decision_handoff", version=MODULE_VERSION
|
||||
),
|
||||
ModuleInterfaceProvider(name=SCHEDULING_DSAR_CAPABILITY, version="0.1.0"),
|
||||
),
|
||||
requires_interfaces=(
|
||||
ModuleInterfaceRequirement(name="poll.option_ordering", version_min="0.1.11", version_max_exclusive="0.2.0"),
|
||||
ModuleInterfaceRequirement(name="poll.availability_matrix", version_min="0.1.11", version_max_exclusive="0.2.0"),
|
||||
ModuleInterfaceRequirement(name="poll.response_collection", version_min="0.1.11", version_max_exclusive="0.2.0"),
|
||||
ModuleInterfaceRequirement(name="poll.workflow_context", version_min="0.1.11", version_max_exclusive="0.2.0"),
|
||||
ModuleInterfaceRequirement(name="poll.governed_participation", version_min="0.1.11", version_max_exclusive="0.2.0"),
|
||||
ModuleInterfaceRequirement(name="evaluation.feedback", version_min="0.1.8", version_max_exclusive="0.2.0", optional=True),
|
||||
ModuleInterfaceRequirement(name="notifications.dispatch", version_min="0.1.8", version_max_exclusive="0.2.0", optional=True),
|
||||
ModuleInterfaceRequirement(name=CAPABILITY_ACCESS_PEOPLE_SEARCH, version_min="0.1.0", version_max_exclusive="0.2.0", optional=True),
|
||||
ModuleInterfaceRequirement(name=CAPABILITY_ADDRESSES_PEOPLE_SEARCH, version_min="0.1.0", version_max_exclusive="0.2.0", optional=True),
|
||||
ModuleInterfaceRequirement(name="calendar.scheduling", version_min="0.1.9", version_max_exclusive="0.2.0", optional=True),
|
||||
ModuleInterfaceRequirement(
|
||||
name="poll.option_ordering",
|
||||
version_min="0.1.11",
|
||||
version_max_exclusive="0.2.0",
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name="poll.availability_matrix",
|
||||
version_min="0.1.11",
|
||||
version_max_exclusive="0.2.0",
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name="poll.response_collection",
|
||||
version_min="0.1.11",
|
||||
version_max_exclusive="0.2.0",
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name="poll.workflow_context",
|
||||
version_min="0.1.11",
|
||||
version_max_exclusive="0.2.0",
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name="poll.governed_participation",
|
||||
version_min="0.1.11",
|
||||
version_max_exclusive="0.2.0",
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name="evaluation.feedback",
|
||||
version_min="0.1.8",
|
||||
version_max_exclusive="0.2.0",
|
||||
optional=True,
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name="notifications.dispatch",
|
||||
version_min="0.1.8",
|
||||
version_max_exclusive="0.2.0",
|
||||
optional=True,
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name=CAPABILITY_ACCESS_PEOPLE_SEARCH,
|
||||
version_min="0.1.0",
|
||||
version_max_exclusive="0.2.0",
|
||||
optional=True,
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name=CAPABILITY_ADDRESSES_PEOPLE_SEARCH,
|
||||
version_min="0.1.0",
|
||||
version_max_exclusive="0.2.0",
|
||||
optional=True,
|
||||
),
|
||||
ModuleInterfaceRequirement(
|
||||
name="calendar.scheduling",
|
||||
version_min="0.1.9",
|
||||
version_max_exclusive="0.2.0",
|
||||
optional=True,
|
||||
),
|
||||
),
|
||||
permissions=PERMISSIONS,
|
||||
role_templates=ROLE_TEMPLATES,
|
||||
nav_items=(NavItem(path="/scheduling", label="Scheduling", icon="calendar-clock", required_any=(READ_SCOPE,), order=56),),
|
||||
nav_items=(
|
||||
NavItem(
|
||||
path="/scheduling",
|
||||
label="Scheduling",
|
||||
icon="calendar-clock",
|
||||
required_any=(READ_SCOPE,),
|
||||
order=56,
|
||||
),
|
||||
),
|
||||
frontend=FrontendModule(
|
||||
module_id=MODULE_ID,
|
||||
package_name="@govoplan/scheduling-webui",
|
||||
@@ -451,7 +698,15 @@ manifest = ModuleManifest(
|
||||
order=11,
|
||||
),
|
||||
),
|
||||
nav_items=(NavItem(path="/scheduling", label="Scheduling", icon="calendar-clock", required_any=(READ_SCOPE,), order=56),),
|
||||
nav_items=(
|
||||
NavItem(
|
||||
path="/scheduling",
|
||||
label="Scheduling",
|
||||
icon="calendar-clock",
|
||||
required_any=(READ_SCOPE,),
|
||||
order=56,
|
||||
),
|
||||
),
|
||||
product_areas=(
|
||||
ProductAreaContribution(
|
||||
id="meetings-decisions",
|
||||
@@ -459,7 +714,10 @@ manifest = ModuleManifest(
|
||||
label="i18n:govoplan-core.product_area.meetings_decisions",
|
||||
icon="calendar",
|
||||
description="i18n:govoplan-core.product_area.meetings_decisions_description",
|
||||
surface_ids=("scheduling.nav.scheduling", "scheduling.route.scheduling"),
|
||||
surface_ids=(
|
||||
"scheduling.nav.scheduling",
|
||||
"scheduling.route.scheduling",
|
||||
),
|
||||
order=50,
|
||||
),
|
||||
),
|
||||
@@ -520,9 +778,21 @@ manifest = ModuleManifest(
|
||||
maturity="vertical_slice",
|
||||
documentation_ref="README.md",
|
||||
test_ref="tests/test_service.py",
|
||||
known_limits=("Reference deployment notification delivery and every calendar-provider constraint remain incomplete.",),
|
||||
owned_concepts=("scheduling request", "candidate slot", "scheduling participant", "public self-enrollment link", "scheduling decision"),
|
||||
non_owned_concepts=("poll response primitive", "calendar event", "mail delivery"),
|
||||
known_limits=(
|
||||
"Reference deployment notification delivery and every calendar-provider constraint remain incomplete.",
|
||||
),
|
||||
owned_concepts=(
|
||||
"scheduling request",
|
||||
"candidate slot",
|
||||
"scheduling participant",
|
||||
"public self-enrollment link",
|
||||
"scheduling decision",
|
||||
),
|
||||
non_owned_concepts=(
|
||||
"poll response primitive",
|
||||
"calendar event",
|
||||
"mail delivery",
|
||||
),
|
||||
recovery_docs=("README.md",),
|
||||
security_docs=("README.md",),
|
||||
),
|
||||
|
||||
+57
-12
@@ -7,6 +7,19 @@ from govoplan_scheduling.backend.manifest import get_manifest
|
||||
|
||||
|
||||
class SchedulingManifestTests(unittest.TestCase):
|
||||
def test_all_static_topics_have_complete_german_content(self) -> None:
|
||||
manifest = get_manifest()
|
||||
for topic in manifest.documentation:
|
||||
german = (topic.translations or {}).get("de", {})
|
||||
self.assertEqual(
|
||||
{"title", "summary", "body"},
|
||||
set(german),
|
||||
topic.id,
|
||||
)
|
||||
self.assertTrue(
|
||||
all(str(value).strip() for value in german.values()), topic.id
|
||||
)
|
||||
|
||||
def test_manifest_contract(self) -> None:
|
||||
manifest = get_manifest()
|
||||
|
||||
@@ -24,7 +37,9 @@ class SchedulingManifestTests(unittest.TestCase):
|
||||
self.assertIn("auth.principalResolver", manifest.optional_capabilities)
|
||||
self.assertIn("poll.scheduling", manifest.required_capabilities)
|
||||
self.assertIn("calendar.scheduling", manifest.optional_capabilities)
|
||||
self.assertIn("policy.schedulingParticipantPrivacy", manifest.optional_capabilities)
|
||||
self.assertIn(
|
||||
"policy.schedulingParticipantPrivacy", manifest.optional_capabilities
|
||||
)
|
||||
self.assertIn("access.people_search", manifest.optional_capabilities)
|
||||
self.assertIn("addresses.people_search", manifest.optional_capabilities)
|
||||
self.assertIn("evaluation", manifest.optional_dependencies)
|
||||
@@ -39,15 +54,41 @@ class SchedulingManifestTests(unittest.TestCase):
|
||||
],
|
||||
[route.path for route in manifest.frontend.public_routes],
|
||||
)
|
||||
self.assertIn("poll.availability_matrix", {interface.name for interface in manifest.requires_interfaces})
|
||||
self.assertIn("poll.response_collection", {interface.name for interface in manifest.requires_interfaces})
|
||||
self.assertIn("poll.workflow_context", {interface.name for interface in manifest.requires_interfaces})
|
||||
self.assertIn("poll.governed_participation", {interface.name for interface in manifest.requires_interfaces})
|
||||
self.assertIn("notifications.dispatch", {interface.name for interface in manifest.requires_interfaces})
|
||||
self.assertIn("access.people_search", {interface.name for interface in manifest.requires_interfaces})
|
||||
self.assertIn("addresses.people_search", {interface.name for interface in manifest.requires_interfaces})
|
||||
self.assertIn("calendar.scheduling", {interface.name for interface in manifest.requires_interfaces})
|
||||
required_interfaces = {interface.name: interface for interface in manifest.requires_interfaces}
|
||||
self.assertIn(
|
||||
"poll.availability_matrix",
|
||||
{interface.name for interface in manifest.requires_interfaces},
|
||||
)
|
||||
self.assertIn(
|
||||
"poll.response_collection",
|
||||
{interface.name for interface in manifest.requires_interfaces},
|
||||
)
|
||||
self.assertIn(
|
||||
"poll.workflow_context",
|
||||
{interface.name for interface in manifest.requires_interfaces},
|
||||
)
|
||||
self.assertIn(
|
||||
"poll.governed_participation",
|
||||
{interface.name for interface in manifest.requires_interfaces},
|
||||
)
|
||||
self.assertIn(
|
||||
"notifications.dispatch",
|
||||
{interface.name for interface in manifest.requires_interfaces},
|
||||
)
|
||||
self.assertIn(
|
||||
"access.people_search",
|
||||
{interface.name for interface in manifest.requires_interfaces},
|
||||
)
|
||||
self.assertIn(
|
||||
"addresses.people_search",
|
||||
{interface.name for interface in manifest.requires_interfaces},
|
||||
)
|
||||
self.assertIn(
|
||||
"calendar.scheduling",
|
||||
{interface.name for interface in manifest.requires_interfaces},
|
||||
)
|
||||
required_interfaces = {
|
||||
interface.name: interface for interface in manifest.requires_interfaces
|
||||
}
|
||||
for interface_name in (
|
||||
"poll.availability_matrix",
|
||||
"poll.response_collection",
|
||||
@@ -61,14 +102,18 @@ class SchedulingManifestTests(unittest.TestCase):
|
||||
self.assertEqual("workflow", workflow.metadata["kind"])
|
||||
self.assertEqual("/scheduling", workflow.metadata["route"])
|
||||
self.assertIn("scheduling.request", workflow.metadata["help_contexts"])
|
||||
self.assertIn("scheduling.public-participation", workflow.metadata["help_contexts"])
|
||||
self.assertIn(
|
||||
"scheduling.public-participation", workflow.metadata["help_contexts"]
|
||||
)
|
||||
self.assertIn(
|
||||
"scheduling.calendar-coordination",
|
||||
documentation["scheduling.calendar-coordination"].metadata["help_contexts"],
|
||||
)
|
||||
self.assertIn(
|
||||
"scheduling.public-self-enrollment",
|
||||
documentation["scheduling.public-self-enrollment"].metadata["help_contexts"],
|
||||
documentation["scheduling.public-self-enrollment"].metadata[
|
||||
"help_contexts"
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/scheduling-webui",
|
||||
"version": "0.1.18",
|
||||
"version": "0.1.20",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ const schedulingDashboardWidgets: DashboardWidgetsUiCapability = {
|
||||
export const schedulingModule: PlatformWebModule = {
|
||||
id: "scheduling",
|
||||
label: "Scheduling",
|
||||
version: "0.1.18",
|
||||
version: "0.1.20",
|
||||
dependencies: ["poll"],
|
||||
optionalDependencies: ["access", "calendar", "mail", "notifications", "workflow", "appointments", "addresses"],
|
||||
translations: generatedTranslations,
|
||||
|
||||
Reference in New Issue
Block a user