194 lines
6 KiB
Python
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()
|
|
|