Files
ws-sanctum-chronicler/app/exports/markdown.py

143 lines
4.8 KiB
Python

"""Markdown export functionality for stream ledgers."""
import logging
from datetime import datetime
from pathlib import Path
from app.config import settings
from app.memory.database import get_session
from app.memory.repository import Repository
logger = logging.getLogger(__name__)
class MarkdownExporter:
"""Exports stream session data as markdown ledgers."""
def __init__(self, export_path: str | None = None):
"""
Initialize exporter.
Args:
export_path: Directory to export ledgers to (defaults to settings.EXPORT_PATH)
"""
self.export_path = Path(export_path or settings.EXPORT_PATH)
self.export_path.mkdir(parents=True, exist_ok=True)
async def export_session(self, session_id: str) -> str:
"""
Export a session as a markdown ledger.
Args:
session_id: Session ID to export
Returns:
Markdown content
"""
async for db_session in get_session():
repo = Repository(db_session)
session = await repo.get_session(session_id)
if not session:
logger.warning(f"Session {session_id} not found")
return ""
# Gather data
messages = await repo.get_recent_messages(session_id, limit=1000)
actions = await repo.get_session_actions(session_id)
clips = await repo.get_clip_candidates(session_id)
seeds = await repo.get_blog_seeds(session_id)
dashboard = await repo.get_dashboard(session_id)
dashboard_data = Repository.serialize_dashboard(dashboard)
# Build markdown
date = session.started_at.strftime("%Y-%m-%d")
ledger = f"# Sanctum Ledger — {date}\n\n"
ledger += f"**Channel:** {session.channel_name}\n"
ledger += f"**Started:** {session.started_at.isoformat()}\n"
if session.ended_at:
ledger += f"**Ended:** {session.ended_at.isoformat()}\n"
ledger += "\n"
# Stream Theme
ledger += "## Stream Theme\n"
if dashboard_data:
if dashboard_data.get("stream_title"):
ledger += f"**Title:** {dashboard_data['stream_title']}\n"
if dashboard_data.get("game"):
ledger += f"**Game:** {dashboard_data['game']}\n"
if dashboard_data.get("mood"):
ledger += f"**Mood:** {dashboard_data['mood']}\n"
if dashboard_data.get("content_angle"):
ledger += f"**Content Angle:** {dashboard_data['content_angle']}\n"
if dashboard_data.get("session_goals"):
ledger += "\n**Session Goals**\n"
for goal in dashboard_data["session_goals"]:
ledger += f"- {goal}\n"
elif session.theme:
ledger += f"{session.theme}\n"
else:
ledger += "*No theme recorded*\n"
ledger += "\n"
# Notable Discussion
ledger += "## Notable Discussion\n"
if messages:
for msg in messages[:20]: # Latest 20 messages
ledger += f"- **{msg.username}:** {msg.content[:100]}\n"
else:
ledger += "*No messages recorded*\n"
ledger += "\n"
# Agent Actions
ledger += "## Agent Actions\n"
if actions:
for action in actions:
ledger += f"- **{action.mode}** ({action.action_type}): {action.description}\n"
else:
ledger += "*No agent actions recorded*\n"
ledger += "\n"
# Clip Candidates
ledger += "## Clip Candidates\n"
if clips:
for clip in clips:
ledger += f"- {clip.reason}\n"
else:
ledger += "*No clip candidates identified*\n"
ledger += "\n"
# Blog Seeds
ledger += "## Blog Seeds\n"
if seeds:
for seed in seeds:
ledger += f"- **{seed.topic}:** {seed.description}\n"
else:
ledger += "*No blog seeds proposed*\n"
ledger += "\n"
logger.info(f"Generated ledger for session {session_id}")
return ledger
async def save_session_ledger(self, session_id: str) -> Path:
"""
Export session and save to file.
Args:
session_id: Session ID
Returns:
Path to saved file
"""
ledger = await self.export_session(session_id)
date = datetime.utcnow().strftime("%Y-%m-%d")
filename = f"ledger_{date}_{session_id[:8]}.md"
filepath = self.export_path / filename
filepath.write_text(ledger, encoding="utf-8")
logger.info(f"Saved ledger to {filepath}")
return filepath