HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/kw-schueler.bak.21032025/import_original_texts.py
#!/usr/bin/env python3
"""
Dieses Skript importiert Originaltexte aus dem Dateisystem in die Datenbank
und ordnet sie dem angegebenen Lehrer zu. 

Verwendung:
    python import_original_texts.py [--dryrun] [--school mos] [--teacher end]
"""

import os
import json
import argparse
import logging
from pathlib import Path
import mysql.connector
from mysql.connector import Error

# Konfiguration des Loggings
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# Basisverzeichnis für die Materialien der Schulen
BASE_DIR = "schule-materialien"

def get_db_connection():
    """Stellt eine Verbindung zur MySQL-Datenbank her."""
    try:
        connection = mysql.connector.connect(
            host="houston.ezoshosting.com",  # Anpassen nach Bedarf
            user="klausurenweb_db",          # Anpassen nach Bedarf
            password=os.environ.get("DB_PASSWORD", ""),  # Passwort aus Umgebungsvariable
            database="klausurenweb_db"       # Anpassen nach Bedarf
        )
        
        if connection.is_connected():
            logger.info("Datenbankverbindung hergestellt")
            return connection
        
    except Error as e:
        logger.error(f"Fehler bei der Datenbankverbindung: {e}")
    
    return None

def get_teacher_id_by_code(connection, school_code, teacher_code):
    """Ermittelt die Lehrer-ID anhand des Schulcodes und Lehrercodes."""
    try:
        cursor = connection.cursor(dictionary=True)
        query = """
            SELECT id FROM teachers 
            WHERE school_code = %s AND teacher_code = %s
        """
        cursor.execute(query, (school_code, teacher_code))
        result = cursor.fetchone()
        cursor.close()
        
        if result:
            return result['id']
        else:
            logger.error(f"Lehrer mit Schule '{school_code}' und Kürzel '{teacher_code}' nicht gefunden")
            return None
            
    except Error as e:
        logger.error(f"Fehler beim Abrufen der Lehrer-ID: {e}")
        return None

def add_original_text(connection, teacher_id, title, content, dry_run=False):
    """Fügt einen Originaltext zur Datenbank hinzu."""
    try:
        if dry_run:
            logger.info(f"DRYRUN: Würde Originaltext '{title}' für Lehrer-ID {teacher_id} hinzufügen")
            return True, 999  # Dummy-ID im Dryrun-Modus
        
        cursor = connection.cursor()
        query = """
            INSERT INTO original_texts (teacher_id, title, content) 
            VALUES (%s, %s, %s)
        """
        cursor.execute(query, (teacher_id, title, content))
        connection.commit()
        
        new_id = cursor.lastrowid
        cursor.close()
        
        logger.info(f"Originaltext '{title}' mit ID {new_id} erfolgreich importiert")
        return True, new_id
        
    except Error as e:
        logger.error(f"Fehler beim Hinzufügen des Originaltexts '{title}': {e}")
        return False, None

def get_material_id(connection, teacher_id, subject_name, assignment_type_name, dry_run=False):
    """Versucht, eine passende Material-ID zu finden."""
    if dry_run:
        # Im Dryrun-Modus geben wir eine Dummy-ID zurück
        logger.info(f"DRYRUN: Würde nach Material für Lehrer {teacher_id}, Fach '{subject_name}', Aufgabentyp '{assignment_type_name}' suchen")
        return 888  # Dummy-ID im Dryrun-Modus
    
    try:
        cursor = connection.cursor(dictionary=True)
        query = """
            SELECT m.id 
            FROM materials m
            JOIN subjects s ON m.subject_id = s.id
            JOIN assignment_types at ON m.assignment_type_id = at.id
            WHERE m.teacher_id = %s AND s.name = %s AND at.name = %s
            LIMIT 1
        """
        cursor.execute(query, (teacher_id, subject_name, assignment_type_name))
        result = cursor.fetchone()
        cursor.close()
        
        if result:
            return result['id']
        return None
            
    except Error as e:
        logger.error(f"Fehler beim Suchen der Material-ID: {e}")
        return None

def assign_text_to_material(connection, material_id, original_text_id, dry_run=False):
    """Ordnet einen Originaltext einem Material zu."""
    if not material_id or not original_text_id:
        return False
    
    try:
        if dry_run:
            logger.info(f"DRYRUN: Würde Originaltext {original_text_id} dem Material {material_id} zuordnen")
            return True
        
        cursor = connection.cursor()
        query = """
            INSERT INTO material_original_texts (material_id, original_text_id) 
            VALUES (%s, %s)
            ON DUPLICATE KEY UPDATE material_id = material_id
        """
        cursor.execute(query, (material_id, original_text_id))
        connection.commit()
        cursor.close()
        
        logger.info(f"Originaltext {original_text_id} erfolgreich dem Material {material_id} zugeordnet")
        return True
        
    except Error as e:
        logger.error(f"Fehler bei der Zuordnung von Originaltext zu Material: {e}")
        return False

def load_display_names(path):
    """Lädt Display-Namen aus display_names.json, falls vorhanden."""
    display_names_path = os.path.join(path, "display_names.json")
    
    if os.path.exists(display_names_path):
        try:
            with open(display_names_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"Fehler beim Laden der Display-Namen: {e}")
    
    return {}

def process_original_texts(connection, teacher_id, school_code, teacher_code, dry_run=False):
    """Verarbeitet alle Originaltexte für den angegebenen Lehrer."""
    base_path = os.path.join(BASE_DIR, school_code, teacher_code)
    
    if not os.path.exists(base_path):
        logger.error(f"Verzeichnis nicht gefunden: {base_path}")
        return
    
    # Gesamtzähler
    total_texts = 0
    imported_texts = 0
    assigned_texts = 0
    
    # Durchlaufe die Verzeichnisstruktur
    for year_dir in os.listdir(base_path):
        year_path = os.path.join(base_path, year_dir)
        if not os.path.isdir(year_path):
            continue
            
        for subject_dir in os.listdir(year_path):
            subject_path = os.path.join(year_path, subject_dir)
            if not os.path.isdir(subject_path):
                continue
                
            for assignment_dir in os.listdir(subject_path):
                assignment_path = os.path.join(subject_path, assignment_dir)
                if not os.path.isdir(assignment_path):
                    continue
                
                # Suche das 'originaltexte' Verzeichnis
                originals_path = os.path.join(assignment_path, "originaltexte")
                if not os.path.exists(originals_path):
                    continue
                
                # Lade Display-Namen
                display_names = load_display_names(originals_path)
                
                # Versuche, eine Material-ID zu finden
                material_id = get_material_id(connection, teacher_id, subject_dir, assignment_dir, dry_run)
                
                # Verarbeite alle HTML-Dateien im Originaltexte-Verzeichnis
                for filename in os.listdir(originals_path):
                    if not filename.endswith('.html'):
                        continue
                    
                    file_path = os.path.join(originals_path, filename)
                    total_texts += 1
                    
                    # Lese den Inhalt der Datei
                    try:
                        with open(file_path, 'r', encoding='utf-8') as f:
                            content = f.read()
                    except Exception as e:
                        logger.error(f"Fehler beim Lesen der Datei {file_path}: {e}")
                        continue
                    
                    # Bestimme den Titel (entweder aus display_names.json oder Dateiname)
                    title = display_names.get(filename, filename.replace('.html', ''))
                    
                    # Füge den Originaltext zur Datenbank hinzu
                    success, text_id = add_original_text(connection, teacher_id, title, content, dry_run)
                    
                    if success:
                        imported_texts += 1
                        
                        # Wenn eine Material-ID gefunden wurde, ordne den Text dem Material zu
                        if material_id and text_id:
                            if assign_text_to_material(connection, material_id, text_id, dry_run):
                                assigned_texts += 1
    
    logger.info(f"Import abgeschlossen: {imported_texts} von {total_texts} Texten importiert")
    logger.info(f"{assigned_texts} Texte wurden Materialien zugeordnet")

def main():
    parser = argparse.ArgumentParser(description='Importiert Originaltexte in die Datenbank')
    parser.add_argument('--school', default='mos', help='Schulcode (z.B. "mos")')
    parser.add_argument('--teacher', default='end', help='Lehrerkürzel (z.B. "end")')
    parser.add_argument('--dryrun', action='store_true', help='Simulation ohne tatsächliche Änderungen')
    
    args = parser.parse_args()
    
    # Überprüfe, ob DB_PASSWORD gesetzt ist
    if not os.environ.get("DB_PASSWORD") and not args.dryrun:
        logger.error("Umgebungsvariable DB_PASSWORD nicht gesetzt. Setze mit: export DB_PASSWORD=dein_passwort")
        return
    
    if args.dryrun:
        logger.info("DRYRUN-MODUS: Es werden keine Änderungen in der Datenbank vorgenommen")
    
    # Stelle eine Verbindung zur Datenbank her (im dryrun-Modus kann dies übersprungen werden)
    connection = None
    if not args.dryrun:
        connection = get_db_connection()
        if not connection:
            return
    
    try:
        # Ermittle die Lehrer-ID
        teacher_id = None
        if not args.dryrun:
            teacher_id = get_teacher_id_by_code(connection, args.school, args.teacher)
            if not teacher_id:
                return
        else:
            teacher_id = 999  # Dummy-ID für dryrun
        
        # Verarbeite alle Originaltexte
        process_original_texts(connection, teacher_id, args.school, args.teacher, args.dryrun)
        
    finally:
        if connection and connection.is_connected():
            connection.close()
            logger.info("Datenbankverbindung geschlossen")

if __name__ == "__main__":
    main()