الملفات
todo-mcp-server/database.py
2025-12-19 13:59:48 +03:00

234 أسطر
6.3 KiB
Python

"""
SQLite database manager for TODO MCP server.
"""
import sqlite3
from datetime import datetime
from typing import List, Dict, Any, Optional
from pathlib import Path
from date_utils import parse_relative_date, get_date_for_query
class TodoDatabase:
"""Manages SQLite database for TODO items"""
def __init__(self, db_path: str = "todos.db"):
"""
Initialize database connection.
Args:
db_path: Path to SQLite database file
"""
self.db_path = db_path
self.conn = None
self._init_database()
def _init_database(self):
"""Create database and tables if they don't exist"""
self.conn = sqlite3.connect(self.db_path, check_same_thread=False)
self.conn.row_factory = sqlite3.Row # Return rows as dictionaries
cursor = self.conn.cursor()
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
created_date TEXT NOT NULL,
due_date TEXT,
status TEXT NOT NULL DEFAULT 'pending',
completed_date TEXT
)
"""
)
self.conn.commit()
def create_todo(
self,
title: str,
description: str = "",
relative_days: Optional[int] = None,
) -> Dict[str, Any]:
"""
Create a new TODO item.
Args:
title: TODO title
description: TODO description
relative_days: Relative due date (0=today, +1=tomorrow, -1=yesterday, etc.)
Returns:
Created TODO item as dictionary
"""
created_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Convert relative days to absolute date
due_date = get_date_for_query(relative_days)
cursor = self.conn.cursor()
cursor.execute(
"""
INSERT INTO todos (title, description, created_date, due_date, status)
VALUES (?, ?, ?, ?, 'pending')
""",
(title, description, created_date, due_date),
)
self.conn.commit()
todo_id = cursor.lastrowid
return self.get_todo(todo_id)
def get_todo(self, todo_id: int) -> Optional[Dict[str, Any]]:
"""
Get a TODO by ID.
Args:
todo_id: TODO ID
Returns:
TODO item as dictionary or None if not found
"""
cursor = self.conn.cursor()
cursor.execute("SELECT * FROM todos WHERE id = ?", (todo_id,))
row = cursor.fetchone()
if row:
return dict(row)
return None
def list_todos(
self,
relative_days: Optional[int] = None,
status: Optional[str] = None,
) -> List[Dict[str, Any]]:
"""
List TODO items with optional filtering.
Args:
relative_days: Filter by relative due date (0=today, +1=tomorrow, etc.)
status: Filter by status ('pending', 'completed')
Returns:
List of TODO items
"""
query = "SELECT * FROM todos WHERE 1=1"
params = []
if relative_days is not None:
# Convert relative days to absolute date for filtering
date = parse_relative_date(relative_days)
query += " AND due_date LIKE ?"
params.append(f"{date}%")
if status:
query += " AND status = ?"
params.append(status)
query += " ORDER BY due_date ASC, created_date ASC"
cursor = self.conn.cursor()
cursor.execute(query, params)
rows = cursor.fetchall()
return [dict(row) for row in rows]
def update_todo(
self,
todo_id: int,
title: Optional[str] = None,
description: Optional[str] = None,
relative_days: Optional[int] = None,
) -> Optional[Dict[str, Any]]:
"""
Update a TODO item.
Args:
todo_id: TODO ID
title: New title (optional)
description: New description (optional)
relative_days: New relative due date (optional, 0=today, +1=tomorrow, etc.)
Returns:
Updated TODO item or None if not found
"""
# Build dynamic update query
updates = []
params = []
if title is not None:
updates.append("title = ?")
params.append(title)
if description is not None:
updates.append("description = ?")
params.append(description)
if relative_days is not None:
# Convert relative days to absolute date
due_date = parse_relative_date(relative_days)
updates.append("due_date = ?")
params.append(due_date)
if not updates:
return self.get_todo(todo_id)
params.append(todo_id)
query = f"UPDATE todos SET {', '.join(updates)} WHERE id = ?"
cursor = self.conn.cursor()
cursor.execute(query, params)
self.conn.commit()
if cursor.rowcount > 0:
return self.get_todo(todo_id)
return None
def mark_complete(self, todo_id: int) -> Optional[Dict[str, Any]]:
"""
Mark a TODO as completed.
Args:
todo_id: TODO ID
Returns:
Updated TODO item or None if not found
"""
completed_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
cursor = self.conn.cursor()
cursor.execute(
"""
UPDATE todos
SET status = 'completed', completed_date = ?
WHERE id = ?
""",
(completed_date, todo_id),
)
self.conn.commit()
if cursor.rowcount > 0:
return self.get_todo(todo_id)
return None
def delete_todo(self, todo_id: int) -> bool:
"""
Delete a TODO item.
Args:
todo_id: TODO ID
Returns:
True if deleted, False if not found
"""
cursor = self.conn.cursor()
cursor.execute("DELETE FROM todos WHERE id = ?", (todo_id,))
self.conn.commit()
return cursor.rowcount > 0
def close(self):
"""Close database connection"""
if self.conn:
self.conn.close()