Dateien nach "/" hochladen
This commit is contained in:
27
config_manager.py
Normal file
27
config_manager.py
Normal file
@@ -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)
|
||||
260
db_connector.py
Normal file
260
db_connector.py
Normal file
@@ -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}"
|
||||
BIN
diggerwf.jpeg
Normal file
BIN
diggerwf.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
69
fix_scripts.sh
Normal file
69
fix_scripts.sh
Normal file
@@ -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
|
||||
|
||||
103
installer.bat
Normal file
103
installer.bat
Normal file
@@ -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
|
||||
)
|
||||
Reference in New Issue
Block a user