From 82d03f6c4814bd52c8d4878578d5517f645c969e Mon Sep 17 00:00:00 2001 From: Till Tomczak Date: Sat, 10 May 2025 23:12:51 +0200 Subject: [PATCH] "Refactor app. Update import/Improve Python Cython code formatting (#__" --- __pycache__/app.cpython-313.pyc | Bin 105332 -> 115387 bytes app.py | 231 ++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc index 656669f6373488a347de98f18f790ba8e3eb20e3..c7f01c4843f0294667400b7b7015bcee68df7aae 100644 GIT binary patch delta 15275 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 VMSMAhCFv|hTZ!y)nW9}L`(JTDo_GKN delta 7947 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/import', methods=['POST']) +@login_required +def import_mindmap(mindmap_id): + """ + Importiert Daten in eine bestehende Mindmap. + + Die Daten können in verschiedenen Formaten (JSON, XML, CSV) hochgeladen werden. + """ + try: + # Sicherheitscheck: Nur eigene Mindmaps können bearbeitet werden + mindmap = UserMindmap.query.get_or_404(mindmap_id) + + if mindmap.user_id != current_user.id: + return jsonify({ + 'success': False, + 'message': 'Keine Berechtigung zum Bearbeiten dieser Mindmap' + }), 403 + + # Prüfen, ob eine Datei hochgeladen wurde + if 'file' not in request.files: + return jsonify({ + 'success': False, + 'message': 'Keine Datei ausgewählt' + }), 400 + + file = request.files['file'] + + if file.filename == '': + return jsonify({ + 'success': False, + 'message': 'Keine Datei ausgewählt' + }), 400 + + # Format anhand der Dateiendung erkennen + file_ext = file.filename.rsplit('.', 1)[1].lower() if '.' in file.filename else None + + if file_ext not in ['json', 'xml', 'csv']: + return jsonify({ + 'success': False, + 'message': f'Nicht unterstütztes Dateiformat: {file_ext}' + }), 400 + + # Datei einlesen + import_data = None + + if file_ext == 'json': + import_data = json.loads(file.read().decode('utf-8')) + elif file_ext == 'xml': + import xml.etree.ElementTree as ET + import xmltodict + xml_data = file.read().decode('utf-8') + import_data = xmltodict.parse(xml_data) + elif file_ext == 'csv': + import io + import csv + csv_data = file.read().decode('utf-8') + reader = csv.DictReader(io.StringIO(csv_data)) + nodes = [] + for row in reader: + nodes.append(row) + import_data = {'nodes': nodes} + + # Daten in die Mindmap importieren + if 'nodes' in import_data: + # Bestehende Knoten in der Mindmap für Referenz + existing_nodes = db.session.query( + UserMindmapNode.node_id + ).filter_by( + user_mindmap_id=mindmap_id + ).all() + existing_node_ids = [n[0] for n in existing_nodes] + + # Mapping von alten zu neuen Knoten-IDs für importierte Knoten + id_mapping = {} + + # Knoten importieren + for node_data in import_data.get('nodes', []): + # Prüfen, ob es sich um Stringkeys (aus CSV) oder Dict (aus JSON) handelt + if isinstance(node_data, dict): + node_name = node_data.get('name') + node_desc = node_data.get('description', '') + node_color = node_data.get('color_code', '#9F7AEA') + x_pos = float(node_data.get('x_position', 0)) + y_pos = float(node_data.get('y_position', 0)) + node_scale = float(node_data.get('scale', 1.0)) + old_id = node_data.get('id') + else: + # Fallback für andere Formate + continue + + # Neuen Knoten erstellen, wenn nötig + new_node = MindMapNode( + name=node_name, + description=node_desc, + color_code=node_color + ) + db.session.add(new_node) + db.session.flush() # ID generieren + + # Verknüpfung zur Mindmap erstellen + user_node = UserMindmapNode( + user_mindmap_id=mindmap_id, + node_id=new_node.id, + x_position=x_pos, + y_position=y_pos, + scale=node_scale + ) + db.session.add(user_node) + + # ID-Mapping für Beziehungen speichern + if old_id: + id_mapping[old_id] = new_node.id + + # Beziehungen zwischen Knoten importieren + for rel in import_data.get('relationships', []): + source_id = rel.get('source') + target_id = rel.get('target') + + if source_id in id_mapping and target_id in id_mapping: + # Knoten-Objekte holen + source_node = MindMapNode.query.get(id_mapping[source_id]) + target_node = MindMapNode.query.get(id_mapping[target_id]) + + if source_node and target_node: + # Beziehung erstellen + source_node.children.append(target_node) + + db.session.commit() + + return jsonify({ + 'success': True, + 'message': f'{len(import_data.get("nodes", []))} Knoten erfolgreich importiert' + }) + + else: + return jsonify({ + 'success': False, + 'message': 'Keine Knotendaten in der Importdatei gefunden' + }), 400 + + except Exception as e: + db.session.rollback() + print(f"Fehler beim Importieren der Mindmap: {str(e)}") + return jsonify({ + 'success': False, + 'message': f'Fehler beim Importieren: {str(e)}' + }), 500 + # Automatische Datenbankinitialisierung - Aktualisiert für Flask 2.2+ Kompatibilität def initialize_app(): """Initialisierung der Anwendung"""