diff --git a/src/database.py b/src/database.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..df5be0a0ecce810fad633820f9eaea61c19f192d 100644 --- a/src/database.py +++ b/src/database.py @@ -0,0 +1,149 @@ +import mysql.connector +from mysql.connector import errorcode + +# --- MySQL Server Konfiguration --- +DB_CONFIG = { + 'host': 'localhost', # z.B. 'localhost' oder Ihre DB-Server-IP + 'user': 'your_mysql_user', # Ihr MySQL-Benutzername + 'password': 'your_mysql_password', # Ihr MySQL-Passwort + 'database': 'hhz_video_analytics' # Der Name Ihrer Datenbank +} + +TABLES = {} + +# Tabelle für Video-Metadaten +TABLES['Videos'] = ( + "CREATE TABLE IF NOT EXISTS `Videos` (" + " `video_id` INT AUTO_INCREMENT PRIMARY KEY," + " `youtube_url` VARCHAR(512) NOT NULL UNIQUE," + " `title` VARCHAR(255) DEFAULT NULL," + " `duration_seconds` INT DEFAULT NULL," + " `downloaded_filepath` VARCHAR(1024) DEFAULT NULL," + " `resolution_width` INT DEFAULT NULL," + " `resolution_height` INT DEFAULT NULL," + " `fps` FLOAT DEFAULT NULL," + " `analysis_status` ENUM('pending', 'downloading', 'processing_frames', 'processing_yolo', 'completed', 'failed') " + " NOT NULL DEFAULT 'pending'," + " `processed_at` TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP," + " `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci") + +# Tabelle für extrahierte Frames +TABLES['Frames'] = ( + "CREATE TABLE IF NOT EXISTS `Frames` (" + " `frame_id` INT AUTO_INCREMENT PRIMARY KEY," + " `video_id` INT NOT NULL," + " `frame_timestamp_sec` DECIMAL(10, 3) NOT NULL," # z.B. 123.456 Sekunden + " `frame_filepath` VARCHAR(1024) DEFAULT NULL," # Pfad zum gespeicherten Frame-Bild + " `width` INT DEFAULT NULL," # tatsächliche Breite des Frames + " `height` INT DEFAULT NULL," # tatsächliche Höhe des Frames + " `processed_yolo_v8` BOOLEAN NOT NULL DEFAULT FALSE," # Status ob YOLOv8 Analyse erfolgt ist + " `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP," + " FOREIGN KEY (`video_id`)" + " REFERENCES `Videos`(`video_id`)" + " ON DELETE CASCADE," # Wenn ein Video gelöscht wird, werden auch die zugehörigen Frames gelöscht + " INDEX `idx_video_id_timestamp` (`video_id`, `frame_timestamp_sec`)" # Index für häufige Abfragen + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci") + +# Tabelle für YOLOv8 Detektionen +TABLES['DetectionsYOLOv8'] = ( + "CREATE TABLE IF NOT EXISTS `DetectionsYOLOv8` (" + " `detection_id` INT AUTO_INCREMENT PRIMARY KEY," + " `frame_id` INT NOT NULL," + " `class_id` INT NOT NULL," # YOLOv8 numerische Klassen-ID + " `class_name` VARCHAR(100) NOT NULL," # YOLOv8 Klassenname (z.B. 'car', 'person') + " `confidence` FLOAT NOT NULL," # Konfidenzwert der Detektion (0.0 bis 1.0) + " `x_center` INT NOT NULL," # Mittelpunkt X der Bounding Box + " `y_center` INT NOT NULL," # Mittelpunkt Y der Bounding Box + " `width` INT NOT NULL," # Breite der Bounding Box + " `height` INT NOT NULL," # Höhe der Bounding Box + " `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP," + " FOREIGN KEY (`frame_id`)" + " REFERENCES `Frames`(`frame_id`)" + " ON DELETE CASCADE," # Wenn ein Frame gelöscht wird, werden auch die Detektionen gelöscht + " INDEX `idx_frame_id` (`frame_id`)," + " INDEX `idx_class_name` (`class_name`)" # Index für Abfragen nach bestimmten Objekten + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci") + +# Optionale Tabelle für aggregierte Analysen pro Video +TABLES['VideoAnalyticsSummary'] = ( + "CREATE TABLE IF NOT EXISTS `VideoAnalyticsSummary` (" + " `summary_id` INT AUTO_INCREMENT PRIMARY KEY," + " `video_id` INT NOT NULL UNIQUE," # Stellt sicher, dass es nur einen Summary-Eintrag pro Video gibt + " `most_frequent_object_yolo` VARCHAR(100) DEFAULT NULL," + " `object_counts_json_yolo` JSON DEFAULT NULL," # Speichert ein JSON-Objekt mit {object_name: count} + " `llm_generated_description` TEXT DEFAULT NULL," + " `average_objects_per_frame_yolo` FLOAT DEFAULT NULL," + " `custom_stat_placeholder_1` VARCHAR(255) DEFAULT NULL," # Für zukünftige Erweiterungen + " `last_updated` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," + " FOREIGN KEY (`video_id`)" + " REFERENCES `Videos`(`video_id`)" + " ON DELETE CASCADE" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci") + + +def create_database_if_not_exists(cursor, db_name): + try: + cursor.execute( + f"CREATE DATABASE IF NOT EXISTS `{db_name}` DEFAULT CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'") + print(f"Datenbank '{db_name}' erfolgreich erstellt oder existiert bereits.") + except mysql.connector.Error as err: + print(f"Fehler beim Erstellen der Datenbank '{db_name}': {err}") + exit(1) + + +def create_tables(cursor): + for table_name, table_description in TABLES.items(): + try: + print(f"Erstelle Tabelle '{table_name}'... ", end='') + cursor.execute(table_description) + print("OK") + except mysql.connector.Error as err: + if err.errno == errorcode.ER_TABLE_EXISTS_ERROR: + print("existiert bereits.") + else: + print(f"Fehler: {err.msg}") + else: + pass # Tabelle erfolgreich erstellt oder existierte bereits + + +def main(): + cnx = None # Initialisiere cnx außerhalb des try-Blocks + try: + # Zuerst ohne Angabe der Datenbank verbinden, um sie ggf. zu erstellen + temp_cnx = mysql.connector.connect( + host=DB_CONFIG['host'], + user=DB_CONFIG['user'], + password=DB_CONFIG['password'] + ) + cursor = temp_cnx.cursor() + create_database_if_not_exists(cursor, DB_CONFIG['database']) + temp_cnx.database = DB_CONFIG['database'] # Wechsle zur erstellten/existierenden DB + cursor.close() + temp_cnx.close() + + # Jetzt mit der spezifizierten Datenbank verbinden + cnx = mysql.connector.connect(**DB_CONFIG) + cursor = cnx.cursor() + print(f"\nErfolgreich mit MySQL-Datenbank '{DB_CONFIG['database']}' verbunden.") + + create_tables(cursor) + + except mysql.connector.Error as err: + if err.errno == errorcode.ER_ACCESS_DENIED_ERROR: + print("Zugriff verweigert. Überprüfen Sie Benutzername und Passwort.") + elif err.errno == errorcode.ER_BAD_DB_ERROR: + print( + f"Datenbank '{DB_CONFIG['database']}' existiert nicht und konnte nicht erstellt werden (oder keine Berechtigung).") + else: + print(f"Ein MySQL-Fehler ist aufgetreten: {err}") + finally: + if cnx and cnx.is_connected(): + cursor.close() + cnx.close() + print("\nMySQL-Verbindung geschlossen.") + + +if __name__ == "__main__": + print("Starte Skript zum Erstellen des Datenbankschemas...") + main() \ No newline at end of file