[2f988f42] Feat: Automatisierter Session-Abschluss und Doku-Commit
This commit is contained in:
191
dev_session.py
191
dev_session.py
@@ -469,15 +469,11 @@ def generate_git_summary() -> Tuple[str, str]:
|
||||
except subprocess.CalledProcessError:
|
||||
main_branch = "HEAD~1" # Fallback, falls gar kein Branch gefunden wird
|
||||
|
||||
# Git diff --stat
|
||||
diff_stat_cmd = ["git", "diff", "--stat", f"{main_branch}...HEAD"]
|
||||
diff_stat = subprocess.check_output(diff_stat_cmd).decode("utf-8").strip()
|
||||
|
||||
# Git log --pretty
|
||||
commit_log_cmd = ["git", "log", "--pretty=format:- %s", f"{main_branch}...HEAD"]
|
||||
commit_messages = subprocess.check_output(commit_log_cmd).decode("utf-8").strip()
|
||||
|
||||
return diff_stat, commit_messages
|
||||
return "", commit_messages
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ FEHLER beim Generieren der Git-Zusammenfassung: {e}")
|
||||
return "", ""
|
||||
@@ -520,7 +516,7 @@ def report_status_to_notion(
|
||||
summary_override: Optional[str]
|
||||
) -> None:
|
||||
"""
|
||||
Erstellt einen Statusbericht für den Notion-Task, entweder interaktiv oder mit überschriebenen Werten.
|
||||
Erstellt einen automatisierten Statusbericht für den Notion-Task basierend auf Git-Commits.
|
||||
"""
|
||||
if not os.path.exists(SESSION_FILE_PATH):
|
||||
print("❌ FEHLER: Keine aktive Session gefunden. Kann keinen Statusbericht erstellen.")
|
||||
@@ -531,6 +527,7 @@ def report_status_to_notion(
|
||||
session_data = json.load(f)
|
||||
task_id = session_data.get("task_id")
|
||||
token = session_data.get("token")
|
||||
readme_path = session_data.get("readme_path", "readme.md") # Lade den Readme-Pfad
|
||||
|
||||
if not (task_id and token):
|
||||
print("❌ FEHLER: Session-Daten unvollständig. Kann keinen Statusbericht erstellen.")
|
||||
@@ -542,16 +539,9 @@ def report_status_to_notion(
|
||||
session_start_time = datetime.fromisoformat(session_start_time_str)
|
||||
elapsed_time = datetime.now() - session_start_time
|
||||
elapsed_hours = elapsed_time.total_seconds() / 3600
|
||||
|
||||
# Get current task page to read existing duration
|
||||
# Note: This is a simplified way. A more robust solution might query the DB
|
||||
# to get the page object without a separate API call if we already have it.
|
||||
# For now, a direct API call is clear and ensures we have the latest data.
|
||||
|
||||
task_page_url = f"https://api.notion.com/v1/pages/{task_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Notion-Version": "2022-06-28"
|
||||
}
|
||||
headers = {"Authorization": f"Bearer {token}", "Notion-Version": "2022-06-28"}
|
||||
try:
|
||||
page_response = requests.get(task_page_url, headers=headers)
|
||||
page_response.raise_for_status()
|
||||
@@ -560,157 +550,83 @@ def report_status_to_notion(
|
||||
current_duration = get_page_number_property(task_page, "Total Duration (h)") or 0.0
|
||||
new_total_duration = current_duration + elapsed_hours
|
||||
|
||||
duration_payload = {
|
||||
"Total Duration (h)": {
|
||||
"number": new_total_duration
|
||||
}
|
||||
}
|
||||
duration_payload = {"Total Duration (h)": {"number": new_total_duration}}
|
||||
update_notion_task_property(token, task_id, duration_payload)
|
||||
print(f"✅ Zeiterfassung: {elapsed_hours:.2f} Stunden zum Task hinzugefügt. Neue Gesamtdauer: {new_total_duration:.2f} Stunden.")
|
||||
|
||||
# Reset session start time for the next interval
|
||||
save_session_info(task_id, token)
|
||||
save_session_info(task_id, token, readme_path)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"❌ FEHLER beim Abrufen der Task-Details für die Zeiterfassung: {e}")
|
||||
|
||||
print(f"--- Erstelle Statusbericht für Task {task_id} ---")
|
||||
print(f"--- Erstelle automatischen Statusbericht für Task {task_id} ---")
|
||||
|
||||
# Git-Zusammenfassung generieren (immer, wenn nicht explizit überschrieben)
|
||||
actual_git_changes = git_changes_override
|
||||
actual_commit_messages = commit_messages_override
|
||||
if not git_changes_override or not commit_messages_override:
|
||||
print("Generiere Git-Zusammenfassung...")
|
||||
diff_stat, commit_log = generate_git_summary()
|
||||
if not git_changes_override:
|
||||
actual_git_changes = diff_stat
|
||||
if not commit_messages_override:
|
||||
actual_commit_messages = commit_log
|
||||
|
||||
# Status abfragen oder übernehmen
|
||||
actual_status = status_override
|
||||
if not actual_status:
|
||||
tasks_db_id = find_database_by_title(token, "Tasks [UT]")
|
||||
if tasks_db_id:
|
||||
status_options = get_database_status_options(token, tasks_db_id)
|
||||
if status_options:
|
||||
print("\nBitte wähle den neuen Status des Tasks:")
|
||||
for i, option in enumerate(status_options):
|
||||
print(f"[{i+1}] {option}")
|
||||
while True:
|
||||
try:
|
||||
choice = int(input("Wähle eine Nummer: "))
|
||||
if 1 <= choice <= len(status_options):
|
||||
actual_status = status_options[choice - 1]
|
||||
break
|
||||
else:
|
||||
print("Ungültige Auswahl.")
|
||||
except ValueError:
|
||||
print("Ungültige Eingabe. Bitte eine Zahl eingeben.")
|
||||
else:
|
||||
print("❌ FEHLER: Konnte Status-Optionen nicht abrufen. Abbruch des Berichts.")
|
||||
return
|
||||
# Git-Zusammenfassung generieren
|
||||
print("Generiere Git-Zusammenfassung...")
|
||||
_, commit_log = generate_git_summary()
|
||||
actual_commit_messages = commit_log
|
||||
|
||||
if not actual_status:
|
||||
print("❌ FEHLER: Kein Status festgelegt. Abbruch des Berichts.")
|
||||
return
|
||||
|
||||
# Detaillierte Zusammenfassung abfragen oder übernehmen
|
||||
actual_summary = summary_override
|
||||
if not actual_summary:
|
||||
print("\nBitte gib eine Zusammenfassung der Arbeit ein (was wurde getan, Ergebnisse, Probleme etc.).")
|
||||
user_summary_lines = []
|
||||
while True:
|
||||
line = input()
|
||||
if not line:
|
||||
break
|
||||
user_summary_lines.append(line)
|
||||
actual_summary = "\n".join(user_summary_lines)
|
||||
|
||||
# To-Dos abfragen oder übernehmen
|
||||
actual_todos = todos_override
|
||||
if not actual_todos:
|
||||
user_todos = input("\nGibt es offene To-Dos oder nächste Schritte? (Leer lassen zum Überspringen): ")
|
||||
actual_todos = user_todos.strip()
|
||||
# Summary automatisch aus Commit-Nachrichten erstellen
|
||||
actual_summary = summary_override or "Keine Zusammenfassung angegeben."
|
||||
if not summary_override:
|
||||
_, commit_log = generate_git_summary()
|
||||
summary_from_commits = commit_log.replace("- ", "✦ ").strip()
|
||||
if summary_from_commits:
|
||||
actual_summary = summary_from_commits
|
||||
else:
|
||||
actual_summary = "Keine neuen Commits in dieser Session."
|
||||
|
||||
# Kommentar zusammenstellen
|
||||
report_lines = []
|
||||
# Diese Zeilen werden jetzt innerhalb des Code-Blocks formatiert
|
||||
|
||||
# Add invested time to the report if available
|
||||
if 'elapsed_hours' in locals():
|
||||
elapsed_hhmm = decimal_hours_to_hhmm(elapsed_hours)
|
||||
report_lines.append(f"Investierte Zeit in dieser Session: {elapsed_hhmm}")
|
||||
|
||||
report_lines.append(f"Neuer Status: {actual_status}")
|
||||
|
||||
if actual_summary:
|
||||
report_lines.append("\nArbeitszusammenfassung:")
|
||||
report_lines.append(actual_summary)
|
||||
report_lines.append("\nArbeitszusammenfassung:")
|
||||
report_lines.append(actual_summary)
|
||||
|
||||
if actual_git_changes or actual_commit_messages:
|
||||
report_lines.append("\nTechnische Änderungen (Git):")
|
||||
if actual_git_changes:
|
||||
report_lines.append(f"```{actual_git_changes}```")
|
||||
if actual_commit_messages:
|
||||
report_lines.append("\nCommit Nachrichten:")
|
||||
report_lines.append(f"```{actual_commit_messages}```")
|
||||
|
||||
if actual_todos:
|
||||
report_lines.append("\nOffene To-Dos / Nächste Schritte:")
|
||||
for todo_item in actual_todos.split('\n'):
|
||||
report_lines.append(f"- {todo_item.strip()}")
|
||||
|
||||
report_content = "\n".join(report_lines)
|
||||
|
||||
# Notion Blöcke für die API erstellen
|
||||
timestamp = datetime.now(BERLIN_TZ).strftime('%Y-%m-%d %H:%M')
|
||||
notion_blocks = [
|
||||
{
|
||||
"object": "block",
|
||||
"type": "heading_2",
|
||||
"heading_2": {
|
||||
"rich_text": [{"type": "text", "text": {"content": f"🤖 Status-Update ({timestamp} Berlin Time)"}}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"object": "block",
|
||||
"type": "code",
|
||||
"code": {
|
||||
"rich_text": [{"type": "text", "text": {"content": report_content}}],
|
||||
"language": "yaml"
|
||||
}
|
||||
}
|
||||
{"object": "block", "type": "heading_2", "heading_2": {"rich_text": [{"type": "text", "text": {"content": f"🤖 Status-Update ({timestamp} Berlin Time)"}}]}},
|
||||
{"object": "block", "type": "code", "code": {"rich_text": [{"type": "text", "text": {"content": report_content}}], "language": "yaml"}}
|
||||
]
|
||||
|
||||
# Notion aktualisieren
|
||||
# Notion aktualisieren (nur Kommentar/Block, kein Status)
|
||||
append_blocks_to_notion_page(token, task_id, notion_blocks)
|
||||
status_payload = {"Status": {"status": {"name": actual_status}}}
|
||||
update_notion_task_property(token, task_id, status_payload)
|
||||
|
||||
# --- Git Operationen ---
|
||||
# --- Doku & Git Operationen ---
|
||||
print("\n--- Führe Git-Operationen aus ---")
|
||||
try:
|
||||
# Doku-Update-Logik (vor dem Staging)
|
||||
git_status_output_doku = subprocess.run(["git", "status", "--porcelain", readme_path], capture_output=True, text=True).stdout.strip()
|
||||
if git_status_output_doku:
|
||||
print(f"Relevante Dokumentation '{readme_path}' wurde geändert. Committe Änderungen...")
|
||||
# In einer echten Implementierung könnte hier eine KI-Zusammenfassung stehen.
|
||||
# Vorerst wird nur die vorhandene Änderung committet.
|
||||
subprocess.run(["git", "add", readme_path], check=True)
|
||||
doku_commit_msg = f"Docs: Aktualisierung der Dokumentation für Task [{task_id.split('-')[0]}]"
|
||||
subprocess.run(["git", "commit", "-m", doku_commit_msg], check=True)
|
||||
print("✅ Dokumentation committet.")
|
||||
|
||||
# Stage alle anderen Änderungen
|
||||
subprocess.run(["git", "add", "."], check=True)
|
||||
print("✅ Alle Änderungen gestaged (git add .).")
|
||||
|
||||
# Prüfen, ob es Änderungen zum Committen gibt
|
||||
git_status_output = subprocess.run(["git", "status", "--porcelain"], capture_output=True, text=True, check=True).stdout.strip()
|
||||
if not git_status_output:
|
||||
print("⚠️ Keine Änderungen zum Committen gefunden. Überspringe git commit.")
|
||||
return # Beende die Funktion, da nichts zu tun ist
|
||||
git_status_output_rest = subprocess.run(["git", "status", "--porcelain"], capture_output=True, text=True, check=True).stdout.strip()
|
||||
if git_status_output_rest:
|
||||
# Commit-Nachricht erstellen
|
||||
commit_subject = actual_summary.splitlines()[0] if actual_summary else "Notion Status Update"
|
||||
commit_message = f"[{task_id.split('-')[0]}] {commit_subject}\n\n{actual_summary}"
|
||||
subprocess.run(["git", "commit", "-m", commit_message], check=True)
|
||||
print("✅ Code-Änderungen committet.")
|
||||
else:
|
||||
print("⚠️ Keine weiteren Code-Änderungen zum Committen gefunden.")
|
||||
|
||||
# Commit-Nachricht erstellen
|
||||
commit_subject = actual_summary.splitlines()[0] if actual_summary else "Notion Status Update"
|
||||
commit_message = f"[{task_id.split('-')[0]}] {commit_subject}\n\n{actual_summary}"
|
||||
|
||||
subprocess.run(["git", "commit", "-m", commit_message], check=True)
|
||||
print("✅ Git commit erfolgreich.")
|
||||
|
||||
# Interaktive Abfrage für git push
|
||||
push_choice = input("\n✅ Commit erfolgreich erstellt. Sollen die Änderungen jetzt gepusht werden? (j/n): ").lower()
|
||||
if push_choice == 'j':
|
||||
git_push_with_retry()
|
||||
# Änderungen automatisch pushen
|
||||
git_push_with_retry()
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ FEHLER bei Git-Operationen: {e}")
|
||||
@@ -759,12 +675,13 @@ def generate_cli_context(project_title: str, task_title: str, task_id: str, read
|
||||
|
||||
# Die start_gemini_cli Funktion wird entfernt, da das aufrufende Skript jetzt die Gemini CLI startet.
|
||||
|
||||
def save_session_info(task_id: str, token: str):
|
||||
"""Speichert die Task-ID, den Token und den Startzeitpunkt für den Git-Hook."""
|
||||
def save_session_info(task_id: str, token: str, readme_path: Optional[str]):
|
||||
"""Speichert die Task-ID, den Token, den Readme-Pfad und den Startzeitpunkt."""
|
||||
os.makedirs(SESSION_DIR, exist_ok=True)
|
||||
session_data = {
|
||||
"task_id": task_id,
|
||||
"token": token,
|
||||
"readme_path": readme_path,
|
||||
"session_start_time": datetime.now().isoformat()
|
||||
}
|
||||
with open(SESSION_FILE_PATH, "w") as f:
|
||||
@@ -867,7 +784,7 @@ def start_interactive_session():
|
||||
print(f"> Bisher für diesen Task erfasst: {total_duration_hhmm} Stunden.")
|
||||
|
||||
# Session-Informationen für den Git-Hook speichern
|
||||
save_session_info(task_id, token)
|
||||
save_session_info(task_id, token, readme_path)
|
||||
|
||||
# Git-Hook installieren, der die Session-Infos nutzt
|
||||
install_git_hook()
|
||||
|
||||
Reference in New Issue
Block a user