234 أسطر
6.3 KiB
Python
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()
|