From bf2a950607b718635276d27b3563f9c1c4aacda4 Mon Sep 17 00:00:00 2001 From: Martin Vanek Date: Fri, 12 Jul 2024 14:42:29 +0200 Subject: [PATCH 1/3] Bugfix xtf_to_ifc, css update --- config.txt => controllers/config.txt | 0 controllers/xtf_to_ifc.py | 45 ++++--- views/static/css/Styles3.css | 20 +++ views/static/css/styles1.css | 20 +++ views/static/css/styles2.css | 20 +++ views/static/css/styles4.css | 70 +++++++---- views/static/css/styles5.css | 112 ++++++++++------- views/templates/index.html | 179 ++++++++++++--------------- 8 files changed, 280 insertions(+), 186 deletions(-) rename config.txt => controllers/config.txt (100%) diff --git a/config.txt b/controllers/config.txt similarity index 100% rename from config.txt rename to controllers/config.txt diff --git a/controllers/xtf_to_ifc.py b/controllers/xtf_to_ifc.py index c14802a..4823226 100644 --- a/controllers/xtf_to_ifc.py +++ b/controllers/xtf_to_ifc.py @@ -1,8 +1,11 @@ import logging import os +import sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import shutil from models.xtf_model import XTFParser from models.ifc_model import create_ifc +from config.default_config import DEFAULT_CONFIG def setup_logging(): logging.basicConfig(level=logging.DEBUG, @@ -10,13 +13,23 @@ def setup_logging(): datefmt='%Y-%m-%d %H:%M:%S') def read_config(config_file='config.txt'): - config = {} + config = DEFAULT_CONFIG.copy() script_dir = os.path.dirname(os.path.abspath(__file__)) config_path = os.path.join(script_dir, config_file) - with open(config_path, 'r') as file: - for line in file: - name, value = line.strip().split('=') - config[name.strip()] = value.strip() + if os.path.exists(config_path): + with open(config_path, 'r') as file: + for line in file: + if '=' in line: + name, value = line.strip().split('=') + name = name.strip() + value = value.strip() + if name in DEFAULT_CONFIG: + # Convert to the same type as in DEFAULT_CONFIG + config[name] = type(DEFAULT_CONFIG[name])(value) + else: + config[name] = value + else: + logging.warning(f"Config file not found: {config_path}. Using default configuration.") return config def get_xtf_files(xtf_path): @@ -24,28 +37,28 @@ def get_xtf_files(xtf_path): return [os.path.join(xtf_path, file) for file in os.listdir(xtf_path) if file.endswith('.xtf')] return [xtf_path] -def convert_xtf_to_ifc(xtf_file, ifc_file): +def convert_xtf_to_ifc(xtf_file, ifc_file, config): parser = XTFParser() - data = parser.parse(xtf_file) + data = parser.parse(xtf_file, config) - create_ifc(ifc_file, data, data['defaults'][5]) + create_ifc(ifc_file, data) logging.info(f"IFC file saved: {ifc_file}") def delete_pycache(): pycache_path = os.path.join(os.path.dirname(__file__), '__pycache__') if os.path.exists(pycache_path): shutil.rmtree(pycache_path) - logging.info(f"__pycache__ Ordner wurde gelöscht: {pycache_path}") + logging.info(f"__pycache__ folder deleted: {pycache_path}") if __name__ == '__main__': setup_logging() - logging.info("Programmstart") + logging.info("Program start") config = read_config('config.txt') - xtf_path = config['xtf_files'] + xtf_path = config.get('xtf_files', 'C:\\converter\\xtf\\') xtf_files = get_xtf_files(xtf_path) - output_folder = config['output_folder'] + output_folder = config.get('output_folder', 'C:\\converter\\') if not os.path.exists(output_folder): os.makedirs(output_folder) @@ -55,12 +68,12 @@ def delete_pycache(): ifc_file_name = os.path.splitext(xtf_file_name)[0] + '.ifc' ifc_file_path = os.path.join(output_folder, ifc_file_name) - logging.info(f"XTF-Dateipfad: {xtf_file_path}") - logging.info(f"IFC-Dateipfad: {ifc_file_path}") + logging.info(f"XTF file path: {xtf_file_path}") + logging.info(f"IFC file path: {ifc_file_path}") try: - convert_xtf_to_ifc(xtf_file_path, ifc_file_path) + convert_xtf_to_ifc(xtf_file_path, ifc_file_path, config) except Exception as e: - logging.error(f"Fehler bei der Konvertierung von XTF zu IFC: {e}", exc_info=True) + logging.error(f"Error during XTF to IFC conversion: {e}", exc_info=True) delete_pycache() \ No newline at end of file diff --git a/views/static/css/Styles3.css b/views/static/css/Styles3.css index cbb46ec..0bf7248 100644 --- a/views/static/css/Styles3.css +++ b/views/static/css/Styles3.css @@ -43,6 +43,26 @@ body { text-shadow: 3px 3px 0 #FF0000, 6px 6px 0 #FF7F00, 9px 9px 0 #FFFF00, 12px 12px 0 #00FF00, 15px 15px 0 #0000FF, 18px 18px 0 #4B0082, 21px 21px 0 #8B00FF; } +.switch-theme-buttons { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; +} + +.switch-theme-buttons .pixel-button { + padding: 8px 16px; + font-size: 0.8em; /* Kleinere Schriftgröße */ + font-family: 'Press Start 2P', cursive; + color: white; + border: none; + background-color: #FF0000; /* Red */ + transition: all 0.3s ease; + box-shadow: 0 4px 0 #C2185B, 0 8px 0 #880E4F; + position: relative; + top: 0; +} + .pixel-button { padding: 15px 30px; font-family: 'Press Start 2P', cursive; diff --git a/views/static/css/styles1.css b/views/static/css/styles1.css index d93477a..5d653d9 100644 --- a/views/static/css/styles1.css +++ b/views/static/css/styles1.css @@ -43,6 +43,26 @@ body { text-shadow: 3px 3px 0 #2196F3, 6px 6px 0 #1565C0; } +.switch-theme-buttons { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; +} + +.switch-theme-buttons .pixel-button { + padding: 8px 16px; + font-size: 0.8em; /* Kleinere Schriftgröße */ + font-family: 'Press Start 2P', cursive; + color: white; + border: none; + background-color: #FF4081; + transition: all 0.3s ease; + box-shadow: 0 4px 0 #C2185B, 0 8px 0 #880E4F; + position: relative; + top: 0; +} + .pixel-button { padding: 15px 30px; font-family: 'Press Start 2P', cursive; diff --git a/views/static/css/styles2.css b/views/static/css/styles2.css index 6ba0e21..14b326a 100644 --- a/views/static/css/styles2.css +++ b/views/static/css/styles2.css @@ -43,6 +43,26 @@ body { text-shadow: 3px 3px 0 #2196F3, 6px 6px 0 #1565C0; } +.switch-theme-buttons { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; +} + +.switch-theme-buttons .pixel-button { + padding: 8px 16px; + font-size: 0.8em; /* Kleinere Schriftgröße */ + font-family: 'Press Start 2P', cursive; + color: white; + border: none; + background-color: #E91E63; + transition: all 0.3s ease; + box-shadow: 0 4px 0 #C2185B, 0 8px 0 #880E4F; + position: relative; + top: 0; +} + .pixel-button { padding: 15px 30px; font-family: 'Press Start 2P', cursive; diff --git a/views/static/css/styles4.css b/views/static/css/styles4.css index 34f09c3..cc17ad1 100644 --- a/views/static/css/styles4.css +++ b/views/static/css/styles4.css @@ -1,6 +1,7 @@ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); body { + font-size: 14px; background-color: #f5f5f5; font-family: 'Roboto', sans-serif; color: #333; @@ -23,24 +24,25 @@ h1 { margin-bottom: 20px; } -.custom-file-input { - display: inline-block; +.custom-file-input, .pixel-button { padding: 12px 24px; font-family: 'Roboto', sans-serif; font-weight: 700; color: white; - background-color: #007BFF; + background-color: #007BFF; /* Blau */ border: none; border-radius: 4px; transition: all 0.3s ease; cursor: pointer; + display: inline-block; + margin: 10px; /* Abstand zwischen den Buttons */ } -.custom-file-input:hover { +.custom-file-input:hover, .pixel-button:hover { background-color: #0056b3; } -.custom-file-input:active { +.custom-file-input:active, .pixel-button:active { background-color: #004085; } @@ -48,26 +50,25 @@ h1 { text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1); } -.pixel-button { - padding: 12px 24px; +.switch-theme-buttons { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; +} + +.switch-theme-buttons .pixel-button { + padding: 8px 16px; + font-size: 0.8em; /* Kleinere Schriftgröße */ font-family: 'Roboto', sans-serif; font-weight: 700; color: white; border: none; - background-color: #28a745; + background-color: #007BFF; /* Blau */ border-radius: 4px; transition: all 0.3s ease; cursor: pointer; - display: block; - margin: 20px auto; -} - -.pixel-button:hover { - background-color: #218838; -} - -.pixel-button:active { - background-color: #1e7e34; + display: inline-block; } .pixel-input { @@ -90,9 +91,9 @@ h1 { } .card-header { - font-size: 1.5em; + font-size: 1.2em; /* Angepasste Schriftgröße */ font-weight: 700; - color: #007BFF; + color: #007BFF; /* Blau */ margin-bottom: 10px; } @@ -148,18 +149,35 @@ h1 { border-top: 1px solid #ddd; } -#preview-container img { - width: 128px; - height: 128px; - border-radius: 8px; - margin-top: 10px; +#file-preview-wrapper { + display: flex; + align-items: flex-start; + justify-content: flex-start; +} + +#preview-container, #output-list { + display: flex; + flex-direction: column; + gap: 5px; + align-items: flex-start; + margin-left: 10px; /* Weniger Abstand */ +} + +#preview-container div, #output-list div { + display: flex; + align-items: center; + gap: 5px; +} + +#preview-container div span, #output-list div span { + font-size: 0.9em; + color: #333; } .file-input-wrapper { position: relative; overflow: hidden; display: inline-block; - width: 100%; } .file-input-wrapper input[type=file] { diff --git a/views/static/css/styles5.css b/views/static/css/styles5.css index 62de8ff..ebc9efc 100644 --- a/views/static/css/styles5.css +++ b/views/static/css/styles5.css @@ -1,7 +1,8 @@ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap'); body { - background-color: #0a192f; + font-size: 14px; + background-color: #181818; font-family: 'Roboto', sans-serif; color: #e0e0e0; line-height: 1.6; @@ -23,78 +24,78 @@ h1 { margin-bottom: 20px; } -.custom-file-input { - display: inline-block; +.custom-file-input, .pixel-button { padding: 12px 24px; font-family: 'Roboto', sans-serif; font-weight: 700; - color: #ffffff; - background-color: #3a7bd5; + color: white; + background-color: #007BFF; /* Blau */ border: none; border-radius: 4px; transition: all 0.3s ease; cursor: pointer; + display: inline-block; + margin: 10px; /* Abstand zwischen den Buttons */ } -.custom-file-input:hover { - background-color: #2c5ea0; +.custom-file-input:hover, .pixel-button:hover { + background-color: #0056b3; } -.custom-file-input:active { - background-color: #1e4976; +.custom-file-input:active, .pixel-button:active { + background-color: #004085; } .text-shadow { - text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2); + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1); } -.pixel-button { - padding: 12px 24px; +.switch-theme-buttons { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; +} + +.switch-theme-buttons .pixel-button { + padding: 8px 16px; + font-size: 0.8em; /* Kleinere Schriftgröße */ font-family: 'Roboto', sans-serif; font-weight: 700; - color: #ffffff; + color: white; border: none; - background-color: #00b4db; + background-color: #007BFF; /* Blau */ border-radius: 4px; transition: all 0.3s ease; cursor: pointer; - display: block; - margin: 20px auto; -} - -.pixel-button:hover { - background-color: #0090b0; -} - -.pixel-button:active { - background-color: #007390; + display: inline-block; } .pixel-input { width: calc(100% - 24px); padding: 10px; font-family: 'Roboto', sans-serif; - border: 1px solid #2c5ea0; + border: 1px solid #ccc; border-radius: 4px; margin-bottom: 10px; box-sizing: border-box; - background-color: #0f2746; + background-color: #282828; color: #e0e0e0; } .card { - background-color: #112240; - border: 1px solid #1e4976; + background-color: #202020; + border: 1px solid #444; border-radius: 8px; margin-bottom: 20px; padding: 20px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } .card-header { - font-size: 1.5em; + font-size: 1.2em; /* Angepasste Schriftgröße */ font-weight: 700; - color: #64a5ff; + color: #007BFF; /* Blau */ margin-bottom: 10px; } @@ -102,9 +103,9 @@ h1 { appearance: none; width: 20px; height: 20px; - border: 1px solid #3a7bd5; + border: 1px solid #007BFF; border-radius: 4px; - background: #0f2746; + background: #282828; cursor: pointer; display: inline-block; position: relative; @@ -113,12 +114,12 @@ h1 { } .form-checkbox:checked { - background-color: #3a7bd5; + background-color: #007BFF; } .form-checkbox:checked::before { content: '✔'; - color: #ffffff; + color: white; font-size: 16px; position: absolute; top: 50%; @@ -133,9 +134,9 @@ h1 { } .alert { - background-color: #1c3d5a; - border: 1px solid #2c5ea0; - color: #64ffda; + background-color: #1e7e34; + border: 1px solid #155724; + color: #d4edda; padding: 15px; border-radius: 4px; margin-top: 20px; @@ -143,25 +144,42 @@ h1 { .footer { font-size: 14px; - color: #8892b0; + color: #888; text-align: center; padding: 20px 0; margin-top: 20px; - border-top: 1px solid #1e4976; + border-top: 1px solid #444; } -#preview-container img { - width: 128px; - height: 128px; - border-radius: 8px; - margin-top: 10px; +#file-preview-wrapper { + display: flex; + align-items: flex-start; + justify-content: flex-start; +} + +#preview-container, #output-list { + display: flex; + flex-direction: column; + gap: 5px; + align-items: flex-start; + margin-left: 10px; /* Weniger Abstand */ +} + +#preview-container div, #output-list div { + display: flex; + align-items: center; + gap: 5px; +} + +#preview-container div span, #output-list div span { + font-size: 0.9em; + color: #e0e0e0; } .file-input-wrapper { position: relative; overflow: hidden; display: inline-block; - width: 100%; } .file-input-wrapper input[type=file] { @@ -173,4 +191,4 @@ h1 { width: 100%; height: 100%; z-index: 10; -} \ No newline at end of file +} diff --git a/views/templates/index.html b/views/templates/index.html index af6b967..2ff24c2 100644 --- a/views/templates/index.html +++ b/views/templates/index.html @@ -4,12 +4,83 @@ XTF to IFC Converter - + @@ -19,12 +90,14 @@

XTF to IFC Converter
Upload XTF Files
-
- Dateien auswählen - +
+
+ Dateien auswählen + +
+

Keine ausgewählt

-
@@ -69,14 +142,15 @@

XTF to IFC Converter
Ausgabe
- +
+
Switch Theme
-
+
@@ -87,94 +161,5 @@

XTF to IFC Converter

- - - \ No newline at end of file + From 930eb6a24eb253bc3cb7b84c2128c6159807fd39 Mon Sep 17 00:00:00 2001 From: Martin Vanek Date: Tue, 16 Jul 2024 06:51:52 +0200 Subject: [PATCH 2/3] exapmle endpoints --- api/endpoints.py | 103 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/api/endpoints.py b/api/endpoints.py index e69de29..ad61f10 100644 --- a/api/endpoints.py +++ b/api/endpoints.py @@ -0,0 +1,103 @@ +from flask import Blueprint, request, jsonify +from werkzeug.utils import secure_filename +import os +from controllers.conversion_controller import handle_conversion_request +from config.default_config import DEFAULT_CONFIG + +# Erstellen eines Blueprint für die API +api = Blueprint('api', __name__) + +# Definieren des Upload-Ordners und Erstellen, falls er nicht existiert +UPLOAD_FOLDER = '/tmp/uploads' +if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER) + +# Globale Variable für die aktuelle Konfiguration, initialisiert mit den Standardwerten +current_config = DEFAULT_CONFIG.copy() + +@api.route('/convert', methods=['POST']) +def convert(): + """ + Endpunkt zum Konvertieren von XTF-Dateien zu IFC. + Akzeptiert XTF-Dateien und optionale Konfigurationsparameter. + """ + if 'xtfFiles' not in request.files: + return jsonify({'error': 'Keine Dateien ausgewählt'}), 400 + + files = request.files.getlist('xtfFiles') + + # Erstellen einer Kopie der aktuellen Konfiguration und Aktualisieren mit Werten aus der Anfrage + config = current_config.copy() + for key in config.keys(): + if key in request.form: + config[key] = type(config[key])(request.form.get(key)) + + # Speichern der hochgeladenen Dateien + saved_files = [] + for file in files: + if file and file.filename.endswith('.xtf'): + filename = secure_filename(file.filename) + file_path = os.path.join(UPLOAD_FOLDER, filename) + file.save(file_path) + saved_files.append(file_path) + + # Durchführen der Konvertierung + result = handle_conversion_request(config, saved_files) + + return jsonify(result) + +@api.route('/config', methods=['GET']) +def get_config(): + """ + Endpunkt zum Abrufen der aktuellen Konfiguration. + """ + return jsonify(current_config) + +@api.route('/config', methods=['POST']) +def update_config(): + """ + Endpunkt zum Aktualisieren mehrerer Konfigurationswerte gleichzeitig. + """ + global current_config + for key, value in request.json.items(): + if key in current_config: + try: + # Versuche, den Wert in den korrekten Typ zu konvertieren + current_config[key] = type(current_config[key])(value) + except ValueError: + return jsonify({'error': f'Ungültiger Wert für {key}'}), 400 + return jsonify(current_config), 200 + +@api.route('/config/reset', methods=['POST']) +def reset_config(): + """ + Endpunkt zum Zurücksetzen der Konfiguration auf die Standardwerte. + """ + global current_config + current_config = DEFAULT_CONFIG.copy() + return jsonify(current_config), 200 + +@api.route('/config/', methods=['GET']) +def get_config_value(key): + """ + Endpunkt zum Abrufen eines spezifischen Konfigurationswerts. + """ + if key in current_config: + return jsonify({key: current_config[key]}) + return jsonify({'error': 'Konfigurationsschlüssel nicht gefunden'}), 404 + +@api.route('/config/', methods=['PUT']) +def update_config_value(key): + """ + Endpunkt zum Aktualisieren eines spezifischen Konfigurationswerts. + """ + global current_config + if key in current_config: + try: + value = request.json.get('value') + # Konvertieren des Werts in den korrekten Typ + current_config[key] = type(current_config[key])(value) + return jsonify({key: current_config[key]}) + except ValueError: + return jsonify({'error': 'Ungültiger Wert'}), 400 + return jsonify({'error': 'Konfigurationsschlüssel nicht gefunden'}), 404 \ No newline at end of file From 0b81e8b81bcedcbc9bd3d54913e0d9b10c6b3cbb Mon Sep 17 00:00:00 2001 From: Martin Vanek Date: Fri, 19 Jul 2024 09:29:33 +0200 Subject: [PATCH 3/3] Improvement missing values, psets --- models/ifc_model.py | 2 +- utils/graphics_ns.py | 59 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/models/ifc_model.py b/models/ifc_model.py index db1c1b1..f61fa6f 100644 --- a/models/ifc_model.py +++ b/models/ifc_model.py @@ -72,7 +72,7 @@ def create_ifc_haltungen(ifc_file, data, site, context, haltungen_group): einfaerben = data['einfaerben'] for haltung in haltungen: - innendurchmesser = haltung.get('durchmesser', default_durchmesser) + innendurchmesser = haltung.get('durchmesser') outer_radius = (innendurchmesser / 2) + default_rohrdicke inner_radius = innendurchmesser / 2 diff --git a/utils/graphics_ns.py b/utils/graphics_ns.py index 89e62d5..9e04dee 100644 --- a/utils/graphics_ns.py +++ b/utils/graphics_ns.py @@ -79,19 +79,39 @@ def create_ifc_normschacht(ifc_file, ns, abwasserknoten, facility, context, abwa properties = { "Bezeichnung": ns.get('bezeichnung', ''), "Standortname": ns.get('standortname', ''), - "Höhe": str(hoehe), - "Durchmesser": str(breite), + "Höhe": '', + "Durchmesser": '', "Funktion": ns.get('funktion', ''), "Material": ns.get('material', ''), - "Sohlenkote": str(z_mitte) + "Sohlenkote": '' } + # Fülle Höhe und Durchmesser nur, wenn sie vorhanden und nicht '0' sind + if ns.get('dimorg1') and ns.get('dimorg1') != '0': + properties["Höhe"] = ns['dimorg1'] + if ns.get('dimorg2') and ns.get('dimorg2') != '0': + properties["Durchmesser"] = ns['dimorg2'] + + # Fülle die Sohlenkote nur, wenn sie in den originalen Daten vorhanden und gültig ist + if abwasserknoten: + kote = abwasserknoten.get('kote') + if kote is not None and kote != '' and kote != '0': + try: + float_kote = float(kote) + if float_kote != 0 and float_kote != data.get('default_sohlenkote', 0): + properties["Sohlenkote"] = str(float_kote) + except ValueError: + pass # Ungültiger Wert, ignorieren + + + # Erstelle Property Set property_set = ifc_file.create_entity("IfcPropertySet", GlobalId=generate_guid(), Name="TBAKTZH STRE Schacht", HasProperties=[create_property_single_value(ifc_file, key, value) for key, value in properties.items()] ) + # Verknüpfe das Property Set mit dem Schacht ifc_file.create_entity("IfcRelDefinesByProperties", GlobalId=generate_guid(), RelatingPropertyDefinition=property_set, @@ -114,15 +134,38 @@ def create_ifc_normschacht(ifc_file, ns, abwasserknoten, facility, context, abwa fehlende_werte = 0 farbe = "Blau" if einfaerben: - if ns.get('dimorg1') == '0' or ns.get('dimorg2') == '0': + # Prüfe Dimension1 (Höhe) + if ns.get('dimorg1') == '0' or not ns.get('dimorg1'): fehlende_werte += 1 - if not abwasserknoten or float(abwasserknoten.get('kote', 0)) == 0: + + # Prüfe Dimension2 (Durchmesser) + if ns.get('dimorg2') == '0' or not ns.get('dimorg2'): + fehlende_werte += 1 + + # Prüfe Sohlenkote + if abwasserknoten: + kote = abwasserknoten.get('kote') + if kote is None or kote == '' or kote == '0': + fehlende_werte += 1 + else: + try: + float_kote = float(kote) + if float_kote == 0 or float_kote == data.get('default_sohlenkote', 0): + fehlende_werte += 1 + except ValueError: + fehlende_werte += 1 + else: + # Wenn kein Abwasserknoten vorhanden ist fehlende_werte += 1 - farbe = "Grün" - if fehlende_werte == 1: + # Bestimme die Farbe basierend auf fehlenden Werten + if fehlende_werte == 0: + farbe = "Grün" + elif fehlende_werte == 1: farbe = "Orange" - elif fehlende_werte >= 2: + else: # fehlende_werte >= 2 farbe = "Rot" + print(f"Fehlende Werte: {fehlende_werte}, Farbe: {farbe}") # Debugging-Ausgabe + add_color(ifc_file, schacht, farbe, context) \ No newline at end of file