diff --git a/config_manager.py b/config_manager.py new file mode 100644 index 0000000..ef2e8c4 --- /dev/null +++ b/config_manager.py @@ -0,0 +1,27 @@ +# config_manager.py +# -*- coding: utf-8 -*- +from configparser import ConfigParser + +CONFIG_FILE = 'db_config.ini' + +def load_config(): + """Lädt die MySQL-Einstellungen aus der Konfigurationsdatei.""" + config = ConfigParser() + # Versucht, die Datei db_config.ini zu lesen + config.read(CONFIG_FILE, encoding='utf-8') # Explizit UTF-8 beim Lesen + if 'mysql' not in config: + # Standardwerte, falls die Datei fehlt + return { + 'host': 'localhost', + 'user': 'root', + 'password': '', + 'database': 'pflanzendatenbank' + } + return dict(config['mysql']) + +def save_config(settings): + """Speichert die MySQL-Einstellungen in der Konfigurationsdatei.""" + config = ConfigParser() + config['mysql'] = settings + with open(CONFIG_FILE, 'w', encoding='utf-8') as configfile: # Explizit UTF-8 beim Schreiben + config.write(configfile) \ No newline at end of file diff --git a/db_connector.py b/db_connector.py new file mode 100644 index 0000000..ac1ff98 --- /dev/null +++ b/db_connector.py @@ -0,0 +1,260 @@ +# db_connector.py +# -*- coding: utf-8 -*- +import mysql.connector +from mysql.connector import errorcode + +PROTOKOLL_TABLE_NAME = 'pflanzenprotokoll' +PLANUNG_TABLE_NAME = 'pflanzenplanung' + +def get_db_connection(config, with_db=False): + """Versucht, eine Verbindung zur Datenbank herzustellen.""" + port = config.get('port', 3306) + + db_args = {} + if with_db: + db_args['database'] = config['database'] + + try: + cnx = mysql.connector.connect( + user=config['user'], + password=config['password'], + host=config['host'], + port=port, + charset='utf8', + **db_args + ) + return cnx, cnx.cursor() + + except mysql.connector.Error as err: + if err.errno == errorcode.ER_ACCESS_DENIED_ERROR: + return None, "❌ Falscher Benutzername oder Passwort." + elif err.errno == errorcode.CR_CONN_HOST_ERROR: + return None, f"❌ Verbindung zum Host {config['host']} an Port {port} nicht möglich." + else: + return None, f"❌ Unbekannter Fehler bei der Verbindung: {err}" + + +def setup_database_and_table(cursor, db_name): + """Stellt sicher, dass Datenbank, Protokoll- und Planungstabelle existieren.""" + + try: + cursor.execute(f"CREATE DATABASE IF NOT EXISTS {db_name} DEFAULT CHARACTER SET 'utf8'") + cursor.execute(f"USE {db_name}") + except mysql.connector.Error as err: + return False, f"Fehler beim Erstellen/Auswählen der Datenbank: {err}" + + # Protokoll-Tabelle (Ist-Werte) + PROTOKOLL_DESCRIPTION = f""" + CREATE TABLE IF NOT EXISTS {PROTOKOLL_TABLE_NAME} ( + id INT AUTO_INCREMENT PRIMARY KEY, + pflanzen_name VARCHAR(50) NOT NULL, + woche INT NOT NULL, + phase VARCHAR(50), + lichtzyklus_h INT, + root_juice_ml_l FLOAT, + calmag_ml_l FLOAT, + bio_grow_ml_l FLOAT, + acti_alc_ml_l FLOAT, + bio_bloom_ml_l FLOAT, + top_max_ml_l FLOAT, + ph_wert_ziel FLOAT, + ec_wert FLOAT, + erstellungsdatum TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """ + + # Planungstabelle (Soll-Werte) + PLANUNG_DESCRIPTION = f""" + CREATE TABLE IF NOT EXISTS {PLANUNG_TABLE_NAME} ( + pflanzen_name VARCHAR(50) NOT NULL, + woche INT NOT NULL, + phase VARCHAR(50), + lichtzyklus_h INT, + root_juice_ml_l FLOAT, + calmag_ml_l FLOAT, + bio_grow_ml_l FLOAT, + acti_alc_ml_l FLOAT, + bio_bloom_ml_l FLOAT, + top_max_ml_l FLOAT, + ph_wert_ziel FLOAT, + ec_wert FLOAT, + PRIMARY KEY (pflanzen_name, woche) + ) + """ + + try: + cursor.execute(PROTOKOLL_DESCRIPTION) + cursor.execute(PLANUNG_DESCRIPTION) + + # Sicherstellen, dass die Spalte ec_wert existiert (für Updates bestehender Tabellen) + for table in [PROTOKOLL_TABLE_NAME, PLANUNG_TABLE_NAME]: + try: + cursor.execute(f"ALTER TABLE {table} ADD COLUMN ec_wert FLOAT AFTER ph_wert_ziel") + except mysql.connector.Error as err: + if err.errno != 1060: # 1060 = Spalte existiert bereits, das ist okay + print(f"Hinweis bei Alter Table {table}: {err.msg}") + + return True, "Datenbankstruktur erfolgreich eingerichtet." + except mysql.connector.Error as err: + return False, f"Fehler beim Erstellen der Tabellen: {err.msg}" + + +def insert_pflanzen_data(cnx, datensatz): + """Fügt einen neuen IST-Datensatz (Protokoll) in die Tabelle ein und committet.""" + cursor = cnx.cursor() + + add_log = (f"INSERT INTO {PROTOKOLL_TABLE_NAME} " + "(pflanzen_name, woche, phase, lichtzyklus_h, root_juice_ml_l, " + "calmag_ml_l, bio_grow_ml_l, acti_alc_ml_l, bio_bloom_ml_l, " + "top_max_ml_l, ph_wert_ziel, ec_wert, erstellungsdatum) " + "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)") + + try: + cursor.execute(add_log, datensatz) + last_id = cursor.lastrowid + cnx.commit() + cursor.close() + return True, f"Datensatz erfolgreich eingefügt. (ID: {last_id})" + except mysql.connector.Error as err: + cursor.close() + return False, f"Fehler beim Einfügen des Datensatzes: {err.msg}" + + +def save_pflanzen_plan(cnx, planungsdatensatz): + """Speichert oder aktualisiert einen SOLL-Datensatz (Planung) in der Tabelle.""" + cursor = cnx.cursor() + + save_plan = f""" + INSERT INTO {PLANUNG_TABLE_NAME} + (pflanzen_name, woche, phase, lichtzyklus_h, root_juice_ml_l, + calmag_ml_l, bio_grow_ml_l, acti_alc_ml_l, bio_bloom_ml_l, + top_max_ml_l, ph_wert_ziel, ec_wert) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE + phase = VALUES(phase), + lichtzyklus_h = VALUES(lichtzyklus_h), + root_juice_ml_l = VALUES(root_juice_ml_l), + calmag_ml_l = VALUES(calmag_ml_l), + bio_grow_ml_l = VALUES(bio_grow_ml_l), + acti_alc_ml_l = VALUES(acti_alc_ml_l), + bio_bloom_ml_l = VALUES(bio_bloom_ml_l), + top_max_ml_l = VALUES(top_max_ml_l), + ph_wert_ziel = VALUES(ph_wert_ziel), + ec_wert = VALUES(ec_wert) + """ + + try: + cursor.execute(save_plan, planungsdatensatz) + cnx.commit() + cursor.close() + return True, "Planung erfolgreich gespeichert/aktualisiert." + except mysql.connector.Error as err: + cursor.close() + return False, f"Fehler beim Speichern der Planung: {err.msg}" + + +def get_pflanzen_plan(config, plant_name, week): + """Ruft den Plan für eine spezifische Pflanze und Woche ab.""" + cnx, result = get_db_connection(config, with_db=True) + if cnx is None: + return None, None + + cursor = cnx.cursor() + + try: + query = f"SELECT * FROM {PLANUNG_TABLE_NAME} WHERE pflanzen_name = %s AND woche = %s" + cursor.execute(query, (plant_name, week)) + plan = cursor.fetchone() + + column_names = [i[0] for i in cursor.description] + + cursor.close() + cnx.close() + return plan, column_names + + except mysql.connector.Error: + cursor.close() + cnx.close() + return None, None + + +def fetch_all_data(config): + """Holt alle Protokolleinträge aus der Datenbank.""" + cnx, result = get_db_connection(config, with_db=True) + if cnx is None: + return None, result + + cursor = cnx.cursor() + + try: + cursor.execute(f"SELECT * FROM {PROTOKOLL_TABLE_NAME} ORDER BY erstellungsdatum DESC") + data = cursor.fetchall() + column_names = [i[0] for i in cursor.description] + cursor.close() + cnx.close() + return data, column_names + + except mysql.connector.Error as err: + cursor.close() + cnx.close() + if err.errno == errorcode.ER_NO_SUCH_TABLE: + return [], f"⚠️ Tabelle '{PROTOKOLL_TABLE_NAME}' existiert nicht. Speichern Sie zuerst einen Datensatz." + elif err.errno == errorcode.ER_BAD_DB_ERROR: + return [], f"⚠️ Datenbank '{config['database']}' existiert nicht." + return None, f"Fehler beim Abrufen der Daten: {err.msg}" + + +def delete_data_by_id(config, record_id): + """Löscht einen Datensatz anhand seiner ID.""" + cnx, result = get_db_connection(config, with_db=True) + if cnx is None: + return False, result + + cursor = cnx.cursor() + + try: + delete_query = f"DELETE FROM {PROTOKOLL_TABLE_NAME} WHERE id = %s" + cursor.execute(delete_query, (record_id,)) + cnx.commit() + rows_affected = cursor.rowcount + cursor.close() + cnx.close() + + if rows_affected > 0: + return True, f"Datensatz (ID: {record_id}) erfolgreich gelöscht." + else: + return False, f"Datensatz mit ID {record_id} nicht gefunden." + + except mysql.connector.Error as err: + cursor.close() + cnx.close() + return False, f"Fehler beim Löschen des Datensatzes: {err.msg}" + + +def test_db_connection(config): + """Testet die Verbindung zur Datenbank und gibt den Status zurück.""" + port = config.get('port', 3306) + + try: + cnx = mysql.connector.connect( + user=config['user'], + password=config['password'], + host=config['host'], + port=port, + charset='utf8', + database=config['database'] + ) + cnx.close() + return True, "✅ Verbindung erfolgreich hergestellt und Datenbank gefunden." + + except mysql.connector.Error as err: + if err.errno == errorcode.ER_ACCESS_DENIED_ERROR: + return False, "❌ Falscher Benutzername oder Passwort." + elif err.errno == errorcode.CR_CONN_HOST_ERROR: + return False, f"❌ Verbindung zum Host {config['host']} an Port {port} nicht möglich." + elif err.errno == errorcode.ER_BAD_DB_ERROR: + return False, f"⚠️ Datenbank '{config['database']}' existiert nicht. Wird beim Speichern erstellt." + else: + return False, f"❌ Unbekannter Verbindungsfehler: {err.msg}" + except Exception as e: + return False, f"❌ Allgemeiner Fehler: {e}" \ No newline at end of file diff --git a/diggerwf.jpeg b/diggerwf.jpeg new file mode 100644 index 0000000..01e9991 Binary files /dev/null and b/diggerwf.jpeg differ diff --git a/fix_scripts.sh b/fix_scripts.sh new file mode 100644 index 0000000..836fd08 --- /dev/null +++ b/fix_scripts.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# ========================================== +# KONFIGURATION +# ========================================== +# Das Skript, das nach der Reparatur gestartet werden soll +TARGET_TO_CALL="update.sh" +# Liste der benötigten Tools +REQUIRED_TOOLS=("git" "dos2unix" "curl") + +echo "==================================================" +echo "⚙️ SYSTEM-REPARATUR (fix_scripts.sh)" +echo "==================================================" + +# 1. TOOL-CHECK & AUTOMATISCHE INSTALLATION +echo "🔍 Prüfe benötigte Werkzeuge..." +MISSING_TOOLS=() + +for tool in "${REQUIRED_TOOLS[@]}"; do + if ! command -v "$tool" &> /dev/null; then + MISSING_TOOLS+=("$tool") + fi +done + +if [ ${#MISSING_TOOLS[@]} -gt 0 ]; then + echo "📦 Fehlende Tools gefunden: ${MISSING_TOOLS[*]}" + echo "📥 Starte Installation..." + sudo apt update + for tool in "${MISSING_TOOLS[@]}"; do + sudo apt install -y "$tool" + done + echo "✅ Tools erfolgreich installiert." +else + echo "✅ Alle Werkzeuge (git, dos2unix, curl) sind bereit." +fi + +# 2. GIT-KONFLIKT-LÖSUNG (Hard Reset) +# Dies löst den Fehler: "unversionierte Dateien würden überschrieben werden" +if [ -d ".git" ]; then + echo "📦 Git-Repository erkannt. Erzwinge Update vom Server..." + git fetch --all &> /dev/null + # Reset auf den Stand des Servers (überschreibt lokale kaputte Skripte) + git reset --hard origin/$(grep 'BRANCH=' update.sh | cut -d'"' -f2) +else + echo "⚠️ Kein Git-Repository gefunden. Überspringe Git-Reset." +fi + +# 3. FORMAT-REPARATUR (CRLF -> LF) +# Wir reparieren alle .sh Dateien im aktuellen Ordner +echo "🧹 Entferne Windows-Zeilenenden aus allen Skripten..." +dos2unix *.sh &> /dev/null + +# 4. RECHTE SETZEN +echo "🔑 Setze Ausführungsrechte (chmod +x)..." +chmod +x *.sh + +# 5. ABSCHLUSS & ÜBERGABE +if [ -f "./$TARGET_TO_CALL" ]; then + echo "--------------------------------------------------" + echo "🚀 Reparatur abgeschlossen! Starte nun: $TARGET_TO_CALL" + echo "--------------------------------------------------" + ./"$TARGET_TO_CALL" +else + echo "--------------------------------------------------" + echo "❌ Fehler: '$TARGET_TO_CALL' wurde nicht gefunden." + echo "Vorhandene Skripte im Ordner:" + ls -l *.sh +fi + diff --git a/installer.bat b/installer.bat new file mode 100644 index 0000000..0afdb5e --- /dev/null +++ b/installer.bat @@ -0,0 +1,103 @@ +@echo off +setlocal enabledelayedexpansion + +:: --- KONFIGURATION --- +set "REPO_URL=https://github.com/diggerwf/Brokkoli-Gie-planung-helfer.git" +set "BRANCH=main" +set "START_FILE=start4.bat" +:: --------------------- + +echo =========================================== +echo Projekt-Installer ^& Updater +echo =========================================== + +:CHOOSE_FOLDER +echo [1/4] Ordner-Auswahl... +:: PowerShell-Dialog aufrufen +set "psCmd=Add-Type -AssemblyName System.Windows.Forms; $f = New-Object System.Windows.Forms.FolderBrowserDialog; $f.Description = 'Zielordner waehlen'; if($f.ShowDialog() -eq 'OK'){ $f.SelectedPath }" +for /f "delims=" %%I in ('powershell -ExecutionPolicy Bypass -Command "%psCmd%"') do set "TARGET_DIR=%%I" + +:: Falls Fenster geschlossen wurde +if "%TARGET_DIR%"=="" ( + echo [!] Abbruch: Kein Ordner gewaehlt oder Dialog geschlossen. + echo Druecke eine beliebige Taste zum Beenden... + pause + exit /b +) + +echo [+] Gewaehlter Pfad: "!TARGET_DIR!" + +:: WECHSEL IN DEN ORDNER (mit Anführungszeichen für Pfade mit Leerzeichen) +cd /d "!TARGET_DIR!" || ( + echo [!] FEHLER: Konnte nicht in den Ordner wechseln. + pause + exit /b +) + +:CHECK_GIT +echo [2/4] Pruefe Git-Status... +git --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo [!] Git nicht gefunden. Installation wird gestartet... + winget install --id Git.Git -e --source winget --accept-package-agreements --accept-source-agreements + echo [i] Bitte das Skript nach der Installation neu starten. + pause + exit +) + +:PROCESS +echo [3/4] Projekt-Verarbeitung... + +if exist ".git" ( + echo [+] Update-Modus: Synchronisiere... + git remote set-url origin "!REPO_URL!" + git -c credential.helper= fetch origin %BRANCH% --progress + + for /f "tokens=*" %%a in ('git rev-parse HEAD') do set "LOCAL_HASH=%%a" + for /f "tokens=1" %%a in ('git ls-remote origin %BRANCH%') do set "REMOTE_HASH=%%a" + + if "!LOCAL_HASH!"=="!REMOTE_HASH!" ( + echo [+] Status: Alles aktuell. + ) else ( + echo [+] Status: Update wird heruntergeladen... + git pull origin %BRANCH% --progress + ) +) else ( + echo [+] Installations-Modus... + :: Prüfen ob leer + dir /a /b | findstr . >nul 2>&1 + if %errorlevel% equ 0 ( + :: Nicht leer + for %%F in ("%REPO_URL%") do set "DIR_NAME=%%~nF" + echo [!] Ordner nicht leer. Klone in: !DIR_NAME! + git -c credential.helper= clone -b %BRANCH% --progress "!REPO_URL!" "!DIR_NAME!" + if exist "!DIR_NAME!" cd /d "!DIR_NAME!" + ) else ( + :: Leer + echo [+] Klone direkt in Zielverzeichnis... + git -c credential.helper= clone -b %BRANCH% --progress "!REPO_URL!" . + ) +) + +:START_LOGIC +echo [4/4] Start-Check... +echo [i] Pfad: %CD% + +if exist "%START_FILE%" ( + echo [+] Starte %START_FILE%... + echo ------------------------------------------- + call "%START_FILE%" + echo ------------------------------------------- + echo Programm beendet. + pause + exit +) else ( + echo. + echo [!] FEHLER: "%START_FILE%" wurde nicht gefunden. + echo [i] Inhalt von %CD%: + dir /b + echo. + echo Druecke eine beliebige Taste um zu beenden + pause + exit +)