Files
tracker-cli/tracker/storage/markdown_writer.py
Daniel Arroyo 4547c492da Implement storage layer for MVP-1 Personal Tracker CLI
Add storage layer with FileStorage, MarkdownReader, and MarkdownWriter classes.
Add data models (Project, Session, Note, Change).
2026-03-23 08:54:00 -03:00

242 lines
6.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Markdown writer utility."""
import re
from datetime import datetime
from typing import Optional
from tracker.models.session import Session
class MarkdownWriter:
"""Escritura de archivos Markdown del proyecto."""
def format_log_entry(self, session: Session, summary: str) -> str:
"""Formatea una entrada para LOG.md.
Formato:
## 2026-03-23 10:0011:20
**Objetivo**
...
**Trabajo realizado**
- ...
**Cambios relevantes**
- ...
**Bloqueos**
- ...
**Decisiones**
- ...
**Próximos pasos**
- ...
**Resumen**
...
Returns string formateado.
"""
started = session.started_at.strftime("%Y-%m-%d %H:%M")
ended = session.ended_at.strftime("%H:%M") if session.ended_at else "En Curso"
date_range = f"{started}{ended}"
lines = [
f"## {date_range}",
"",
"**Objetivo**",
f"{session.objective or 'No especificado'}",
"",
"**Trabajo realizado**",
]
if session.work_done:
for item in session.work_done:
lines.append(f"- {item}")
else:
lines.append("- Sin trabajo registrado")
lines.extend(["", "**Cambios relevantes**"])
if session.changes:
for item in session.changes:
lines.append(f"- {item}")
else:
lines.append("- Sin cambios")
lines.extend(["", "**Bloqueos**"])
if session.blockers:
for item in session.blockers:
lines.append(f"- {item}")
else:
lines.append("- Sin bloqueos")
lines.extend(["", "**Decisiones**"])
if session.decisions:
for item in session.decisions:
lines.append(f"- {item}")
else:
lines.append("- Sin decisiones")
lines.extend(["", "**Próximos pasos**"])
if session.next_steps:
for item in session.next_steps:
lines.append(f"- {item}")
else:
lines.append("- Sin pasos definidos")
lines.extend(["", "**Resumen**", summary])
return "\n".join(lines) + "\n\n"
def format_session_file(self, session: Session) -> str:
"""Formatea archivo de sesion detalle en sessions/YYYY-MM-DD_HHMM.md.
Formato:
# Sesion: 2026-03-23 10:0011:20
## Objetivo
...
## Notas
...
## Trabajo realizado
...
## Cambios
...
## Decisiones
...
## Bloqueos
...
## Proximos pasos
...
## Referencias
...
## Duracion
X minutos
"""
started = session.started_at.strftime("%Y-%m-%d %H:%M")
ended = session.ended_at.strftime("%H:%M") if session.ended_at else "En Curso"
lines = [
f"# Sesion: {started}{ended}",
"",
"## Objetivo",
f"{session.objective or 'No especificado'}",
"",
"## Notas",
]
if session.raw_notes:
for note in session.raw_notes:
note_type = note.get("type", "work")
note_text = note.get("text", "")
lines.append(f"- [{note_type}] {note_text}")
else:
lines.append("- Sin notas")
lines.extend([
"",
"## Trabajo realizado",
])
if session.work_done:
for item in session.work_done:
lines.append(f"- {item}")
else:
lines.append("- Sin trabajo realizado")
lines.extend([
"",
"## Cambios",
])
if session.changes:
for item in session.changes:
lines.append(f"- {item}")
else:
lines.append("- Sin cambios")
lines.extend([
"",
"## Decisiones",
])
if session.decisions:
for item in session.decisions:
lines.append(f"- {item}")
else:
lines.append("- Sin decisiones")
lines.extend([
"",
"## Bloqueos",
])
if session.blockers:
for item in session.blockers:
lines.append(f"- {item}")
else:
lines.append("- Sin bloqueos")
lines.extend([
"",
"## Proximos pasos",
])
if session.next_steps:
for item in session.next_steps:
lines.append(f"- {item}")
else:
lines.append("- Sin pasos definidos")
lines.extend([
"",
"## Referencias",
])
if session.references:
for item in session.references:
lines.append(f"- {item}")
else:
lines.append("- Sin referencias")
lines.extend([
"",
"## Duracion",
f"{session.duration_minutes} minutos",
])
return "\n".join(lines) + "\n"
def format_autogen_section(self, content: str, section: str, new_content: str) -> str:
"""Reemplaza o inserta una seccion AUTOGEN en contenido Markdown.
Busca <!-- AUTOGEN:{section}_START --> ... <!-- AUTOGEN:{section}_END -->
Si existe, reemplaza el contenido entre los marcadores.
Si no existe, inserta la seccion al final.
Returns el contenido modificado.
"""
start_marker = f"<!-- AUTOGEN:{section}_START -->"
end_marker = f"<!-- AUTOGEN:{section}_END -->"
full_marker = f"{start_marker}\n{new_content}\n{end_marker}"
# Buscar si existe la seccion
pattern = rf"{re.escape(start_marker)}.*?{re.escape(end_marker)}"
if re.search(pattern, content, re.DOTALL):
# Reemplazar seccion existente
return re.sub(pattern, full_marker, content, flags=re.DOTALL)
else:
# Insertar al final
return content + "\n" + full_marker + "\n"
def format_readme_section(self, section: str, content: str) -> str:
"""Formatea una seccion de README.md.
Para usar con format_autogen_section.
"""
return content