pyTothBD update

This commit is contained in:
Alex38Lyon
2025-05-13 10:17:20 +02:00
parent c7d40af9b1
commit 231a5a22f3
22 changed files with 186 additions and 215 deletions
+11 -10
View File
@@ -72,34 +72,31 @@ def compile_file(filename, **kwargs):
bufsize=1 # ligne par ligne
)
log.info(f"Start therion compilation file : {Colors.MAGENTA}{filename}")
log.info(f"Start therion compilation file : {Colors.WHITE}{filename}")
# Lecture en temps réel
for line in process.stdout:
line = line.rstrip()
lower_line = line.lower()
if "error" in lower_line:
log.error(f"\t\t{line}")
log.error(f"\t\t[Therion_Compile]\t\{line}")
elif "warning" in lower_line:
log.warning(f"\t{line}")
log.warning(f"\t[Therion_Compile]\t{line}")
else:
log.debug(f"\t\t{Colors.WHITE}{line}")
log.debug(f"\t\t[Therion_Compile]\t{Colors.WHITE}{line}")
process.wait()
# Si la commande échoue, result.returncode sera non nul
if process.returncode != 0:
# Affichage des erreurs et de la sortie standard
log.error(f"Error during Therion compilation")
log.error(f"stderr: \n{Colors.MAGENTA}{process.stderr.decode()}")
log.error(f"Error during Therion compilation, stderr : \n{Colors.MAGENTA}{process.stderr.decode()}")
log.info(f"Therion file : {Colors.MAGENTA}{filename} {Colors.GREEN}compilation succeeded")
log.info(f"Therion file : {Colors.WHITE}{filename} {Colors.GREEN}compilation succeeded")
except Exception as e:
log.error(f"Error: Therion file {Colors.ENDC}{filename}{Colors.ERROR} compilation error: {Colors.ENDC}{e}")
#################################################################################################
def compile_file_th(filepath, **kwargs):
template = """source {filepath}
@@ -111,10 +108,12 @@ def compile_file_th(filepath, **kwargs):
logs, _ = compile_template(template, template_args, cleanup=True, **kwargs)
return logs
# Attention, version avec therion en français ! à voir pour les autres langues
#################################################################################################
# Attention fonctionne pour la version therion en français ! à voir pour les autres langues
lengthre = re.compile(r".*Longueur totale de la topographie = \s*(\S+)m")
depthre = re.compile(r".*Longueur totale verticale =\s*(\S+)m")
#################################################################################################
def get_stats_from_log(log):
lenmatch = lengthre.findall(log)
@@ -124,8 +123,10 @@ def get_stats_from_log(log):
return {"length": 0, "depth": 0}
#################################################################################################
syscoord = re.compile(r".*output coordinate system: \s*(\S+)")
#################################################################################################
def get_syscoord_from_log(log):
lenmatch = syscoord.findall(log)
+76 -106
View File
@@ -7,8 +7,9 @@
# By Alexandre PONT alexandre.pont@yahoo.fr #
# #
# Commande : python pyThtoBD.py --help #
# #
# Utilisation: #
# 1 : Placer des fichiers Export_bd.ini dans chacun des dossiers des cavités à exporter #
# 1 : Placer des fichiers vides "Export_bd.ini" dans chacun des dossiers des cavités à exporter #
# 2 : Lancer python pyThtoBD.py, sélectionner le dossier therion à exporter #
# 3 : Résultats pour Karsteau dans le dossier /Outputs/Export_bd/ #
# 4 : (A venir - Importer le résultat de l'importation dans Karsteau ) #
@@ -18,9 +19,8 @@
'''
To do list :
- A gérer le cas de données supprimées dans th... les supprimer de la BD
- Fonction pour importer les clés Karsteau
- Fonction pour importer les clés Karsteau
- Refaire une fonction de calcul de développement/profondeur depuis fichier sql
'''
@@ -42,14 +42,16 @@ from openpyxl import load_workbook
from openpyxl.styles import PatternFill, Border, Side, Alignment, Font
from openpyxl.utils import get_column_letter
Version ="2025.05.12"
Version ="2025.05.13"
export_file ="Export_bd.ini"
export_folder = "/Outputs/Export_bd/"
export_db = "Export_bd.db"
therion_path = "C:/Program Files/Therion/therion.exe"
log_file = "Export_bd.log"
debug_log = False
debug_log = False # Mode debug des messages
debug_exe_therion = False # Coupe l'execution de Therion pour gagner du temps lors des tests
#####################################################################################################################################
@@ -105,7 +107,7 @@ def importation_sql_db(fichier_sql, fichier_db):
connection.close()
log.info(f"Importation réussie de la base de données Therion : {Colors.MAGENTA}{fichier_db}")
log.info(f"Importation réussie de la base de données Therion : {Colors.WHITE}{fichier_db}")
except sqlite3.Error as e:
log.error(f"Erreur lors de l'exécution de la requête importation_sql_data code:{Colors.CYAN}{e}")
@@ -292,7 +294,7 @@ def create_new_db(db_path):
#####################################################################################################################################
# Initialisation de la BD (pour la détection des données supprimées dans Therion # #
# Initialisation de TH_VALIDE de la BD (pour la détection des données supprimées dans Therion) # #
#####################################################################################################################################
def init_th_valide(conn):
cursor = conn.cursor()
@@ -304,9 +306,8 @@ def init_th_valide(conn):
#####################################################################################################################################
# Nom de la cavité #
# Nom de la cavité (extrait du champs -title du fichier -tot.th) #
#####################################################################################################################################
# extrait du champs -title du fichier -tot.th
def cave_name(_path_name):
global error_count
@@ -339,7 +340,8 @@ def cave_name(_path_name):
# Execution du fichier thconfig #
#####################################################################################################################################
def exe_therion_cave(_path_name):
global error_count
global error_count, debug_exe_therion, debug_log
ligne_export_db = "export database "
@@ -357,6 +359,7 @@ def exe_therion_cave(_path_name):
error_count += 1
return
if not debug_exe_therion :
with open(tot_files[0], 'r+', encoding='utf-8') as f:
lignes = f.readlines()
# Cherche une ligne commençant par la commande
@@ -370,6 +373,8 @@ def exe_therion_cave(_path_name):
# print(tot_files[0])
compile_file(tot_files[0].replace("\\", "/"))
else :
log.debug(f"Execution Therion arrêtée (debug_exe_therion = False), pas de nouvelle execution de : {Colors.MAGENTA}{file_name}{Colors.YELLOW}")
#####################################################################################################################################
@@ -437,7 +442,7 @@ def cave_sys(_path_name):
#####################################################################################################################################
# Mise à jour pdf à exporter #
# Lecture de l'exif d'un pdf pour mise à jour données #
#####################################################################################################################################
def pdf_exif(file):
"""
@@ -494,7 +499,7 @@ def pdf_update(conn, base_path, path_name, _update, ID_CAVITE, NAME):
if len(tot_files) > 1 :
log.debug(f"Cavité: {Colors.WHITE}{NAME}{Colors.GREEN}, fichiers pdf à exporter : {Colors.WHITE}{len(tot_files)}")
elif tot_files == [] :
log.warning(f"Attention cavité: {Colors.WHITE}{NAME}{Colors.YELLOW}, pas de fichiers pdf : {Colors.MAGENTA}{_path_name}")
log.warning(f"Attention cavité: {Colors.MAGENTA}{NAME}{Colors.YELLOW}, pas de fichiers pdf : {Colors.MAGENTA}{_path_name}")
return
for file in tot_files:
@@ -650,7 +655,7 @@ def kml_update(conn, base_path, path_name, _update, ID_CAVITE, NAME):
#####################################################################################################################################
# Création des fichiers zip #
# Création des fichiers zip (filtré avec les données brutes nécessaires) #
#####################################################################################################################################
def zip_file(path_source, path_dest, _name_zip):
@@ -708,7 +713,7 @@ def zip_file(path_source, path_dest, _name_zip):
#####################################################################################################################################
# Création du fichier zip avec les données à exporter #
# Création du fichier zip avec l'ensemble des documents à exporter #
#####################################################################################################################################
def zip_data(conn, path_dest, base_path, _name_zip):
@@ -834,7 +839,6 @@ def zip_update(conn, base_path, path_name, _update, ID_CAVITE, Name) :
# Mise à jour des entrées #
#####################################################################################################################################
def entrance_update(conn, base_path, path_name, _update, ID_CAVITE, NAME, syscoord):
global error_count
_path_name = path_name + "/Outputs"
@@ -940,7 +944,7 @@ def entrance_update(conn, base_path, path_name, _update, ID_CAVITE, NAME, syscoo
#####################################################################################################################################
# Update BD #
# Mise à jour des cavités #
#####################################################################################################################################
def cavite_update(conn, file_list, base_path, _update):
cursor = conn.cursor()
@@ -993,7 +997,7 @@ def cavite_update(conn, file_list, base_path, _update):
WHERE ID = ?
""",
(rel_path_name, float(cave_Dev['length']), float(cave_Dev['depth']), 0.0, _update, ID_CAVITE))
log.info(f"Nouvelle cavité insérée avec ID : {Colors.MAGENTA}{ID_CAVITE}{Colors.YELLOW}, Name : {Colors.MAGENTA}{cave_Name}{Colors.YELLOW}, Dev : {Colors.MAGENTA}{cave_Dev['length']}m{Colors.YELLOW}, Prof : {Colors.MAGENTA}{cave_Dev['depth']}m{Colors.YELLOW}, Len path : {Colors.MAGENTA}{len(path_name)}")
log.info(f"Nouvelle cavité insérée avec ID : {Colors.CYAN}{ID_CAVITE}{Colors.YELLOW}, Name : {Colors.CYAN}{cave_Name}{Colors.YELLOW}, Dev : {Colors.CYAN}{cave_Dev['length']}m{Colors.YELLOW}, Prof : {Colors.CYAN}{cave_Dev['depth']}m{Colors.YELLOW}, Len path : {Colors.CYAN}{len(path_name)}")
cave_config['Data_Export'] = {'ID_CAVITE': str(ID_CAVITE)}
with open(file_path, 'w', encoding='utf-8') as configfile:
cave_config.write(configfile)
@@ -1066,11 +1070,13 @@ def db_to_excel(conn, excel_filename):
:param conn: Connexion sqlite3.Connection ouverte
:param excel_filename: Nom du fichier Excel de sortie
:param max_entries: Nombre max de ENTREE par cavité à inclure
:param max_documents: Nombre max de DOCUMENT par cavité à inclure
"""
global error_count
try:
cursor = conn.cursor()
# Récupération du nombre maximum de documents par cavité
cursor.execute("""
SELECT MAX(doc_count)
FROM (
@@ -1082,26 +1088,16 @@ def db_to_excel(conn, excel_filename):
result = cursor.fetchone()
max_documents = result[0] if result[0] is not None else 0
# query = """
# SELECT *
# FROM ENTREE
# INNER JOIN CAVITE ON CAVITE.ID = ENTREE.ID_CAVITE
# """
# df_cavite_2 = pd.read_sql_query(query, conn)
# Charger les tables
df_cav = pd.read_sql_query("SELECT * FROM CAVITE", conn)
# Chargement des tables
df_cav = pd.read_sql_query("SELECT * FROM CAVITE WHERE TH_VALIDE == TRUE", conn)
df_cav.columns = [f"CAVITE_{col}" for col in df_cav.columns]
df_ent = pd.read_sql_query("SELECT * FROM ENTREE", conn)
df_ent = pd.read_sql_query("SELECT * FROM ENTREE WHERE TH_VALIDE == TRUE", conn)
df_ent.columns = [f"ENT_{col}" for col in df_ent.columns]
df_cavite = df_ent.merge(df_cav, how='left', left_on='ENT_ID_CAVITE', right_on='CAVITE_ID')
df_document = pd.read_sql_query("SELECT * FROM DOCUMENT", conn)
# Idem pour DOCUMENT
df_document = pd.read_sql_query("SELECT * FROM DOCUMENT WHERE TH_VALIDE == TRUE", conn)
df_document['DOCUMENT_NUM'] = df_document.groupby('ID_CAVITE').cumcount() + 1
df_document = df_document[df_document['DOCUMENT_NUM'] <= max_documents]
@@ -1109,66 +1105,55 @@ def db_to_excel(conn, excel_filename):
df_document_flat.columns = [f"DOCUMENT_{num}_{col}" for col, num in df_document_flat.columns]
df_document_flat.reset_index(inplace=True)
# print(df_document_flat)
df_document_flat = df_document_flat[sorted(df_document_flat.columns)]
colonnes_tries = sorted(df_document_flat.columns)
# document_columns = sorted([col for col in df_document_flat.columns if col.startswith('DOCUMENT_')], key=lambda x: (int(x.split('_')[1]), x))
df_document_flat = df_document_flat[colonnes_tries]
# print(df_document_flat)
# Fusion avec CAVITE
df_cavite['_est_premiere'] = ~df_cavite.duplicated(subset='ENT_ID_CAVITE', keep='first')
# Joindre normalement
df_merged = df_cavite.merge(df_document_flat, how='left', left_on='ENT_ID_CAVITE', right_on='ID_CAVITE')
# Pour les lignes qui ne sont pas les premières : vider les colonnes venant de df_document_flat
colonnes_document = [col for col in df_document_flat.columns if col != 'ID_CAVITE']
df_merged.loc[~df_merged['_est_premiere'], colonnes_document] = pd.NA
# Nettoyage
df = df_merged.drop(columns=['_est_premiere'])
# print(df)
# Supprimer colonnes de jointure inutiles
df.drop(columns=['ID_CAVITE'], inplace=True, errors='ignore')
df.drop(columns=[col for col in df.columns if '_TH_VALIDE' in col], inplace=True, errors='ignore')
# Export Excel
df.to_excel(excel_filename, index=False)
log.info(f"Exportation Excel terminée : {Colors.WHITE}{excel_filename}")
log.info(f"Exportation excel terminée : {Colors.WHITE}{excel_filename}")
except Exception as e:
error_count += 1
log.critical(f"Erreur critique lors de l'exportation vers Excel : {Colors.MAGENTA}{e}")
#####################################################################################################################################
# Mise en forme du fichier EXCEL #
#####################################################################################################################################
def adapt_excel(excel_filename, selected_folder, update, max_documents=5):
def adapt_excel(excel_filename, selected_folder, update):
"""
Met en forme un fichier Excel produit précédemment : couleurs, largeur colonnes, bordures, etc.
# Chargement pour édition
:param excel_filename: Chemin vers le fichier Excel à modifier
:param selected_folder: Dossier de base pour affichage dans la ligne de titre
:param update: Date ou texte de mise à jour
"""
global error_count
try:
wb = load_workbook(excel_filename)
ws = wb.active
# Définition des styles
yellow_fill = PatternFill(start_color="FFCC00", end_color="FFCC00", fill_type="solid") # Jaune moyen
yellow_low_fill = PatternFill(start_color="FFEA8F", end_color="FFEA8F", fill_type="solid") # Jaune moyen
green_fill = PatternFill(start_color="99CC00", end_color="99CC00", fill_type="solid") # Vert clair
green_low_fill = PatternFill(start_color="E5FF9B", end_color="E5FF9B", fill_type="solid") # Vert clair
blue_fill = PatternFill(start_color="2F97FF", end_color="2F97FF", fill_type="solid") # bleu
blue_low_fill = PatternFill(start_color="99CCFF", end_color="99CCFF", fill_type="solid") # bleu
gris_fill = PatternFill(start_color="777777", end_color="777777", fill_type="solid") # Style de fond gris clair
gris_low_fill = PatternFill(start_color="C0C0C0", end_color="C0C0C0", fill_type="solid") # Style de fond gris clair
header_fill = PatternFill(start_color="DDDDDD", end_color="DDDDDD", fill_type="solid") # Style de fond gris clair
header_alignment = Alignment(horizontal="center", vertical="center", wrap_text=True) # Définir le style pour l'en-tête
header_font = Font(bold=True) # Définir le style pour l'en-tête
# Styles
yellow_low_fill = PatternFill(start_color="FFEA8F", end_color="FFEA8F", fill_type="solid")
green_low_fill = PatternFill(start_color="E5FF9B", end_color="E5FF9B", fill_type="solid")
blue_low_fill = PatternFill(start_color="99CCFF", end_color="99CCFF", fill_type="solid")
gris_fill = PatternFill(start_color="777777", end_color="777777", fill_type="solid")
gris_low_fill = PatternFill(start_color="C0C0C0", end_color="C0C0C0", fill_type="solid")
header_fill = PatternFill(start_color="DDDDDD", end_color="DDDDDD", fill_type="solid")
header_alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
header_font = Font(bold=True)
# Identifier les colonnes à colorier selon leur nom en première ligne
# Coloration conditionnelle des colonnes
for col in range(1, ws.max_column + 1):
header_value = ws.cell(row=1, column=col).value
if header_value is None:
@@ -1182,45 +1167,34 @@ def adapt_excel(excel_filename, selected_folder, update, max_documents=5):
else:
continue
# Appliquer la couleur aux cellules de la colonne (sauf entête si tu veux)
for row in ws.iter_rows(min_row=2, max_row=ws.max_row, min_col=col, max_col=col):
for cell in row:
cell.fill = fill
for col in range(1, ws.max_column + 1):
header_value = ws.cell(row=1, column=col).value
if header_value is None:
continue
if str(header_value).endswith("_ID"):
fill = gris_fill
elif str(header_value).endswith("_ID_CAVITE"):
fill = gris_fill
elif str(header_value).endswith("_PATH"):
if any(str(header_value).endswith(suffix) for suffix in [
"_ID", "_ID_CAVITE", "_PATH", "_DATE_UPDATE", "_HASH_SQL_FILE", "_HASH_FILE"
]):
fill = gris_fill
elif str(header_value).endswith("_KEY_KARSTEAU"):
fill = gris_low_fill
elif str(header_value).endswith("_DATE_UPDATE"):
fill = gris_fill
elif str(header_value).endswith("_HASH_SQL_FILE"):
fill = gris_fill
elif str(header_value).endswith("_HASH_FILE"):
fill = gris_fill
else:
continue
# Appliquer la couleur aux cellules de la colonne (sauf entête si tu veux)
for row in ws.iter_rows(min_row=2, max_row=ws.max_row, min_col=col, max_col=col):
for cell in row:
cell.fill = fill
# Ajustement largeur colonnes
for column_cells in ws.columns:
length = max(len(str(cell.value)) if cell.value is not None else 0 for cell in column_cells)
col_letter = get_column_letter(column_cells[0].column)
ws.column_dimensions[col_letter].width = length + 2 # +2 pour un peu de marge
ws.column_dimensions[col_letter].width = length + 2
# Définir une bordure fine sur tous les côtés
# Bordures fines
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
@@ -1228,42 +1202,37 @@ def adapt_excel(excel_filename, selected_folder, update, max_documents=5):
bottom=Side(style='thin')
)
# Appliquer la bordure à toutes les cellules sauf l'en-tête
for row in ws.iter_rows(min_row=2, max_row=ws.max_row, min_col=1, max_col=ws.max_column):
for cell in row:
cell.border = thin_border
# Appliquer le style à chaque cellule de la première ligne
# Mise en forme ligne den-tête
for cell in ws[1]:
cell.alignment = header_alignment
cell.font = header_font
cell.fill = header_fill
# Définir la hauteur de ligne à 40 pour la ligne 1
ws.row_dimensions[1].height = 40
# Insérer une nouvelle ligne en haut
# Ajout ligne de titre personnalisée
ws.insert_rows(1)
# Mettre un message dans A1
ws['A1'].value = f"Tableau d'échange Therion - Karsteau, base : [{os.path.basename(selected_folder)}], mise à jour le : {update}"
# Appliquer le style à toutes les cellules de la nouvelle première ligne
for col in range(1, ws.max_column + 1):
cell = ws.cell(row=1, column=col)
cell.alignment = Alignment(horizontal="left", vertical="center", wrap_text=False)
cell.font = header_font
cell.fill = header_fill
# Hauteur de ligne
ws.row_dimensions[1].height = 40
# Sauvegarder les modifications
# Sauvegarde
wb.save(excel_filename)
log.info(f"Mise en forme fichier Excel terminée : {Colors.WHITE}{excel_filename}")
log.info(f"Mise en forme fichier excel terminée : {Colors.WHITE}{excel_filename}")
except Exception as e:
error_count += 1
log.error(f"Erreur lors de la mise en forme du fichier Excel : {Colors.MAGENTA}{e}")
#####################################################################################################################################
@@ -1318,11 +1287,12 @@ if __name__ == '__main__':
if not os.path.exists(output_folder):
os.makedirs(output_folder)
print(f"Dossier '{output_folder}' créé.")
else:
print(f"Dossier '{output_folder}' existe déjà.")
# print(f"Dossier '{output_folder}' créé.")
# else:
# print(f"Dossier '{output_folder}' existe déjà.")
log = setup_logger(output_folder + log_file, debug_log)
log = setup_logger(output_folder + log_file, (debug_log or debug_exe_therion))
# log.debug("Ceci est un message de debug")
# log.info("Tout va bien")
@@ -1372,10 +1342,10 @@ if __name__ == '__main__':
conn.close()
if warning_fix > 0 :
log.warning(f"""Nbre de point(s) fixe trouvé(s) : {Colors.CYAN}{warning_fix}{Colors.YELLOW}, vérifier : une entrée doit avoir l'attribut type {Colors.CYAN}station 0 "Entrée XXXX" entrance{Colors.YELLOW} lors de sa déclaration""")
log.warning(f"""Nbre de point(s) fixe trouvé(s) : {Colors.MAGENTA}{warning_fix}{Colors.YELLOW}, vérifier : une entrée doit avoir l'attribut type {Colors.MAGENTA}station 0 "Entrée XXXX" entrance{Colors.YELLOW} lors de sa déclaration""")
if error_count > 0 :
log.error(f"""Nbre d'erreur(s) trouvé(s) : {Colors.CYAN}{error_count}{Colors.YELLOW}, à vérifier""")
log.error(f"""Nbre d'erreur(s) trouvé(s) : {Colors.MAGENTA}{error_count}{Colors.YELLOW}, à vérifier""")
else :
log.info("Aucune d'erreur trouvée, parfait !")
log.info("Fin normale de l'execution du script, aucune d'erreur trouvée, fichier d'excel d'export disponible, parfait !")
Binary file not shown.