139_spanish-voice-trainer/database.py

194 lines
6 KiB
Python

# 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()