Compare commits
7 Commits
a01a6e5c7d
...
feature/ta
| Author | SHA1 | Date | |
|---|---|---|---|
| e57aa374ea | |||
| eb3f77f092 | |||
| b396e54080 | |||
| c4baf68595 | |||
| 0a1be647c4 | |||
| 01e5ae8b5c | |||
| adafab61ae |
@@ -1 +1 @@
|
||||
{"task_id": "2f488f42-8544-81ac-a9f8-e373c4c18115", "token": "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", "session_start_time": "2026-01-26T18:39:11.157549"}
|
||||
{"task_id": "2f488f42-8544-819a-8407-f29748b3e0b8", "token": "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"}
|
||||
170
dev_session.py
170
dev_session.py
@@ -1,14 +1,10 @@
|
||||
import re
|
||||
from typing import List, Dict, Optional, Tuple, Any
|
||||
from getpass import getpass
|
||||
from dotenv import load_dotenv
|
||||
import subprocess
|
||||
from datetime import datetime, timedelta
|
||||
import os
|
||||
import requests
|
||||
import json
|
||||
import argparse
|
||||
import shutil
|
||||
import re
|
||||
from typing import List, Dict, Optional, Tuple
|
||||
from getpass import getpass
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@@ -87,48 +83,18 @@ def get_page_title(page: Dict) -> str:
|
||||
return title_parts[0].get("plain_text", "Unbenannt")
|
||||
return "Unbenannt"
|
||||
|
||||
def get_page_rich_text_property(page: Dict, prop_name: str) -> Optional[str]:
|
||||
def get_page_property(page: Dict, prop_name: str, prop_type: str = "rich_text") -> Optional[str]:
|
||||
"""Extrahiert den Inhalt einer bestimmten Eigenschaft (Property) von einer Seite."""
|
||||
prop = page.get("properties", {}).get(prop_name)
|
||||
if not prop:
|
||||
return None
|
||||
|
||||
if prop.get("type") == "rich_text":
|
||||
if prop_type == "rich_text" and prop.get("type") == "rich_text":
|
||||
text_parts = prop.get("rich_text", [])
|
||||
if text_parts:
|
||||
return text_parts[0].get("plain_text")
|
||||
|
||||
return None
|
||||
|
||||
def get_page_number_property(page: Dict, prop_name: str) -> Optional[float]:
|
||||
"""Extrahiert den Inhalt einer Number-Eigenschaft von einer Seite."""
|
||||
prop = page.get("properties", {}).get(prop_name)
|
||||
if not prop:
|
||||
return None
|
||||
|
||||
if prop.get("type") == "number":
|
||||
return prop.get("number")
|
||||
|
||||
return None
|
||||
|
||||
def get_page_date_property(page: Dict, prop_name: str) -> Optional[datetime]:
|
||||
"""Extrahiert den Inhalt einer Date-Eigenschaft von einer Seite."""
|
||||
prop = page.get("properties", {}).get(prop_name)
|
||||
if not prop:
|
||||
return None
|
||||
|
||||
if prop.get("type") == "date":
|
||||
date_info = prop.get("date")
|
||||
if date_info and date_info.get("start"):
|
||||
try:
|
||||
# Notion gibt Datumszeiten im ISO 8601 Format zurück (z.B. '2023-10-27T10:00:00.000+00:00')
|
||||
# oder nur Datum (z.B. '2023-10-27')
|
||||
# datetime.fromisoformat kann beides verarbeiten, aber Zeitzonen können komplex sein.
|
||||
# Für unsere Zwecke reicht es, wenn es als UTC betrachtet wird und wir die Dauer berechnen.
|
||||
return datetime.fromisoformat(date_info["start"].replace('Z', '+00:00'))
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
# Hier könnten weitere Typen wie 'select', 'number' etc. behandelt werden
|
||||
return None
|
||||
|
||||
def get_page_content(token: str, page_id: str) -> str:
|
||||
@@ -227,42 +193,32 @@ def get_database_status_options(token: str, db_id: str) -> List[str]:
|
||||
print(f"Fehler beim Abrufen der Datenbank-Eigenschaften: {e}")
|
||||
return []
|
||||
|
||||
def update_notion_task_property(token: str, task_id: str, prop_name: str, prop_value: Any, prop_type: str) -> bool:
|
||||
"""Aktualisiert eine bestimmte Eigenschaft (Property) eines Notion-Tasks."""
|
||||
print(f"\n--- Aktualisiere Task '{task_id}', Eigenschaft '{prop_name}' ({prop_type}) mit Wert '{prop_value}'... ---")
|
||||
def update_notion_task_status(token: str, task_id: str, status_value: str = "Doing") -> bool:
|
||||
"""Aktualisiert den Status eines Notion-Tasks."""
|
||||
print(f"\n--- Aktualisiere Status von Task '{task_id}' auf '{status_value}'... ---")
|
||||
url = f"https://api.notion.com/v1/pages/{task_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json",
|
||||
"Notion-Version": "2022-06-28"
|
||||
}
|
||||
|
||||
payload_properties = {}
|
||||
if prop_type == "status":
|
||||
payload_properties[prop_name] = {"status": {"name": prop_value}}
|
||||
elif prop_type == "number":
|
||||
payload_properties[prop_name] = {"number": prop_value}
|
||||
elif prop_type == "date":
|
||||
# Notion erwartet Datum im ISO 8601 Format
|
||||
if isinstance(prop_value, datetime):
|
||||
payload_properties[prop_name] = {"date": {"start": prop_value.isoformat()}}
|
||||
else:
|
||||
print(f"❌ FEHLER: Ungültiges Datumformat für '{prop_name}'. Erwartet datetime-Objekt.")
|
||||
return False
|
||||
# Weitere Typen können hier hinzugefügt werden (z.B. rich_text, multi_select etc.)
|
||||
else:
|
||||
print(f"❌ FEHLER: Nicht unterstützter Eigenschaftstyp '{prop_type}' für Update.")
|
||||
return False
|
||||
|
||||
payload = {"properties": payload_properties}
|
||||
payload = {
|
||||
"properties": {
|
||||
"Status": {
|
||||
"status": {
|
||||
"name": status_value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.patch(url, headers=headers, json=payload)
|
||||
response.raise_for_status()
|
||||
print(f"✅ Task-Eigenschaft '{prop_name}' erfolgreich aktualisiert.")
|
||||
print(f"✅ Task-Status erfolgreich auf '{status_value}' aktualisiert.")
|
||||
return True
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"❌ FEHLER beim Aktualisieren der Task-Eigenschaft '{prop_name}': {e}")
|
||||
print(f"❌ FEHLER beim Aktualisieren des Task-Status: {e}")
|
||||
try:
|
||||
print(f"Antwort des Servers: {json.dumps(e.response.json(), indent=2)}")
|
||||
except:
|
||||
@@ -439,7 +395,7 @@ def select_project(token: str) -> Optional[Tuple[Dict, Optional[str]]]:
|
||||
choice = int(input("Bitte wähle eine Nummer: "))
|
||||
if 1 <= choice <= len(projects):
|
||||
selected_project = projects[choice - 1]
|
||||
readme_path = get_page_rich_text_property(selected_project, "Readme Path")
|
||||
readme_path = get_page_property(selected_project, "Readme Path")
|
||||
return selected_project, readme_path
|
||||
else:
|
||||
print("Ungültige Auswahl.")
|
||||
@@ -535,68 +491,13 @@ def report_status_to_notion(
|
||||
session_data = json.load(f)
|
||||
task_id = session_data.get("task_id")
|
||||
token = session_data.get("token")
|
||||
session_start_time_str = session_data.get("session_start_time")
|
||||
|
||||
if not (task_id and token and session_start_time_str):
|
||||
print("❌ FEHLER: Session-Daten unvollständig oder Startzeit fehlt. Kann keinen Statusbericht erstellen.")
|
||||
return
|
||||
|
||||
try:
|
||||
session_start_time = datetime.fromisoformat(session_start_time_str)
|
||||
except ValueError:
|
||||
print(f"❌ FEHLER: Ungültiges Startzeitformat in Session-Daten: {session_start_time_str}")
|
||||
if not (task_id and token):
|
||||
print("❌ FEHLER: Session-Daten unvollständig. Kann keinen Statusbericht erstellen.")
|
||||
return
|
||||
|
||||
print(f"--- Erstelle Statusbericht für Task {task_id} ---")
|
||||
|
||||
tasks_db_id = find_database_by_title(token, "Tasks [UT]")
|
||||
if not tasks_db_id:
|
||||
return
|
||||
|
||||
# 1. Aktuelles Task-Objekt abrufen, um die vorhandene Dauer zu lesen
|
||||
page_url = f"https://api.notion.com/v1/pages/{task_id}"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json",
|
||||
"Notion-Version": "2022-06-28"
|
||||
}
|
||||
current_task_page = None
|
||||
try:
|
||||
response = requests.get(page_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
current_task_page = response.json()
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"❌ FEHLER beim Abrufen der Task-Seite {task_id}: {e}")
|
||||
return
|
||||
|
||||
if not current_task_page:
|
||||
print(f"❌ FEHLER: Konnte den Task {task_id} in Notion nicht finden. Zeiterfassung wird übersprungen.")
|
||||
return
|
||||
|
||||
# 2. Bestehende Total Duration (h) abrufen
|
||||
existing_duration = get_page_number_property(current_task_page, "Total Duration (h)")
|
||||
if existing_duration is None:
|
||||
existing_duration = 0.0
|
||||
|
||||
# 3. Dauer der aktuellen Arbeitseinheit berechnen
|
||||
current_end_time = datetime.now()
|
||||
time_spent = (current_end_time - session_start_time).total_seconds() / 3600.0 # Dauer in Stunden
|
||||
|
||||
# 4. Neue Gesamtdauer berechnen
|
||||
new_total_duration = existing_duration + time_spent
|
||||
|
||||
# 5. Total Duration (h) in Notion aktualisieren
|
||||
update_notion_task_property(token, task_id, "Total Duration (h)", round(new_total_duration, 2), "number")
|
||||
|
||||
# 6. session_start_time in SESSION_INFO für die nächste Arbeitseinheit aktualisieren
|
||||
# Hier speichern wir die Endzeit als neue Startzeit für die nächste mögliche Einheit
|
||||
# oder löschen sie, wenn die Session als beendet betrachtet wird.
|
||||
# Entsprechend des Plans setzen wir sie zurück (auf die aktuelle Endzeit der Arbeitseinheit).
|
||||
session_data["session_start_time"] = current_end_time.isoformat()
|
||||
with open(SESSION_FILE_PATH, "w") as f:
|
||||
json.dump(session_data, f)
|
||||
|
||||
|
||||
# Git-Zusammenfassung generieren (immer, wenn nicht explizit überschrieben)
|
||||
actual_git_changes = git_changes_override
|
||||
actual_commit_messages = commit_messages_override
|
||||
@@ -700,7 +601,7 @@ def report_status_to_notion(
|
||||
|
||||
# Notion aktualisieren
|
||||
append_blocks_to_notion_page(token, task_id, notion_blocks)
|
||||
update_notion_task_property(token, task_id, "Status", actual_status, "status")
|
||||
update_notion_task_status(token, task_id, actual_status)
|
||||
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
print(f"❌ FEHLER beim Lesen der Session-Informationen für Statusbericht: {e}")
|
||||
@@ -740,13 +641,20 @@ 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, session_start_time: datetime):
|
||||
"""Speichert die Task-ID, den Token und die Startzeit der Session."""
|
||||
import shutil
|
||||
import argparse
|
||||
|
||||
# --- Session Management ---
|
||||
|
||||
SESSION_DIR = ".dev_session"
|
||||
SESSION_FILE_PATH = os.path.join(SESSION_DIR, "SESSION_INFO")
|
||||
|
||||
def save_session_info(task_id: str, token: str):
|
||||
"""Speichert die Task-ID und den Token für den Git-Hook."""
|
||||
os.makedirs(SESSION_DIR, exist_ok=True)
|
||||
session_data = {
|
||||
"task_id": task_id,
|
||||
"token": token,
|
||||
"session_start_time": session_start_time.isoformat() # Speichern als ISO-Format String
|
||||
"token": token
|
||||
}
|
||||
with open(SESSION_FILE_PATH, "w") as f:
|
||||
json.dump(session_data, f)
|
||||
@@ -805,7 +713,7 @@ def complete_session():
|
||||
status_options = get_database_status_options(token, tasks_db_id)
|
||||
if status_options:
|
||||
done_status = status_options[-1]
|
||||
update_notion_task_property(token, task_id, "Status", done_status, "status")
|
||||
update_notion_task_status(token, task_id, done_status)
|
||||
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
print("Fehler beim Lesen der Session-Informationen.")
|
||||
@@ -863,7 +771,7 @@ def start_interactive_session():
|
||||
task_description = get_page_content(token, task_id)
|
||||
|
||||
# Session-Informationen für den Git-Hook speichern
|
||||
save_session_info(task_id, token, datetime.now())
|
||||
save_session_info(task_id, token)
|
||||
|
||||
# Git-Hook installieren, der die Session-Infos nutzt
|
||||
install_git_hook()
|
||||
@@ -874,7 +782,7 @@ def start_interactive_session():
|
||||
|
||||
suggested_branch_name = f"feature/task-{task_id.split('-')[0]}-{title_slug}"
|
||||
|
||||
status_updated = update_notion_task_property(token, task_id, "Status", "Doing", "status")
|
||||
status_updated = update_notion_task_status(token, task_id, "Doing")
|
||||
if not status_updated:
|
||||
print("Warnung: Notion-Task-Status konnte nicht aktualisiert werden.")
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user