Neue Endpunkte

This commit is contained in:
Andreas Knuth 2024-11-27 20:30:28 +01:00
parent 38d2280b88
commit 58d651d981
5 changed files with 215 additions and 29 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@
__pycache__ __pycache__
database.db database.db
debug_images debug_images
*.db
*.lock

View File

@ -3,11 +3,16 @@
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
import sqlite3 import sqlite3
import os import os
import shutil
import logging
deck_bp = Blueprint('deck_bp', __name__) deck_bp = Blueprint('deck_bp', __name__)
DATABASE = 'mydatabase.db' DATABASE = 'mydatabase.db'
# Logger konfigurieren (angenommen, ocr_server3.py konfiguriert das Logging)
logger = logging.getLogger(__name__)
def get_db_connection(): def get_db_connection():
conn = sqlite3.connect(DATABASE) conn = sqlite3.connect(DATABASE)
conn.row_factory = sqlite3.Row conn.row_factory = sqlite3.Row
@ -41,6 +46,48 @@ def init_db():
conn.commit() conn.commit()
conn.close() conn.close()
def clean_debug_directories():
"""
Löscht alle Verzeichnisse unter 'debug_images', die keinen Eintrag in der Image-Tabelle 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()
cursor.execute('SELECT DISTINCT bildname FROM Image')
bildnames = {row['bildname'] for row in cursor.fetchall()}
conn.close()
except sqlite3.Error as e:
logger.error(f"Fehler beim Abrufen der bildname 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:
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.")
# ------
# Endpoints
# ------
# ------
# Deck - POST, GET, DELETE
# ------
@deck_bp.route('/api/decks', methods=['POST']) @deck_bp.route('/api/decks', methods=['POST'])
def create_deck(): def create_deck():
data = request.get_json() data = request.get_json()
@ -91,6 +138,7 @@ def get_decks():
conn.close() conn.close()
return jsonify(deck_list) return jsonify(deck_list)
@deck_bp.route('/api/decks/<deckname>', methods=['DELETE']) @deck_bp.route('/api/decks/<deckname>', methods=['DELETE'])
def delete_deck(deckname): def delete_deck(deckname):
conn = get_db_connection() conn = get_db_connection()
@ -110,36 +158,75 @@ def delete_deck(deckname):
conn.close() conn.close()
return jsonify({'error': 'Deck not found'}), 404 return jsonify({'error': 'Deck not found'}), 404
@deck_bp.route('/image', methods=['PUT']) # ------
# Image - POST, GET, DELETE
# ------
@deck_bp.route('/api/decks/image', methods=['POST'])
def update_image(): def update_image():
data = request.get_json() data = request.get_json()
if not data: if not data:
return jsonify({'error': 'No data provided'}), 400 return jsonify({'error': 'No data provided'}), 400
required_fields = ['deckid', 'bildname', 'iconindex', 'x1', 'x2', 'y1', 'y2'] # Überprüfen, ob die erforderlichen Felder vorhanden sind
required_fields = ['deckname', 'image', 'boxes']
if not all(field in data for field in required_fields): if not all(field in data for field in required_fields):
return jsonify({'error': 'Missing fields in data'}), 400 return jsonify({'error': 'Missing fields in data'}), 400
deckid = data['deckid'] deckname = data['deckname']
bildname = data['bildname'] bildname = data['image']
iconindex = data['iconindex'] boxes = data['boxes']
x1 = data['x1']
x2 = data['x2']
y1 = data['y1']
y2 = data['y2']
# Ü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() conn = get_db_connection()
cursor = conn.cursor() cursor = conn.cursor()
try:
# Deck-ID anhand des Decknamens abrufen
cursor.execute('SELECT id FROM Deck WHERE deckname = ?', (deckname,))
deck = cursor.fetchone()
if not deck:
return jsonify({'error': 'Deck not found'}), 404
deck_id = deck['id']
inserted_image_ids = []
# Durch jede Box iterieren und einen Eintrag in der Image-Tabelle erstellen
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):
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(''' cursor.execute('''
INSERT INTO Image (deckid, bildname, iconindex, x1, x2, y1, y2) INSERT INTO Image (deckid, bildname, iconindex, x1, x2, y1, y2)
VALUES (?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?)
''', (deckid, bildname, iconindex, x1, x2, y1, y2)) ''', (deck_id, bildname, iconindex, x1, x2, y1, y2))
conn.commit() inserted_image_ids.append(cursor.lastrowid)
image_id = cursor.lastrowid
conn.close()
return jsonify({'status': 'success', 'image_id': image_id}), 201
@deck_bp.route('/image/<bildname>', methods=['GET']) conn.commit()
return jsonify({'status': 'success', 'inserted_image_ids': inserted_image_ids}), 201
except sqlite3.Error as e:
conn.rollback()
return jsonify({'error': 'Database error', 'details': str(e)}), 500
finally:
conn.close()
@deck_bp.route('/api/decks/image/<bildname>', methods=['GET'])
def get_images_by_bildname(bildname): def get_images_by_bildname(bildname):
conn = get_db_connection() conn = get_db_connection()
images = conn.execute('SELECT * FROM Image WHERE bildname = ?', (bildname,)).fetchall() images = conn.execute('SELECT * FROM Image WHERE bildname = ?', (bildname,)).fetchall()
@ -147,16 +234,64 @@ def get_images_by_bildname(bildname):
image_list = [dict(image) for image in images] image_list = [dict(image) for image in images]
return jsonify(image_list) return jsonify(image_list)
@deck_bp.route('/image/<bildname>/<int:iconindex>', methods=['GET']) @deck_bp.route('/api/decks/image/<bildname>', methods=['DELETE'])
def get_image_by_bildname_and_index(bildname, iconindex): def delete_images_by_bildname(bildname):
"""
Löscht alle Einträge in der Image-Tabelle für den gegebenen bildname.
Optional: Löscht das zugehörige Debug-Verzeichnis, wenn keine weiteren Einträge bestehen.
"""
try:
# Sicherheitsmaßnahme: Nur erlaubte Zeichen im bildname
if not all(c.isalnum() or c in ('_', '-') for c in bildname):
logger.warning(f"Ungültiger bildname angefordert: {bildname}")
return jsonify({'error': 'Invalid image name'}), 400
conn = get_db_connection() conn = get_db_connection()
image = conn.execute('SELECT * FROM Image WHERE bildname = ? AND iconindex = ?', (bildname, iconindex)).fetchone() cursor = conn.cursor()
# Überprüfen, ob es Einträge mit dem bildname gibt
cursor.execute('SELECT COUNT(*) as count FROM Image WHERE bildname = ?', (bildname,))
result = cursor.fetchone()
count = result['count'] if result else 0
if count == 0:
conn.close() conn.close()
if image is None: return jsonify({'error': 'No entries found for the given image name'}), 404
return jsonify({'error': 'Image not found'}), 404
else: # Löschen der Einträge
return jsonify(dict(image)), 200 cursor.execute('DELETE FROM Image WHERE bildname = ?', (bildname,))
conn.commit()
conn.close()
# Optional: Löschen des Debug-Verzeichnisses, wenn keine weiteren Einträge bestehen
# Überprüfen, ob das Verzeichnis existiert
debug_dir = os.path.join('debug_images', bildname)
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}")
# Sie können entscheiden, ob Sie einen Fehler zurückgeben oder nicht
# Hier geben wir eine Warnung zurück, aber setzen die Anfrage als erfolgreich fort
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 "{bildname}" have been deleted.'}), 200
except sqlite3.Error as e:
logger.error(f"Fehler beim Löschen der Image-Einträge für '{bildname}': {e}")
return jsonify({'error': 'Database error', 'details': str(e)}), 500
except Exception as e:
logger.error(f"Unerwarteter Fehler beim Löschen der Image-Einträge für '{bildname}': {e}")
return jsonify({'error': 'Failed to delete image entries', 'details': str(e)}), 500
# Sicherstellen, dass die Datenbank existiert # Sicherstellen, dass die Datenbank existiert
if not os.path.exists(DATABASE): if not os.path.exists(DATABASE):
init_db() init_db()
clean_debug_directories()

View File

@ -0,0 +1,3 @@
-- database: ./mydatabase.db
SELECT distinct bildname FROM Image;

View File

@ -1,4 +1,4 @@
from flask import Flask, request, jsonify from flask import Flask, request, jsonify, send_file
from paddleocr import PaddleOCR from paddleocr import PaddleOCR
import base64 import base64
from PIL import Image from PIL import Image
@ -202,5 +202,32 @@ def ocr_endpoint():
'debug_dir': debug_dir if 'debug_dir' in locals() else None 'debug_dir': debug_dir if 'debug_dir' in locals() else None
}), 500 }), 500
@app.route('/api/debug_image/<name>', methods=['GET'])
def get_debug_image(name):
"""
Gibt das Originalbild unter 'debug_images/[name]/original.png' direkt als image/png zurück.
"""
try:
# Sicherheitsmaßnahme: Nur erlaubte Zeichen im Namen
if not all(c.isalnum() or c in ('_', '-') for c in name):
logger.warning(f"Ungültiger Bildname angefordert: {name}")
return jsonify({'error': 'Invalid image name'}), 400
image_path = os.path.join('debug_images', name, 'original.png')
if not os.path.isfile(image_path):
logger.warning(f"Bild nicht gefunden: {image_path}")
return jsonify({'error': 'Image not found'}), 404
return send_file(
image_path,
mimetype='image/png',
as_attachment=False
)
except Exception as e:
logger.error(f"Fehler beim Abrufen des Bildes '{name}': {str(e)}")
logger.error(traceback.format_exc())
return jsonify({'error': 'Failed to retrieve image'}), 500
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True) app.run(host='0.0.0.0', port=5000, debug=True)

19
pyproject.toml Normal file
View File

@ -0,0 +1,19 @@
[tool.poetry]
name = "vocab-backend"
version = "0.1.0"
description = ""
authors = ["Andreas Knuth <andreas.knuth@gmail.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
flask = "^2.2.3"
paddleocr = "^2.9.1"
pillow = "^9.4.0"
numpy = "^1.23.5" # Korrigiere dies auf eine 1.x-Version
opencv-python = "^4.6.0.66"
paddlepaddle = "^2.6.2"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"