sort order added to deck
This commit is contained in:
parent
2c0c63036d
commit
ed1c6b9d4e
6 changed files with 59 additions and 18 deletions
|
|
@ -23,6 +23,7 @@ def compile_anki_package(records, output_path, deck_name):
|
|||
Compiles database records into a bidirectional card payload package.
|
||||
Resolves voice models dynamically by gender selection parameters and applies
|
||||
global speed coefficient rates from the active configurations.
|
||||
Injects sort_order as field 0 for structured card sequencing.
|
||||
"""
|
||||
# Generate deterministic positive 32-bit integers from deck name and model name
|
||||
# to avoid collisions across different decks while keeping imports stable
|
||||
|
|
@ -40,6 +41,7 @@ def compile_anki_package(records, output_path, deck_name):
|
|||
model_id,
|
||||
'Spanish Bidirectional Multi-Note HTML Model',
|
||||
fields=[
|
||||
{'name': 'SortOrder'}, # Field 0: Anki primary sort field
|
||||
{'name': 'EnglishText'},
|
||||
{'name': 'SpanishText'},
|
||||
{'name': 'AnkiNotes'},
|
||||
|
|
@ -85,6 +87,11 @@ def compile_anki_package(records, output_path, deck_name):
|
|||
es_raw = record["es_text"]
|
||||
gender_flag = record.get("gender", "Female")
|
||||
|
||||
# Read sort_order from database record (fallback to loop index if unset)
|
||||
raw_order = record.get("sort_order")
|
||||
order_val = int(raw_order) if raw_order is not None else (idx + 1)
|
||||
sort_order_str = f"{order_val:04d}" # Zero-padded integer string for exact sorting
|
||||
|
||||
# Sanitize text payloads for TTS engine
|
||||
en_tts_text = parse_text_for_edgetts(en_raw)
|
||||
es_tts_text = parse_text_for_edgetts(es_raw)
|
||||
|
|
@ -123,7 +130,7 @@ def compile_anki_package(records, output_path, deck_name):
|
|||
# Clean sequential matching fields array matching schema mapping above
|
||||
note = genanki.Note(
|
||||
model=anki_model,
|
||||
fields=[en_raw, es_raw, anki_notes_html, en_audio_field, es_audio_field],
|
||||
fields=[sort_order_str, en_raw, es_raw, anki_notes_html, en_audio_field, es_audio_field],
|
||||
tags=note_tags
|
||||
)
|
||||
deck.add_note(note)
|
||||
|
|
|
|||
33
database.py
33
database.py
|
|
@ -69,7 +69,7 @@ def get_connection():
|
|||
|
||||
|
||||
def ensure_database_populated():
|
||||
"""Initializes tables if running against a new or empty database file."""
|
||||
"""Initializes tables and migrates schemas if running against a new or existing database file."""
|
||||
conn = get_connection()
|
||||
cursor = conn.cursor()
|
||||
try:
|
||||
|
|
@ -83,10 +83,17 @@ def ensure_database_populated():
|
|||
tags TEXT,
|
||||
notes TEXT,
|
||||
anki_notes TEXT DEFAULT '',
|
||||
gender TEXT DEFAULT 'Female'
|
||||
gender TEXT DEFAULT 'Female',
|
||||
sort_order INTEGER DEFAULT 0
|
||||
);
|
||||
""")
|
||||
|
||||
# Safely migrate existing databases that do not yet have the sort_order column
|
||||
cursor.execute("PRAGMA table_info(translations);")
|
||||
columns = [row["name"] for row in cursor.fetchall()]
|
||||
if "sort_order" not in columns:
|
||||
cursor.execute("ALTER TABLE translations ADD COLUMN sort_order INTEGER DEFAULT 0;")
|
||||
|
||||
# 2. Key-value configuration table
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
|
|
@ -141,7 +148,7 @@ def save_setting_to_db(key, value):
|
|||
|
||||
|
||||
def get_all_translations_explicit():
|
||||
"""Retrieves all records using explicit, table-qualified column declarations."""
|
||||
"""Retrieves all records using explicit, table-qualified column declarations sorted by sort_order."""
|
||||
conn = get_connection()
|
||||
cursor = conn.cursor()
|
||||
try:
|
||||
|
|
@ -154,9 +161,10 @@ def get_all_translations_explicit():
|
|||
translations.tags,
|
||||
translations.notes,
|
||||
translations.anki_notes,
|
||||
translations.gender
|
||||
translations.gender,
|
||||
translations.sort_order
|
||||
FROM translations
|
||||
ORDER BY translations.translation_id ASC;
|
||||
ORDER BY translations.sort_order ASC, translations.translation_id ASC;
|
||||
""")
|
||||
return [dict(row) for row in cursor.fetchall()]
|
||||
finally:
|
||||
|
|
@ -178,7 +186,8 @@ def get_translation_by_id(translation_id):
|
|||
translations.tags,
|
||||
translations.notes,
|
||||
translations.anki_notes,
|
||||
translations.gender
|
||||
translations.gender,
|
||||
translations.sort_order
|
||||
FROM translations
|
||||
WHERE translations.translation_id = ?;
|
||||
""",
|
||||
|
|
@ -199,6 +208,7 @@ def update_translation_record(
|
|||
notes,
|
||||
gender,
|
||||
anki_notes,
|
||||
sort_order=0,
|
||||
):
|
||||
"""Saves interface edits directly back into the table using named arguments."""
|
||||
conn = get_connection()
|
||||
|
|
@ -214,7 +224,8 @@ def update_translation_record(
|
|||
tags = :tags,
|
||||
notes = :notes,
|
||||
gender = :gender,
|
||||
anki_notes = :anki
|
||||
anki_notes = :anki,
|
||||
sort_order = :sort_order
|
||||
WHERE translation_id = :id;
|
||||
""",
|
||||
{
|
||||
|
|
@ -226,6 +237,7 @@ def update_translation_record(
|
|||
"notes": notes,
|
||||
"gender": gender,
|
||||
"anki": anki_notes,
|
||||
"sort_order": sort_order,
|
||||
},
|
||||
)
|
||||
conn.commit()
|
||||
|
|
@ -251,7 +263,7 @@ def delete_translation_record(translation_id):
|
|||
|
||||
|
||||
def insert_translation_record(
|
||||
es_text, en_text, source_context, tags, notes, gender, anki_notes
|
||||
es_text, en_text, source_context, tags, notes, gender, anki_notes, sort_order=0
|
||||
):
|
||||
"""Inserts a new record using named arguments."""
|
||||
conn = get_connection()
|
||||
|
|
@ -259,8 +271,8 @@ def insert_translation_record(
|
|||
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);
|
||||
INSERT INTO translations (es_text, en_text, source_context, tags, notes, gender, anki_notes, sort_order)
|
||||
VALUES (:es, :en, :ctx, :tags, :notes, :gender, :anki, :sort_order);
|
||||
""",
|
||||
{
|
||||
"en": en_text,
|
||||
|
|
@ -270,6 +282,7 @@ def insert_translation_record(
|
|||
"notes": notes,
|
||||
"anki": anki_notes,
|
||||
"gender": gender,
|
||||
"sort_order": sort_order,
|
||||
},
|
||||
)
|
||||
conn.commit()
|
||||
|
|
|
|||
BIN
spanish_trainer-26-06-21.db
Normal file
BIN
spanish_trainer-26-06-21.db
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -6,8 +6,9 @@ import threading
|
|||
import asyncio
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QLineEdit, QPushButton, QTableWidget, QTableWidgetItem, \
|
||||
QHeaderView, QMessageBox, QFormLayout, QDialog, QTextEdit, QRadioButton, QButtonGroup
|
||||
QLineEdit, QPushButton, QTableWidget, QTableWidgetItem,
|
||||
QHeaderView, QMessageBox, QFormLayout, QDialog, QTextEdit,
|
||||
QRadioButton, QButtonGroup, QSpinBox
|
||||
)
|
||||
from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt
|
||||
import edge_tts
|
||||
|
|
@ -119,6 +120,12 @@ class SandboxTab(QWidget):
|
|||
self.txt_tags.setStyleSheet("padding: 6px; font-size: 14px;")
|
||||
self.txt_tags.textChanged.connect(self.apply_live_grid_filter)
|
||||
|
||||
# --- Sort Order Field (Added after Tags) ---
|
||||
self.spn_sort_order = QSpinBox()
|
||||
self.spn_sort_order.setRange(0, 999999)
|
||||
self.spn_sort_order.setValue(0)
|
||||
self.spn_sort_order.setStyleSheet("padding: 6px; font-size: 14px;")
|
||||
|
||||
# Modal Editor Row Buttons for Extended Notes
|
||||
editor_buttons_layout = QHBoxLayout()
|
||||
self.btn_edit_notes = QPushButton("📝 Edit Notes Block")
|
||||
|
|
@ -135,6 +142,7 @@ class SandboxTab(QWidget):
|
|||
form_layout.addRow(QLabel("<b>Spanish Translation:</b>"), spanish_widget)
|
||||
form_layout.addRow(QLabel("<b>Source Context:</b>"), self.txt_context)
|
||||
form_layout.addRow(QLabel("<b>Tags:</b>"), self.txt_tags)
|
||||
form_layout.addRow(QLabel("<b>Sort Order:</b>"), self.spn_sort_order)
|
||||
form_layout.addRow(QLabel("<b>Extended Data Fields:</b>"), editor_buttons_layout)
|
||||
|
||||
main_layout.addWidget(form_container)
|
||||
|
|
@ -189,8 +197,8 @@ class SandboxTab(QWidget):
|
|||
|
||||
# --- VIEWPORT GRID TABLE ---
|
||||
self.table = QTableWidget()
|
||||
self.table.setColumnCount(6)
|
||||
self.table.setHorizontalHeaderLabels(["ID", "English", "Spanish", "Context", "Tags", "Gender"])
|
||||
self.table.setColumnCount(7)
|
||||
self.table.setHorizontalHeaderLabels(["ID", "English", "Spanish", "Context", "Tags", "Sort Order", "Gender"])
|
||||
self.table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
|
||||
self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
|
||||
self.table.cellClicked.connect(self.populate_form_from_grid)
|
||||
|
|
@ -199,6 +207,7 @@ class SandboxTab(QWidget):
|
|||
header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
|
||||
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
|
||||
header.setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch)
|
||||
header.setSectionResizeMode(5, QHeaderView.ResizeMode.ResizeToContents)
|
||||
|
||||
main_layout.addWidget(self.table)
|
||||
self.reload_table_display()
|
||||
|
|
@ -238,6 +247,7 @@ class SandboxTab(QWidget):
|
|||
self.txt_spanish.clear()
|
||||
self.txt_context.clear()
|
||||
self.txt_tags.clear()
|
||||
self.spn_sort_order.setValue(0)
|
||||
self.current_notes_content = ""
|
||||
self.current_anki_notes_content = ""
|
||||
self.rb_female.setChecked(True)
|
||||
|
|
@ -255,6 +265,7 @@ class SandboxTab(QWidget):
|
|||
es_t = self.txt_spanish.text().strip()
|
||||
ctx_t = self.txt_context.text().strip()
|
||||
tag_t = self.txt_tags.text().strip()
|
||||
sort_val = self.spn_sort_order.value()
|
||||
gender_t = "Male" if self.rb_male.isChecked() else "Female"
|
||||
|
||||
if not en_t or not es_t:
|
||||
|
|
@ -262,9 +273,17 @@ class SandboxTab(QWidget):
|
|||
return
|
||||
|
||||
if self.selected_translation_id is None:
|
||||
database.insert_translation_record(es_t, en_t, ctx_t, tag_t, self.current_notes_content, gender_t, self.current_anki_notes_content)
|
||||
database.insert_translation_record(
|
||||
es_t, en_t, ctx_t, tag_t,
|
||||
self.current_notes_content, gender_t,
|
||||
self.current_anki_notes_content, sort_val
|
||||
)
|
||||
else:
|
||||
database.update_translation_record(self.selected_translation_id, es_t, en_t, ctx_t, tag_t, self.current_notes_content, gender_t, self.current_anki_notes_content)
|
||||
database.update_translation_record(
|
||||
self.selected_translation_id, es_t, en_t, ctx_t, tag_t,
|
||||
self.current_notes_content, gender_t,
|
||||
self.current_anki_notes_content, sort_val
|
||||
)
|
||||
|
||||
self.clear_form_fields()
|
||||
self.reload_table_display()
|
||||
|
|
@ -303,6 +322,7 @@ class SandboxTab(QWidget):
|
|||
self.txt_spanish.setText(record["es_text"])
|
||||
self.txt_context.setText(record.get("source_context", ""))
|
||||
self.txt_tags.setText(record.get("tags", ""))
|
||||
self.spn_sort_order.setValue(record.get("sort_order", 0))
|
||||
self.current_notes_content = record.get("notes", "")
|
||||
self.current_anki_notes_content = record.get("anki_notes", "")
|
||||
|
||||
|
|
@ -343,7 +363,8 @@ class SandboxTab(QWidget):
|
|||
self.table.setItem(visible_row_idx, 2, QTableWidgetItem(r["es_text"]))
|
||||
self.table.setItem(visible_row_idx, 3, QTableWidgetItem(r.get("source_context", "")))
|
||||
self.table.setItem(visible_row_idx, 4, QTableWidgetItem(r.get("tags", "")))
|
||||
self.table.setItem(visible_row_idx, 5, QTableWidgetItem(r.get("gender", "Female")))
|
||||
self.table.setItem(visible_row_idx, 5, QTableWidgetItem(str(r.get("sort_order", 0))))
|
||||
self.table.setItem(visible_row_idx, 6, QTableWidgetItem(r.get("gender", "Female")))
|
||||
visible_row_idx += 1
|
||||
|
||||
def _async_edge_speech_worker(self, text, voice, rate_modifier):
|
||||
|
|
|
|||
Loading…
Reference in a new issue