Files
db_interface/database_manager.py
scheianu c912bac2dc feat: persistent DB host config, async DB ops, Exit button, test connection
- Save/load DB host from config.json (works both in dev and built .exe)
- All DB operations moved to background threads to prevent UI blocking
- Connection timeout set to 5s to avoid long hangs on unreachable host
- Added Test Connection button to Settings popup
- Added red Exit button fixed to bottom-right corner of main screen
- Updated DatabaseApp.spec with full Kivy deps (sdl2, glew, angle) and hidden imports
- PyInstaller-aware base path using sys.frozen for config.json persistence
2026-04-01 21:00:45 +03:00

203 lines
7.5 KiB
Python

import mysql.connector
from mysql.connector import Error
from typing import List, Tuple, Optional
import json
import os
import sys
# When frozen by PyInstaller, __file__ points to a temp folder that is deleted on exit.
# sys.executable points to the .exe location, which is persistent.
if getattr(sys, 'frozen', False):
_BASE_DIR = os.path.dirname(sys.executable)
else:
_BASE_DIR = os.path.dirname(os.path.abspath(__file__))
CONFIG_FILE = os.path.join(_BASE_DIR, 'config.json')
class DatabaseManager:
"""
Database manager class for handling MariaDB operations.
Connects to MariaDB server with table offsystemsCounting containing id (VARCHAR(20)) and mass (REAL).
"""
def __init__(self):
self.host = self._load_host()
self.database = "cantare_injectie"
self.user = "omron"
self.password = "Initial01!"
self.connection = None
# init_database() is called asynchronously from the UI layer to avoid blocking
def _load_host(self) -> str:
"""Load the database host from the config file, falling back to localhost."""
try:
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r') as f:
data = json.load(f)
return data.get('host', 'localhost')
except Exception as e:
print(f"Could not read config file: {e}")
return 'localhost'
def save_host(self, host: str):
"""Persist the database host to the config file."""
try:
data = {}
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r') as f:
data = json.load(f)
data['host'] = host
with open(CONFIG_FILE, 'w') as f:
json.dump(data, f, indent=2)
except Exception as e:
print(f"Could not save config file: {e}")
def test_connection(self, host: str) -> tuple:
"""Test connectivity to the database using the given host. Returns (success: bool, message: str)."""
try:
test_conn = mysql.connector.connect(
host=host,
database=self.database,
user=self.user,
password=self.password,
connection_timeout=5
)
if test_conn.is_connected():
test_conn.close()
return True, f"Connected successfully to '{host}'"
except Error as e:
return False, str(e)
return False, "Connection failed"
def get_connection(self):
"""Get a database connection."""
try:
if self.connection is None or not self.connection.is_connected():
self.connection = mysql.connector.connect(
host=self.host,
database=self.database,
user=self.user,
password=self.password,
connection_timeout=5
)
return self.connection
except Error as e:
print(f"Database connection error: {e}")
return None
def init_database(self):
"""Initialize the database connection and create the table if it doesn't exist."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS offsystemsCounting (
id VARCHAR(20) PRIMARY KEY,
mass REAL NOT NULL
)
''')
conn.commit()
print(f"Connected to MariaDB database: {self.database}")
print("Table 'offsystemsCounting' ready")
except Error as e:
print(f"Database initialization error: {e}")
def read_all_data(self) -> List[Tuple[str, float]]:
"""Read all data from the database."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute("SELECT id, mass FROM offsystemsCounting ORDER BY id")
return cursor.fetchall()
except Error as e:
print(f"Error reading data: {e}")
return []
def search_by_id(self, record_id: str) -> Optional[Tuple[str, float]]:
"""Search for a record by ID."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute("SELECT id, mass FROM offsystemsCounting WHERE id = %s", (record_id,))
return cursor.fetchone()
except Error as e:
print(f"Error searching data: {e}")
return None
def add_or_update_record(self, record_id: str, mass: float) -> bool:
"""Add a new record or update existing one if ID already exists."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
# Check if record exists
existing = self.search_by_id(record_id)
if existing:
# Update existing record
cursor.execute(
"UPDATE offsystemsCounting SET mass = %s WHERE id = %s",
(mass, record_id)
)
print(f"Updated record: {record_id} = {mass}")
else:
# Insert new record
cursor.execute(
"INSERT INTO offsystemsCounting (id, mass) VALUES (%s, %s)",
(record_id, mass)
)
print(f"Added new record: {record_id} = {mass}")
conn.commit()
return True
except Error as e:
print(f"Error adding/updating record: {e}")
return False
def delete_record(self, record_id: str) -> bool:
"""Delete a record by ID."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute("DELETE FROM offsystemsCounting WHERE id = %s", (record_id,))
if cursor.rowcount > 0:
conn.commit()
print(f"Deleted record: {record_id}")
return True
else:
print(f"No record found with ID: {record_id}")
return False
except Error as e:
print(f"Error deleting record: {e}")
return False
def get_record_count(self) -> int:
"""Get the total number of records in the database."""
try:
conn = self.get_connection()
if conn and conn.is_connected():
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM offsystemsCounting")
return cursor.fetchone()[0]
except Error as e:
print(f"Error getting record count: {e}")
return 0
def close_connection(self):
"""Close the database connection."""
try:
if self.connection and self.connection.is_connected():
self.connection.close()
print("MariaDB connection closed")
except Error as e:
print(f"Error closing connection: {e}")
def __del__(self):
"""Destructor to ensure connection is closed."""
self.close_connection()