added db creation, to project
This commit is contained in:
parent
4a0409b87a
commit
bb6dcb7630
5 changed files with 271 additions and 11 deletions
|
|
@ -0,0 +1,59 @@
|
|||
# database/connection.py
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
DB_NAME = "spanish_trainer.db"
|
||||
|
||||
def get_connection():
|
||||
"""Returns a standard connection object to the SQLite database."""
|
||||
return sqlite3.connect(DB_NAME)
|
||||
|
||||
def init_db():
|
||||
"""
|
||||
Initializes the SQLite database tables if they do not exist.
|
||||
This safely runs on every boot without wiping your existing data.
|
||||
"""
|
||||
print(f"🗄️ Checking database status for '{DB_NAME}'...")
|
||||
|
||||
# The SQL schema we designed for your glossary, cross-references, and tracks
|
||||
schema = """
|
||||
CREATE TABLE IF NOT EXISTS phrases (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
text TEXT NOT NULL,
|
||||
language TEXT NOT NULL,
|
||||
textbook TEXT DEFAULT NULL,
|
||||
unit INTEGER DEFAULT NULL,
|
||||
source_context TEXT DEFAULT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS translations (
|
||||
source_phrase_id INTEGER,
|
||||
target_phrase_id INTEGER,
|
||||
PRIMARY KEY (source_phrase_id, target_phrase_id),
|
||||
FOREIGN KEY (source_phrase_id) REFERENCES phrases(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (target_phrase_id) REFERENCES phrases(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audio_tracks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
phrase_id INTEGER NOT NULL,
|
||||
voice_gender TEXT NOT NULL,
|
||||
voice_name TEXT NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
is_reference INTEGER DEFAULT 1,
|
||||
FOREIGN KEY (phrase_id) REFERENCES phrases(id) ON DELETE CASCADE
|
||||
);
|
||||
"""
|
||||
|
||||
conn = get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
# executescript allows running multiple CREATE TABLE statements at once
|
||||
cursor.executescript(schema)
|
||||
conn.commit()
|
||||
print("✅ Database tables verified and initialized successfully.")
|
||||
except sqlite3.Error as e:
|
||||
print(f"❌ Database initialization failed: {e}")
|
||||
finally:
|
||||
conn.close()
|
||||
205
doc/Notes.md
205
doc/Notes.md
|
|
@ -1,14 +1,40 @@
|
|||
# spanish-voice-trainer
|
||||
# Project Summary:
|
||||
- [1. spanish-voice-trainer](#1-spanish-voice-trainer)
|
||||
- [2. Project Summary:](#2-project-summary)
|
||||
- [2.1. 🛠️ The Core Technical Stack](#21-️-the-core-technical-stack)
|
||||
- [2.2. 🔄 How the System Works](#22--how-the-system-works)
|
||||
- [3. Create Basic Structure as suggested](#3-create-basic-structure-as-suggested)
|
||||
- [3.1. Commands](#31-commands)
|
||||
- [4. use uv](#4-use-uv)
|
||||
- [5. Install Your Dependency Stack](#5-install-your-dependency-stack)
|
||||
- [6. checking the packages are installed](#6-checking-the-packages-are-installed)
|
||||
- [7. Running Scripts with uv Moving Forward](#7-running-scripts-with-uv-moving-forward)
|
||||
- [8. this is the basic](#8-this-is-the-basic)
|
||||
- [9. Developed sqllite schema](#9-developed-sqllite-schema)
|
||||
- [9.1. The Structure inside database/connection.py](#91-the-structure-inside-databaseconnectionpy)
|
||||
- [9.2. Triggering It inside main.py](#92-triggering-it-inside-mainpy)
|
||||
- [9.3. Why This Placement is Ideal](#93-why-this-placement-is-ideal)
|
||||
- [9.3.1. Idempotent Execution: Using CREATE TABLE IF NOT EXISTS means this code runs beautifully every time you start your app. If the database is already there, SQLite silently skips creation and proceeds to boot without overwriting your hard-earned Aula Internacional data.](#931-idempotent-execution-using-create-table-if-not-exists-means-this-code-runs-beautifully-every-time-you-start-your-app-if-the-database-is-already-there-sqlite-silently-skips-creation-and-proceeds-to-boot-without-overwriting-your-hard-earned-aula-internacional-data)
|
||||
- [9.3.2. Crash Prevention: By placing it at the absolute top of main(), you ensure that no other component (like a UI field trying to load your textbook list) can execute queries against a database that hasn't finished setting up its columns yet.](#932-crash-prevention-by-placing-it-at-the-absolute-top-of-main-you-ensure-that-no-other-component-like-a-ui-field-trying-to-load-your-textbook-list-can-execute-queries-against-a-database-that-hasnt-finished-setting-up-its-columns-yet)
|
||||
- [9.3.3. Clean Decoupling: Your root main.py handles the when (on boot), while database/connection.py holds the how (the specific table schemas).](#933-clean-decoupling-your-root-mainpy-handles-the-when-on-boot-while-databaseconnectionpy-holds-the-how-the-specific-table-schemas)
|
||||
- [10. It work 100%](#10-it-work-100)
|
||||
- [11. App to read sqlite](#11-app-to-read-sqlite)
|
||||
- [12. brew](#12-brew)
|
||||
- [12.1. Standard Formula (Default: No Flag)](#121-standard-formula-default-no-flag)
|
||||
- [12.2. Cask Extension (--cask)](#122-cask-extension---cask)
|
||||
- [12.3. Why This Is Useful](#123-why-this-is-useful)
|
||||
- [13. Beekeeper Studio](#13-beekeeper-studio)
|
||||
|
||||
# 1. spanish-voice-trainer
|
||||
# 2. Project Summary:
|
||||
Custom Spanish Voice TrainerA high-performance, completely private desktop application built on macOS to accelerate Spanish language training through automated flashcard creation and intelligent pronunciation analysis.The application utilizes a local-first architecture to ensure complete data privacy, storing all configurations, historical student analytics, and multimedia binary files strictly on the user’s local drive.
|
||||
## 🛠️ The Core Technical Stack
|
||||
## 2.1. 🛠️ The Core Technical Stack
|
||||
- Environment & Package Management: uv (Rust-based Python package manager) for ultra-fast, isolated virtual environments and dependency locking.
|
||||
- Database & Persistence: SQLite to map phrase metadata, local media file paths, and chronological user practice scores without external database infrastructure.
|
||||
- Audio & Signal Processing: sounddevice for hands-free, voice-activated microphone capture; librosa and fastdtw (Dynamic Time Warping) to extract phoneme features (MFCCs) and score user pronunciation accuracy against reference files.
|
||||
- Asset Generation: edge-tts to stream high-quality, neural text-to-speech Spanish audio clips, and Pillow (PIL) to auto-render widescreen flashcard JPEGs matching the phrases.
|
||||
- User Interface: Developed in two phases—starting as a clean, text-based Command Line Interface (CLI) before migrating to a dual-mode desktop GUI built with PyQt6.
|
||||
|
||||
## 🔄 How the System Works
|
||||
## 2.2. 🔄 How the System Works
|
||||
The application operates across two distinct, integrated operational frameworks managed via a unified QStackedWidget interface:
|
||||
1. Content Creator Mode
|
||||
The user inputs a Spanish phrase and its English translation. The system automatically triggers the asset generator to output a custom high-quality flashcard image and a native-sounding neural audio file. The localized text strings and file paths are instantly committed to the SQLite database.
|
||||
|
|
@ -16,7 +42,7 @@ The user inputs a Spanish phrase and its English translation. The system automat
|
|||
2. 📈 Long-Term Capability: Custom Video Compilations
|
||||
Because all image assets share standard HD video dimensions ($1280 X 720) and all audio samples are tracked deterministically in the database, the core engine can be commanded to interface with ffmpeg-python. It can seamlessly compile entire batches of database assets into standalone, continuous .mp4 video lessons complete with timed visual pauses and silent audio gaps, providing an additional passive learning medium for language immersion.
|
||||
|
||||
# Create Basic Structure as suggested
|
||||
# 3. Create Basic Structure as suggested
|
||||
```bash
|
||||
spanish-voice-trainer/
|
||||
├── .gitignore
|
||||
|
|
@ -42,7 +68,7 @@ spanish-voice-trainer/
|
|||
└── media/ # Local storage for physical binary files
|
||||
└── .gitkeep # Keeps directory alive in Forgejo repo
|
||||
```
|
||||
## Commands
|
||||
## 3.1. Commands
|
||||
```zsh
|
||||
stephenlohning@Scotty 139_spanish-voice-trainer % mkdir doc
|
||||
stephenlohning@Scotty 139_spanish-voice-trainer % mkdir doc/images
|
||||
|
|
@ -71,7 +97,7 @@ stephenlohning@Scotty 139_spanish-voice-trainer % touch ui/gui/trainer_mode.py
|
|||
stephenlohning@Scotty 139_spanish-voice-trainer % mkdir media
|
||||
stephenlohning@Scotty 139_spanish-voice-trainer % touch media/.gitkeep
|
||||
````
|
||||
# use uv
|
||||
# 4. use uv
|
||||
```zsh
|
||||
stephenlohning@Scotty 139_spanish-voice-trainer % uv venv
|
||||
Using CPython 3.13.5
|
||||
|
|
@ -80,14 +106,14 @@ Activate with: source .venv/bin/activate
|
|||
stephenlohning@Scotty 139_spanish-voice-trainer %
|
||||
```
|
||||
|
||||
# Install Your Dependency Stack
|
||||
# 5. Install Your Dependency Stack
|
||||
Run the uv add commands to populate your environment lockfile:
|
||||
|
||||
```zsh
|
||||
uv add sounddevice numpy scipy librosa fastdtw pillow edge-tts pyqt6
|
||||
```
|
||||
|
||||
# checking the packages are installed
|
||||
# 6. checking the packages are installed
|
||||
|
||||
```zsh
|
||||
(139_spanish-voice-trainer) stephenlohning@Scotty 139_spanish-voice-trainer % uv pip list
|
||||
|
|
@ -140,7 +166,7 @@ typing-extensions 4.15.0
|
|||
urllib3 2.7.0
|
||||
yarl 1.24.2
|
||||
```
|
||||
# Running Scripts with uv Moving Forward
|
||||
# 7. Running Scripts with uv Moving Forward
|
||||
One of the cleanest features of uv is that you don't necessarily have to remember to run source .venv/bin/activate every single time you open a new terminal window to write code.
|
||||
|
||||
Instead, you can prefix your execution scripts using uv run. It automatically detects your local .venv, verifies that all dependencies in your lockfile are fully up to date, and runs the script seamlessly:
|
||||
|
|
@ -149,4 +175,161 @@ Instead, you can prefix your execution scripts using uv run. It automatically de
|
|||
# Example: To execute your main script once you write it later today
|
||||
uv run main.py
|
||||
```
|
||||
# this is the basic
|
||||
# 8. this is the basic
|
||||
|
||||
# 9. Developed sqllite schema
|
||||
|
||||
This was easy I just discussed what I thought we should have and gemini suggested the following
|
||||
|
||||
## 9.1. The Structure inside database/connection.py
|
||||
In this file, you define your schema and wrap the connection/creation logic inside a function called init_db().
|
||||
```python
|
||||
# database/connection.py
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
DB_NAME = "spanish_trainer.db"
|
||||
|
||||
def get_connection():
|
||||
"""Returns a standard connection object to the SQLite database."""
|
||||
return sqlite3.connect(DB_NAME)
|
||||
|
||||
def init_db():
|
||||
"""
|
||||
Initializes the SQLite database tables if they do not exist.
|
||||
This safely runs on every boot without wiping your existing data.
|
||||
"""
|
||||
print(f"🗄️ Checking database status for '{DB_NAME}'...")
|
||||
|
||||
# The SQL schema we designed for your glossary, cross-references, and tracks
|
||||
schema = """
|
||||
CREATE TABLE IF NOT EXISTS phrases (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
text TEXT NOT NULL,
|
||||
language TEXT NOT NULL,
|
||||
textbook TEXT DEFAULT NULL,
|
||||
unit INTEGER DEFAULT NULL,
|
||||
source_context TEXT DEFAULT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS translations (
|
||||
source_phrase_id INTEGER,
|
||||
target_phrase_id INTEGER,
|
||||
PRIMARY KEY (source_phrase_id, target_phrase_id),
|
||||
FOREIGN KEY (source_phrase_id) REFERENCES phrases(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (target_phrase_id) REFERENCES phrases(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS audio_tracks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
phrase_id INTEGER NOT NULL,
|
||||
voice_gender TEXT NOT NULL,
|
||||
voice_name TEXT NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
is_reference INTEGER DEFAULT 1,
|
||||
FOREIGN KEY (phrase_id) REFERENCES phrases(id) ON DELETE CASCADE
|
||||
);
|
||||
"""
|
||||
|
||||
conn = get_connection()
|
||||
try:
|
||||
cursor = conn.cursor()
|
||||
# executescript allows running multiple CREATE TABLE statements at once
|
||||
cursor.executescript(schema)
|
||||
conn.commit()
|
||||
print("✅ Database tables verified and initialized successfully.")
|
||||
except sqlite3.Error as e:
|
||||
print(f"❌ Database initialization failed: {e}")
|
||||
finally:
|
||||
conn.close()
|
||||
```
|
||||
|
||||
## 9.2. Triggering It inside main.py
|
||||
Now, your root startup script handles execution order perfectly. Before launching the UI, it imports and fires init_db(). If the .db file doesn't exist, SQLite handles creating it seamlessly on the spot.
|
||||
|
||||
```python
|
||||
# main.py
|
||||
import sys
|
||||
from database.connection import init_db
|
||||
|
||||
def main():
|
||||
print("🚀 Booting Castilian Voice Trainer...")
|
||||
|
||||
# Step 1: Ensure infrastructure is present before doing anything else
|
||||
init_db()
|
||||
|
||||
# Step 2: Initialize Core Engines (AudioEngine, AssetGenerator)
|
||||
# Step 3: Launch your PyQt6 Graphical User Interface
|
||||
print("🖥️ Launching GUI...")
|
||||
|
||||
# (PyQt6 window exec loops will go here)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
## 9.3. Why This Placement is Ideal
|
||||
### 9.3.1. Idempotent Execution: Using CREATE TABLE IF NOT EXISTS means this code runs beautifully every time you start your app. If the database is already there, SQLite silently skips creation and proceeds to boot without overwriting your hard-earned Aula Internacional data.
|
||||
|
||||
### 9.3.2. Crash Prevention: By placing it at the absolute top of main(), you ensure that no other component (like a UI field trying to load your textbook list) can execute queries against a database that hasn't finished setting up its columns yet.
|
||||
|
||||
### 9.3.3. Clean Decoupling: Your root main.py handles the when (on boot), while database/connection.py holds the how (the specific table schemas).
|
||||
|
||||
# 10. It work 100%
|
||||
|
||||
```zsh
|
||||
It Looks like it worked
|
||||
tephenlohning@Scotty 139_spanish-voice-trainer % source /Users/stephenlohning/Documents/01_Projects/139_spanish-voice-train
|
||||
er/.venv/bin/activate
|
||||
(139_spanish-voice-trainer) stephenlohning@Scotty 139_spanish-voice-trainer % uv run main.py
|
||||
🚀 Booting Castilian Voice Trainer...
|
||||
🗄️ Checking database status for 'spanish_trainer.db'...
|
||||
✅ Database tables verified and initialized successfully.
|
||||
🖥️ Launching GUI...
|
||||
|
||||
Create database in root
|
||||
(139_spanish-voice-trainer) stephenlohning@Scotty 139_spanish-voice-trainer % ls -la *.db
|
||||
-rw-r--r-- 1 stephenlohning staff 24576 Jun 12 21:04 spanish_trainer.db
|
||||
(139_spanish-voice-trainer) stephenlohning@Scotty 139_spanish-voice-trainer %
|
||||
```
|
||||
|
||||
# 11. App to read sqlite
|
||||
|
||||
Gemini gave a few suggestions, I picked the second one "Bee Keeper Studio"
|
||||
|
||||
I used brew to install it
|
||||
|
||||
```zsh
|
||||
### Top Modern/Aesthetic Option: Beekeeper Studio (Community Edition)
|
||||
If you want something that matches modern macOS design language (with gorgeous dark mode formatting, clean autocomplete tabs, and high-DPI scaling), Beekeeper is phenomenal. It is fast, private, and highly visual.
|
||||
* **How to install via Homebrew:**
|
||||
```bash
|
||||
brew install --cask beekeeper-studio
|
||||
```
|
||||
I did not know what --cask was doing so I asked, and I got a bit of info about brew the installation manager.
|
||||
|
||||
# 12. brew
|
||||
In Homebrew, the --cask flag tells the package manager that you want to install a macOS desktop application that comes with a Graphical User Interface (GUI), rather than a command-line tool.
|
||||
|
||||
Here is the breakdown of how Homebrew treats these two styles of software:
|
||||
|
||||
## 12.1. Standard Formula (Default: No Flag)
|
||||
When you type brew install <package>, Homebrew downloads, compiles, or unpacks command-line utilities or developer libraries. These programs do not have an icon in your Applications folder and run entirely inside your terminal.
|
||||
|
||||
Examples: uv, git, sqlite3, ffmpeg
|
||||
|
||||
## 12.2. Cask Extension (--cask)
|
||||
When you type brew install --cask <package>, Homebrew downloads a pre-compiled macOS binary directly from the developer (usually packaged as a .dmg, .pkg, or .app file). It automatically runs the installer, bypasses the drag-and-drop step, and places the application straight into your /Applications folder.
|
||||
|
||||
Examples: beekeeper-studio, visual-studio-code, db-browser-for-sqlite
|
||||
|
||||
## 12.3. Why This Is Useful
|
||||
Instead of opening Safari, searching for Beekeeper Studio, downloading a disk image, opening it, dragging the icon to your Applications folder, and cleaning up the installer file, Homebrew does all of that for you behind the scenes in a single terminal line.
|
||||
|
||||
Furthermore, whenever you run your system updates down the road using brew upgrade, Homebrew will automatically update your desktop apps right alongside your command-line tools!
|
||||
|
||||
# 13. Beekeeper Studio
|
||||
Has an introduction YouTube video
|
||||
|
||||
The main thing is you double click apon the data base.db we created.
|
||||

|
||||
BIN
doc/Notes.pdf
BIN
doc/Notes.pdf
Binary file not shown.
BIN
doc/images/image-01.png
Normal file
BIN
doc/images/image-01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 306 KiB |
18
main.py
18
main.py
|
|
@ -0,0 +1,18 @@
|
|||
# main.py
|
||||
import sys
|
||||
from database.connection import init_db
|
||||
|
||||
def main():
|
||||
print("🚀 Booting Castilian Voice Trainer...")
|
||||
|
||||
# Step 1: Ensure infrastructure is present before doing anything else
|
||||
init_db()
|
||||
|
||||
# Step 2: Initialize Core Engines (AudioEngine, AssetGenerator)
|
||||
# Step 3: Launch your PyQt6 Graphical User Interface
|
||||
print("🖥️ Launching GUI...")
|
||||
|
||||
# (PyQt6 window exec loops will go here)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in a new issue