Files
Agent Zero 03dd477899
Check Cross-Plugin Imports / check (push) Has been cancelled
feat(N4): Restliche Module — Tasks/Kommunikation/Wiki/Reports/Agents/Tags/Search + Navigation + Dashboard-Schnittstelle (#368)
- Scope-Deklarationen: tasks only_mine, kommunikation conversation_ids, wiki category_ids (NEUE contracts.py), report_generator template_ids, automation agent_ids (module_key agents), tags tag_ids, unified_search entity_types dynamisch aus Provider-Registry
- Core-Beiträge: navigation default_route (Startseite) + dashboard widget_app_ids (Widget-TYP-Angebot, Layout bleibt Phase M)
- Backend-Filter (additive UND): /tasks (only_mine), /comm/conversations, /wiki/articles+/categories (Subtree), /reports/print-templates, /agents, /tags, /search GET+POST (entity_types-Schnitt), /miniapps?host=dashboard
- apply_entity_type_scope-Helper (requested ∧ scope)
- Frontend: WorkspaceSwitcher default_route-Navigation, Sidebar workspace-menu_order-Sortierung, workspaceStore moduleMenuOrder()
- Tests: 18/18 Deklarationen + 11/11 Filter (TDD), Frontend 2/2 + Store 18/18, tsc clean, Build OK
- Regression 64 passed (4 Kombi-Failures = Suite-Isolation, solo-bewiesen); Checker 0; Ruff = Vorbestand (Stash-bewiesen)
2026-09-01 23:23:15 +02:00

159 lines
4.7 KiB
Python

"""Public contract for the kommunikation plugin.
Exposes only the symbols that other builtins plugins need.
Importers should use::
from app.plugins.builtins.contracts import get_contract
komm = get_contract("kommunikation")
if komm:
await komm.send_message(db, tenant_id, ...)
instead of importing from internal modules directly.
"""
from __future__ import annotations
from typing import Any
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.plugins.builtins.contracts import get_contract_registry
from app.plugins.builtins.kommunikation.miniapp_registry import (
MiniAppDef,
MiniAppRegistry,
get_miniapp_registry,
reset_miniapp_registry,
)
from app.plugins.builtins.kommunikation.models import (
CommConversation,
CommMessage,
CommParticipant,
)
from app.plugins.builtins.kommunikation.participant_registry import (
ParticipantHandler,
get_participant_registry,
)
from app.plugins.builtins.kommunikation.services import (
create_plugin_room,
find_locked_room_id,
get_conversation,
get_messages,
parse_mentions,
send_message,
)
from app.plugins.builtins.kommunikation.services import post_system_message as _post_system_message
class KommunikationContract:
"""Public API surface for the kommunikation plugin.
Exposes functions, classes, and model types that other plugins are
allowed to use. Internal implementation details remain private to
the plugin package.
"""
contract_name = "kommunikation"
# ─── services ───
parse_mentions = staticmethod(parse_mentions)
get_conversation = staticmethod(get_conversation)
get_messages = staticmethod(get_messages)
send_message = staticmethod(send_message)
create_plugin_room = staticmethod(create_plugin_room)
find_locked_room_id = staticmethod(find_locked_room_id)
# ─── participant registry ───
get_participant_registry = staticmethod(get_participant_registry)
ParticipantHandler = ParticipantHandler
# ─── mini-app registry ───
MiniAppRegistry = MiniAppRegistry
MiniAppDef = MiniAppDef
get_miniapp_registry = staticmethod(get_miniapp_registry)
reset_miniapp_registry = staticmethod(reset_miniapp_registry)
post_system_message = staticmethod(_post_system_message)
# ─── models (read-only for queries) ───
CommConversation = CommConversation
CommMessage = CommMessage
CommParticipant = CommParticipant
@staticmethod
async def dsar_collect(
db: AsyncSession, tenant_id: Any, user_id: Any
) -> dict[str, Any]:
"""GDPR Art. 15: collect communication messages sent by the user."""
comm_messages = (
await db.execute(
select(CommMessage).where(
CommMessage.sender_id == user_id,
CommMessage.tenant_id == tenant_id,
).limit(1000)
)
).scalars().all()
return {
"comm_messages": [
{
"id": str(m.id),
"sender_type": m.sender_type,
"content": m.content[:500],
"created_at": m.created_at.isoformat() if m.created_at else None,
}
for m in comm_messages
]
}
# ─── Workspace Scopes contribution (Phase N4) ───
@staticmethod
def workspace_scopes() -> list[dict]:
"""Scope-Dimensionen des communication-Moduls: Räume-Teilmengen (N4)."""
return [
{
"module_key": "communication",
"dimensions": [
{
"key": "conversation_ids",
"label": "Räume",
"control": "multiselect",
"options": [],
"value_source": {
"endpoint": "/api/v1/comm/conversations",
"items_path": "items",
"value_key": "id",
"label_key": "title",
},
},
],
}
]
# ─── self-registration ───
_contract = KommunikationContract()
get_contract_registry().register("kommunikation", _contract)
__all__ = [
"KommunikationContract",
"ParticipantHandler",
"get_participant_registry",
"MiniAppRegistry",
"MiniAppDef",
"get_miniapp_registry",
"reset_miniapp_registry",
"parse_mentions",
"get_conversation",
"get_messages",
"send_message",
"create_plugin_room",
"find_locked_room_id",
"CommConversation",
"CommMessage",
"CommParticipant",
]