# database.py import sqlite3 import os DB_NAME = "spanish_trainer.db" def get_connection(): """Returns a connection to the SQLite database with row factory enabled.""" conn = sqlite3.connect(DB_NAME) conn.row_factory = sqlite3.Row return conn def ensure_database_populated(): """ Creates empty tables using the unified schema if running in a fresh environment without a database file. """ conn = get_connection() cursor = conn.cursor() try: # 1. Unified translations table with dedicated anki_notes and gender tracks cursor.execute(""" CREATE TABLE IF NOT EXISTS translations ( translation_id INTEGER PRIMARY KEY AUTOINCREMENT, es_text TEXT NOT NULL, en_text TEXT NOT NULL, source_context TEXT, tags TEXT, notes TEXT, anki_notes TEXT DEFAULT '', gender TEXT DEFAULT 'Female' ); """) # 2. Key-value configuration table cursor.execute(""" CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT ); """) conn.commit() finally: conn.close() # ========================================== # SETTINGS CRUD FUNCTIONS # ========================================== def load_all_settings(): """Fetches all system configuration properties into a flat Python dictionary.""" conn = get_connection() cursor = conn.cursor() settings_dict = {} try: cursor.execute("SELECT settings.key, settings.value FROM settings;") for row in cursor.fetchall(): settings_dict[row["key"]] = row["value"] finally: conn.close() return settings_dict def save_setting_to_db(key, value): """Inserts or replaces an application configuration entry.""" conn = get_connection() cursor = conn.cursor() try: cursor.execute(""" INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?); """, (key, value)) conn.commit() finally: conn.close() # ========================================== # TRANSLATIONS CRUD FUNCTIONS # ========================================== def get_all_translations_explicit(): """ Retrieves all records using completely explicit, table-qualified column declarations for the engines. """ conn = get_connection() cursor = conn.cursor() try: cursor.execute(""" SELECT translations.translation_id, translations.es_text, translations.en_text, translations.source_context, translations.tags, translations.notes, translations.anki_notes, translations.gender FROM translations ORDER BY translations.translation_id ASC; """) return [dict(row) for row in cursor.fetchall()] finally: conn.close() def get_translation_by_id(translation_id): """Loads a single unified record row for specific inspection or editing.""" conn = get_connection() cursor = conn.cursor() try: cursor.execute(""" SELECT translations.translation_id, translations.es_text, translations.en_text, translations.source_context, translations.tags, translations.notes, translations.anki_notes, translations.gender FROM translations WHERE translations.translation_id = ?; """, (translation_id,)) row = cursor.fetchone() return dict(row) if row else None finally: conn.close() def update_translation_record(translation_id, es_text, en_text, source_context, tags, notes, gender, anki_notes): """Saves sandbox interface edits directly back down into the table using named arguments.""" conn = get_connection() cursor = conn.cursor() try: # The SQL uses :key syntax instead of ? cursor.execute(""" UPDATE translations SET es_text = :es, en_text = :en, source_context = :ctx, tags = :tags, notes = :notes, gender = :gender, anki_notes = :anki WHERE translation_id = :id; """, { # The order inside this dictionary does not matter at all! "id": translation_id, "es": es_text, "en": en_text, "ctx": source_context, "tags": tags, "notes": notes, "gender": gender, "anki": anki_notes }) conn.commit() finally: conn.close() def delete_translation_record(translation_id): """Permanently drops a phrase card row from the data index.""" conn = get_connection() cursor = conn.cursor() try: cursor.execute(""" DELETE FROM translations WHERE translations.translation_id = ?; """, (translation_id,)) conn.commit() finally: conn.close() def insert_translation_record(es_text, en_text, source_context, tags, notes, gender, anki_notes): """Inserts a new record using named arguments so positional order doesn't matter.""" conn = get_connection() # Corrected from get_db_connection cursor = conn.cursor() try: cursor.execute(""" INSERT INTO translations (es_text, en_text, source_context, tags, notes, gender, anki_notes) VALUES (:es, :en, :ctx, :tags, :notes, :gender, :anki); """, { # SQLite maps these keys directly to the tokens above by name "en": en_text, "es": es_text, "ctx": source_context, "tags": tags, "notes": notes, "anki": anki_notes, "gender": gender }) conn.commit() finally: conn.close()