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()