database.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. """
  2. Database connection and initialization module.
  3. Manages SQLite database connection and table creation.
  4. """
  5. import sqlite3
  6. import os
  7. from contextlib import contextmanager
  8. from typing import Generator
  9. # Database file path
  10. DB_PATH = os.getenv("DATABASE_PATH", "annotation_platform.db")
  11. @contextmanager
  12. def get_db_connection() -> Generator[sqlite3.Connection, None, None]:
  13. """
  14. Context manager for database connections.
  15. Ensures proper connection cleanup.
  16. """
  17. conn = sqlite3.connect(DB_PATH)
  18. conn.row_factory = sqlite3.Row # Enable column access by name
  19. conn.execute("PRAGMA foreign_keys = ON") # Enable foreign key constraints
  20. try:
  21. yield conn
  22. conn.commit()
  23. except Exception:
  24. conn.rollback()
  25. raise
  26. finally:
  27. conn.close()
  28. def init_database() -> None:
  29. """
  30. Initialize database and create tables if they don't exist.
  31. Creates projects, tasks, annotations, and users tables with proper relationships.
  32. """
  33. with get_db_connection() as conn:
  34. cursor = conn.cursor()
  35. # Enable foreign key constraints
  36. cursor.execute("PRAGMA foreign_keys = ON")
  37. # Create users table
  38. cursor.execute("""
  39. CREATE TABLE IF NOT EXISTS users (
  40. id TEXT PRIMARY KEY,
  41. username TEXT NOT NULL UNIQUE,
  42. email TEXT NOT NULL UNIQUE,
  43. password_hash TEXT NOT NULL,
  44. role TEXT NOT NULL DEFAULT 'annotator',
  45. oauth_provider TEXT,
  46. oauth_id TEXT,
  47. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  48. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  49. )
  50. """)
  51. # Create indexes for users table
  52. cursor.execute("""
  53. CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)
  54. """)
  55. cursor.execute("""
  56. CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)
  57. """)
  58. cursor.execute("""
  59. CREATE INDEX IF NOT EXISTS idx_users_oauth
  60. ON users(oauth_provider, oauth_id)
  61. """)
  62. # Create projects table
  63. cursor.execute("""
  64. CREATE TABLE IF NOT EXISTS projects (
  65. id TEXT PRIMARY KEY,
  66. name TEXT NOT NULL,
  67. description TEXT,
  68. config TEXT NOT NULL,
  69. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  70. )
  71. """)
  72. # Create tasks table
  73. cursor.execute("""
  74. CREATE TABLE IF NOT EXISTS tasks (
  75. id TEXT PRIMARY KEY,
  76. project_id TEXT NOT NULL,
  77. name TEXT NOT NULL,
  78. data TEXT NOT NULL,
  79. status TEXT DEFAULT 'pending',
  80. assigned_to TEXT,
  81. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  82. FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
  83. )
  84. """)
  85. # Create annotations table
  86. cursor.execute("""
  87. CREATE TABLE IF NOT EXISTS annotations (
  88. id TEXT PRIMARY KEY,
  89. task_id TEXT NOT NULL,
  90. user_id TEXT NOT NULL,
  91. result TEXT NOT NULL,
  92. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  93. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  94. FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
  95. )
  96. """)
  97. conn.commit()
  98. def get_db() -> sqlite3.Connection:
  99. """
  100. Get a database connection.
  101. Note: Caller is responsible for closing the connection.
  102. """
  103. conn = sqlite3.connect(DB_PATH)
  104. conn.row_factory = sqlite3.Row
  105. conn.execute("PRAGMA foreign_keys = ON")
  106. return conn