docs(scheduling): complete German reference coverage
Module Package Release / publish-packages (push) Successful in 12s

This commit is contained in:
2026-08-23 20:11:47 +02:00
parent 0fd297231b
commit 118fe395b0
6 changed files with 367 additions and 52 deletions
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -2,4 +2,4 @@
__all__ = ["__version__"]
__version__ = "0.1.18"
__version__ = "0.1.20"
+306 -36
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,