376 lines
14 KiB
Python
376 lines
14 KiB
Python
# deck_endpoints.py
|
|
|
|
from flask import Blueprint, request, jsonify
|
|
import sqlite3
|
|
import os
|
|
import shutil
|
|
import logging
|
|
|
|
deck_bp = Blueprint('deck_bp', __name__)
|
|
|
|
DATABASE = 'mydatabase.db'
|
|
|
|
# Logger konfigurieren (angenommen, ocr_server3.py konfiguriert das Logging)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def get_db_connection():
|
|
conn = sqlite3.connect(DATABASE)
|
|
conn.row_factory = sqlite3.Row
|
|
return conn
|
|
|
|
# Erstellen der Tabelle Deck, falls sie nicht existiert
|
|
def init_db():
|
|
conn = get_db_connection()
|
|
cursor = conn.cursor()
|
|
# Tabelle Deck erstellen
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS Deck (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
deckname TEXT NOT NULL,
|
|
bildname TEXT,
|
|
bildid TEXT,
|
|
iconindex INTEGER,
|
|
x1 REAL,
|
|
x2 REAL,
|
|
y1 REAL,
|
|
y2 REAL
|
|
)
|
|
''')
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def clean_debug_directories():
|
|
"""
|
|
Löscht alle Verzeichnisse unter 'debug_images', die keinen Eintrag in der Deck-Tabelle mit bildname oder bildid haben.
|
|
"""
|
|
debug_base_dir = 'debug_images'
|
|
if not os.path.exists(debug_base_dir):
|
|
logger.info(f"Debug-Verzeichnis '{debug_base_dir}' existiert nicht. Nichts zu bereinigen.")
|
|
return
|
|
|
|
try:
|
|
conn = get_db_connection()
|
|
cursor = conn.cursor()
|
|
# Sammle alle bildname und bildid, die in der Deck-Tabelle vorhanden sind
|
|
cursor.execute('SELECT DISTINCT bildname FROM Deck WHERE bildname IS NOT NULL')
|
|
bildnames = {row['bildname'] for row in cursor.fetchall()}
|
|
|
|
cursor.execute('SELECT DISTINCT bildid FROM Deck WHERE bildid IS NOT NULL')
|
|
bildids = {row['bildid'] for row in cursor.fetchall()}
|
|
conn.close()
|
|
except sqlite3.Error as e:
|
|
logger.error(f"Fehler beim Abrufen der bildname und bildid aus der Datenbank: {e}")
|
|
return
|
|
|
|
# Durchlaufe alle Verzeichnisse unter 'debug_images'
|
|
for dir_name in os.listdir(debug_base_dir):
|
|
dir_path = os.path.join(debug_base_dir, dir_name)
|
|
if os.path.isdir(dir_path):
|
|
if dir_name not in bildnames and dir_name not in bildids:
|
|
try:
|
|
shutil.rmtree(dir_path)
|
|
logger.info(f"Nicht verwendetes Debug-Verzeichnis gelöscht: {dir_path}")
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Löschen des Verzeichnisses '{dir_path}': {e}")
|
|
else:
|
|
logger.debug(f"Debug-Verzeichnis behalten: {dir_path}")
|
|
|
|
logger.info("Bereinigung der Debug-Verzeichnisse abgeschlossen.")
|
|
|
|
def clean_db_entries():
|
|
"""
|
|
Löscht alle Einträge aus der Deck-Tabelle, für die es kein entsprechendes Verzeichnis unter 'debug_images' gibt.
|
|
"""
|
|
debug_base_dir = 'debug_images'
|
|
if not os.path.exists(debug_base_dir):
|
|
logger.info(f"Debug-Verzeichnis '{debug_base_dir}' existiert nicht. Keine DB-Einträge zu bereinigen.")
|
|
return
|
|
|
|
try:
|
|
# Liste der vorhandenen Verzeichnisse unter 'debug_images'
|
|
existing_dirs = {name for name in os.listdir(debug_base_dir)
|
|
if os.path.isdir(os.path.join(debug_base_dir, name))}
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Zugriff auf Verzeichnisse in '{debug_base_dir}': {e}")
|
|
return
|
|
|
|
try:
|
|
conn = get_db_connection()
|
|
conn.row_factory = sqlite3.Row
|
|
cursor = conn.cursor()
|
|
|
|
# Alle bildid-Werte aus der Deck-Tabelle abrufen, die nicht NULL sind
|
|
cursor.execute('SELECT DISTINCT bildid FROM Deck WHERE bildid IS NOT NULL')
|
|
bildids = {row['bildid'] for row in cursor.fetchall()}
|
|
|
|
# bildid-Werte identifizieren, für die es kein Verzeichnis gibt
|
|
missing_dirs = bildids - existing_dirs
|
|
|
|
if missing_dirs:
|
|
# Einträge aus der Deck-Tabelle löschen, deren bildid keinen entsprechenden Ordner hat
|
|
placeholders = ','.join('?' for _ in missing_dirs)
|
|
sql = f'DELETE FROM Deck WHERE bildid IN ({placeholders})'
|
|
cursor.execute(sql, tuple(missing_dirs))
|
|
deleted_count = cursor.rowcount
|
|
conn.commit()
|
|
logger.info(f"{deleted_count} Einträge aus der Deck-Tabelle gelöscht, deren 'bildid'-Verzeichnisse fehlen.")
|
|
else:
|
|
logger.info("Keine DB-Einträge zu löschen; alle 'bildid'-Verzeichnisse existieren.")
|
|
|
|
conn.close()
|
|
except sqlite3.Error as e:
|
|
logger.error(f"Datenbankfehler beim Löschen von Einträgen aus der Deck-Tabelle: {e}")
|
|
return
|
|
|
|
# ------
|
|
# Endpoints
|
|
# ------
|
|
|
|
# ------
|
|
# Deck - POST, GET, DELETE
|
|
# ------
|
|
|
|
@deck_bp.route('/api/decks', methods=['POST'])
|
|
def create_deck():
|
|
data = request.get_json()
|
|
if not data or 'deckname' not in data:
|
|
return jsonify({'error': 'No deckname provided'}), 400
|
|
|
|
deckname = data['deckname']
|
|
conn = get_db_connection()
|
|
cursor = conn.cursor()
|
|
try:
|
|
# Ein neuer Eintrag für das Deck ohne Bilddaten
|
|
cursor.execute('INSERT INTO Deck (deckname) VALUES (?)', (deckname,))
|
|
conn.commit()
|
|
deck_id = cursor.lastrowid
|
|
conn.close()
|
|
return jsonify({'status': 'success', 'deck_id': deck_id}), 201
|
|
except sqlite3.Error as e:
|
|
conn.close()
|
|
logger.error(f"Datenbankfehler beim Erstellen des Decks: {e}")
|
|
return jsonify({'error': 'Database error', 'details': str(e)}), 500
|
|
|
|
@deck_bp.route('/api/decks', methods=['GET'])
|
|
def get_decks():
|
|
conn = get_db_connection()
|
|
cursor = conn.cursor()
|
|
# Alle Einträge abrufen
|
|
entries = cursor.execute('SELECT * FROM Deck').fetchall()
|
|
conn.close()
|
|
|
|
decks = {}
|
|
for entry in entries:
|
|
deckname = entry['deckname']
|
|
if deckname not in decks:
|
|
decks[deckname] = {
|
|
'name': deckname,
|
|
'images': []
|
|
}
|
|
if entry['bildname'] and entry['bildid']:
|
|
image = {
|
|
'name': entry['bildname'],
|
|
'id': entry['bildid'],
|
|
'iconindex': entry['iconindex'],
|
|
'x1': entry['x1'],
|
|
'x2': entry['x2'],
|
|
'y1': entry['y1'],
|
|
'y2': entry['y2']
|
|
}
|
|
decks[deckname]['images'].append(image)
|
|
|
|
deck_list = list(decks.values())
|
|
return jsonify(deck_list)
|
|
|
|
@deck_bp.route('/api/decks/<deckname>', methods=['DELETE'])
|
|
def delete_deck(deckname):
|
|
conn = get_db_connection()
|
|
cursor = conn.cursor()
|
|
try:
|
|
# Überprüfen, ob das Deck existiert
|
|
cursor.execute('SELECT COUNT(*) as count FROM Deck WHERE deckname = ?', (deckname,))
|
|
result = cursor.fetchone()
|
|
if result['count'] == 0:
|
|
conn.close()
|
|
return jsonify({'error': 'Deck not found'}), 404
|
|
|
|
# Löschen aller Einträge mit dem gegebenen deckname
|
|
cursor.execute('DELETE FROM Deck WHERE deckname = ?', (deckname,))
|
|
conn.commit()
|
|
conn.close()
|
|
return jsonify({'status': 'success'}), 200
|
|
except sqlite3.Error as e:
|
|
conn.close()
|
|
logger.error(f"Datenbankfehler beim Löschen des Decks '{deckname}': {e}")
|
|
return jsonify({'error': 'Database error', 'details': str(e)}), 500
|
|
|
|
# ------
|
|
# Image - POST, GET, DELETE
|
|
# ------
|
|
|
|
@deck_bp.route('/api/decks/image', methods=['POST'])
|
|
def update_image():
|
|
data = request.get_json()
|
|
if not data:
|
|
return jsonify({'error': 'No data provided'}), 400
|
|
|
|
# Überprüfen, ob die erforderlichen Felder vorhanden sind
|
|
required_fields = ['deckname', 'bildname', 'bildid', 'boxes']
|
|
if not all(field in data for field in required_fields):
|
|
return jsonify({'error': 'Missing fields in data'}), 400
|
|
|
|
deckname = data['deckname']
|
|
bildname = data['bildname']
|
|
bildid = data['bildid']
|
|
boxes = data['boxes']
|
|
|
|
# Überprüfen, ob 'boxes' eine Liste ist und mindestens ein Box-Element enthält
|
|
if not isinstance(boxes, list) or len(boxes) == 0:
|
|
return jsonify({'error': "'boxes' must be a non-empty list"}), 400
|
|
|
|
# Verbindung zur Datenbank herstellen
|
|
conn = get_db_connection()
|
|
cursor = conn.cursor()
|
|
|
|
try:
|
|
# Überprüfen, ob das Deck existiert
|
|
cursor.execute('SELECT COUNT(*) as count FROM Deck WHERE deckname = ?', (deckname,))
|
|
result = cursor.fetchone()
|
|
if result['count'] == 0:
|
|
conn.close()
|
|
return jsonify({'error': 'Deck not found'}), 404
|
|
|
|
# 1. Lösche alle Einträge für das Deck ohne bildname
|
|
cursor.execute('DELETE FROM Deck WHERE deckname = ? AND bildname IS NULL', (deckname,))
|
|
|
|
inserted_image_ids = []
|
|
|
|
# 2. Füge neue Image-Einträge hinzu
|
|
for index, box in enumerate(boxes):
|
|
# Überprüfen, ob alle erforderlichen Koordinaten vorhanden sind
|
|
box_fields = ['x1', 'x2', 'y1', 'y2']
|
|
if not all(field in box for field in box_fields):
|
|
conn.close()
|
|
return jsonify({'error': 'Missing fields in one of the boxes'}), 400
|
|
|
|
x1 = box['x1']
|
|
x2 = box['x2']
|
|
y1 = box['y1']
|
|
y2 = box['y2']
|
|
|
|
# Setzen des iconindex auf den aktuellen Index der Box
|
|
iconindex = index
|
|
|
|
cursor.execute('''
|
|
INSERT INTO Deck (deckname, bildname, bildid, iconindex, x1, x2, y1, y2)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
''', (deckname, bildname, bildid, iconindex, x1, x2, y1, y2))
|
|
inserted_image_ids.append(cursor.lastrowid)
|
|
|
|
conn.commit()
|
|
return jsonify({'status': 'success', 'inserted_image_ids': inserted_image_ids}), 201
|
|
|
|
except sqlite3.Error as e:
|
|
conn.rollback()
|
|
logger.error(f"Datenbankfehler beim Aktualisieren der Images für Deck '{deckname}': {e}")
|
|
return jsonify({'error': 'Database error', 'details': str(e)}), 500
|
|
|
|
finally:
|
|
conn.close()
|
|
|
|
# @deck_bp.route('/api/decks/image/<bildid>', methods=['GET'])
|
|
# def get_images_by_bildid(bildid):
|
|
# conn = get_db_connection()
|
|
# cursor = conn.cursor()
|
|
# images = cursor.execute('SELECT * FROM Deck WHERE bildid = ?', (bildid,)).fetchall()
|
|
# conn.close()
|
|
# image_list = [dict(image) for image in images]
|
|
# return jsonify(image_list)
|
|
|
|
@deck_bp.route('/api/decks/image/<bildid>', methods=['DELETE'])
|
|
def delete_images_by_bildid(bildid):
|
|
"""
|
|
Löscht alle Einträge in der Deck-Tabelle für die gegebene bildid.
|
|
Falls es das letzte Image für dieses Deck ist, wird ein neuer Deck-Eintrag erstellt.
|
|
Optional: Löscht das zugehörige Debug-Verzeichnis, wenn keine weiteren Einträge bestehen.
|
|
"""
|
|
try:
|
|
# Sicherheitsmaßnahme: Nur erlaubte Zeichen in bildid
|
|
if not all(c.isalnum() or c in ('_', '-') for c in bildid):
|
|
logger.warning(f"Ungültige bildid angefordert: {bildid}")
|
|
return jsonify({'error': 'Invalid image ID'}), 400
|
|
|
|
conn = get_db_connection()
|
|
cursor = conn.cursor()
|
|
|
|
# Start einer Transaktion
|
|
conn.execute('BEGIN')
|
|
|
|
# Schritt 1: Identifizieren der betroffenen Decks
|
|
cursor.execute('SELECT DISTINCT deckname FROM Deck WHERE bildid = ?', (bildid,))
|
|
affected_decks = {row['deckname'] for row in cursor.fetchall()}
|
|
|
|
if not affected_decks:
|
|
conn.close()
|
|
return jsonify({'error': 'No entries found for the given image ID'}), 404
|
|
|
|
# Schritt 2: Löschen der Image-Einträge
|
|
cursor.execute('DELETE FROM Deck WHERE bildid = ?', (bildid,))
|
|
logger.info(f"Alle Einträge für bildid '{bildid}' wurden gelöscht.")
|
|
|
|
# Schritt 3: Überprüfen und Wiederherstellen der Deck-Einträge
|
|
for deckname in affected_decks:
|
|
# Überprüfen, ob noch andere Image-Einträge für dieses Deck existieren
|
|
cursor.execute('SELECT COUNT(*) as count FROM Deck WHERE deckname = ? AND bildid IS NOT NULL', (deckname,))
|
|
result = cursor.fetchone()
|
|
image_count = result['count'] if result else 0
|
|
|
|
if image_count == 0:
|
|
# Überprüfen, ob bereits ein Deck-Eintrag existiert (ohne bildid)
|
|
cursor.execute('SELECT COUNT(*) as count FROM Deck WHERE deckname = ? AND bildid IS NULL', (deckname,))
|
|
deck_entry = cursor.fetchone()
|
|
if deck_entry['count'] == 0:
|
|
# Ein neuer Deck-Eintrag wird erstellt
|
|
cursor.execute('INSERT INTO Deck (deckname) VALUES (?)', (deckname,))
|
|
logger.info(f"Neuer Deck-Eintrag für '{deckname}' erstellt, da keine weiteren Images vorhanden sind.")
|
|
|
|
# Commit der Transaktion
|
|
conn.commit()
|
|
|
|
# Schritt 4: Optionales Löschen des Debug-Verzeichnisses
|
|
debug_dir = os.path.join('debug_images', bildid)
|
|
if os.path.exists(debug_dir):
|
|
try:
|
|
shutil.rmtree(debug_dir)
|
|
logger.info(f"Debug-Verzeichnis gelöscht: {debug_dir}")
|
|
except Exception as e:
|
|
logger.error(f"Fehler beim Löschen des Debug-Verzeichnisses '{debug_dir}': {e}")
|
|
# Eine Warnung wird zurückgegeben, aber die Anfrage wird als erfolgreich betrachtet
|
|
return jsonify({
|
|
'status': 'success',
|
|
'message': 'Database entries deleted, but failed to delete debug directory.',
|
|
'details': str(e)
|
|
}), 200
|
|
|
|
return jsonify({'status': 'success', 'message': f'All entries for image ID "{bildid}" have been deleted.'}), 200
|
|
|
|
except sqlite3.Error as e:
|
|
# Rollback der Transaktion bei einem Datenbankfehler
|
|
conn.rollback()
|
|
logger.error(f"Datenbankfehler beim Löschen der Image-Einträge für '{bildid}': {e}")
|
|
return jsonify({'error': 'Database error', 'details': str(e)}), 500
|
|
|
|
except Exception as e:
|
|
# Rollback der Transaktion bei einem unerwarteten Fehler
|
|
conn.rollback()
|
|
logger.error(f"Unerwarteter Fehler beim Löschen der Image-Einträge für '{bildid}': {e}")
|
|
return jsonify({'error': 'Failed to delete image entries', 'details': str(e)}), 500
|
|
|
|
finally:
|
|
conn.close()
|
|
|
|
# Sicherstellen, dass die Datenbank existiert
|
|
if not os.path.exists(DATABASE):
|
|
init_db()
|
|
clean_debug_directories()
|
|
clean_db_entries() |