Ajout fonctionnalités, mise à jour du README.md

- Les noms de fichiers copiés suivent dorénavant un shémas de nommage.
This commit is contained in:
David Castex 2025-09-11 10:51:34 +02:00
parent 115a3748f7
commit d65b146fde
2 changed files with 1273 additions and 1191 deletions

View File

@ -56,7 +56,7 @@ SEP = ";"
# (MOTIF)+ matche 1 OU PLUS occurence du mot MOTIF # (MOTIF)+ matche 1 OU PLUS occurence du mot MOTIF
# (?:) le groupe ne sera pas capturé, du coup dans python le groupe ne créé pas de chaine vide si pas de match du groupe en question # (?:) le groupe ne sera pas capturé, du coup dans python le groupe ne créé pas de chaine vide si pas de match du groupe en question
# | OR # | OR
EXPRESSION = r"(?:fiche)+|(?:relev)+|(?:topo)+|(?:ouvr)+|(?:\.doc)+|(?:\.dwg)+|(?:\.csv)+|(?:\.odt)+|(?:\.pdf)+|(?:\.doc)+" EXPRESSION = r"(?:fiche)+|(?:relev)+|(?:topo)+|(?:info)+|(?:ouvr)+|(?:\.doc)+|(?:\.dwg)+|(?:\.csv)+|(?:\.odt)+|(?:\.pdf)+|(?:\.doc)+"
# Masques Binaires représentants les controles de chaques catégorie traitées # Masques Binaires représentants les controles de chaques catégorie traitées
@ -231,7 +231,7 @@ def lire(dossier, fichier):
# formatter et écrire le nom # formatter et écrire le nom
ce_Fichier.nom = formater(nom_original) ce_Fichier.nom = formater(nom_original)
# déterminer son implication # déterminer son implication et sa categorie
ce_Fichier.implication, ce_Fichier.categorie = impliquer(fichier) ce_Fichier.implication, ce_Fichier.categorie = impliquer(fichier)
# calculer sa taille # calculer sa taille
ce_Fichier.taille = os.path.getsize(ce_Fichier.chemin) ce_Fichier.taille = os.path.getsize(ce_Fichier.chemin)
@ -252,7 +252,7 @@ class _Notification:
def __init__(self, def __init__(self,
categorie="Pas de catégorie", categorie="Pas de catégorie",
texte="Pas de texte"): texte="Pas de texte"):
self.categorie = categorie # vu comme une énumération de l'ensemble {"CSV", "DWG", "PDF", "FRONT"} seule ces valeurs sont donc possible et traité par le script self.categorie = categorie # vu comme une énumération de l'ensemble {"CSV", "DWG", "PDF", "FRONT"} seule ces valeurs sont donc possibles et traitées par le script
self.texte = texte self.texte = texte
@ -288,8 +288,7 @@ def controler(id_point, point_x, point_y):
# Lorsqu'un point de coordonnées (X, Y) se situe dans la plage 1100000, 1300000 pour X et # Lorsqu'un point de coordonnées (X, Y) se situe dans la plage 1100000, 1300000 pour X et
# la plage 3089000, 3311000 pour Y, alors ce point est à la fois valide en CC47 et en Lambert93 # la plage 3089000, 3311000 pour Y, alors ce point est à la fois valide en CC47 et en Lambert93
# Avant de déterminer la projection, je vais donc vérifier si le point à controler n'est pas dans cette plage # Avant de déterminer la projection, je vais donc vérifier si le point à controler n'est pas dans cette plage
# Et notifier si necessaire # Et avertir si necessaire
# TODO : exporter la notif. En fait non, pas d'export juste un avertissement dans le shell
if 6089000 < point_y < 6311000: if 6089000 < point_y < 6311000:
if 1100000 < point_x < 1300000: if 1100000 < point_x < 1300000:
print(f"AVERTISSEMENT : ID {ID_POINT} : SES COORDONNÉES PEUVENT ÊTRE INTERPRÉTER CORRECTEMENT COMME DU CC47 ET DU LAMBERT93.") print(f"AVERTISSEMENT : ID {ID_POINT} : SES COORDONNÉES PEUVENT ÊTRE INTERPRÉTER CORRECTEMENT COMME DU CC47 ET DU LAMBERT93.")
@ -307,7 +306,6 @@ def controler(id_point, point_x, point_y):
print(f" --> {projection:10}") print(f" --> {projection:10}")
# Controle la longitude # Controle la longitude
# TODO: refaire cette partie avec des variables et non des entiers literraux
longitude_correcte = False longitude_correcte = False
match projection: match projection:
case "Lambert93": case "Lambert93":
@ -332,13 +330,9 @@ def controler(id_point, point_x, point_y):
## Analyse lexicale ## Analyse lexicale
def tokeniser(ligne): #Pas utilisé, je le laisse au cas ou. def tokeniser(ligne): #Pas utilisé, je le laisse au cas ou.
""" """
Tokenise une ligne de texte. Tokenise une ligne de texte.
@ -416,8 +410,6 @@ def formater(chaine):
## Analyse semantique ## Analyse semantique
@ -450,18 +442,19 @@ def impliquer(chaine):
# mettre la chaine en minuscule # mettre la chaine en minuscule
chaine = chaine.casefold() chaine = chaine.casefold()
# cherche la PREMIERE correspondance du motif (alors que findall() cherche toutes les correspodances) # cherche la PREMIERE correspondance du motif (alors que findall() cherche toutes les correspondances)
motif = re.search(EXPRESSION, chaine) motif = re.search(EXPRESSION, chaine) # re.search() retourne un objet Match si trouvé sinon None
if motif: if motif:
print("Il y a un match") # print("Il y a un match")
motif = motif.group() motif = motif.group() # Match.group() permet de retourner la valeur matché
else: else:
print("Pas de match") # print("Pas de match")
pass
match motif : match motif :
# On va traiter les mots clés d'abord # On va traiter les mots clés d'abord
case "fiche" | "relev" | "topo" | "ouvr" | ".doc" | ".odt" : case "fiche" | "relev" | "topo" | "ouvr" | "info" | ".doc" | ".odt" :
return ("Necessaire", "TOPO") return ("Necessaire", "TOPO")
case ".dwg" : case ".dwg" :
return ("Necessaire", "DWG") return ("Necessaire", "DWG")
@ -475,6 +468,41 @@ def impliquer(chaine):
## Fonctions Input Utilisateur
def saisir_annee_topo():
"""
demande à l'utilisateur de saisir l'année du relevé topographique.
Demande à l'utilisateur l'année affichée dans le relevé topograhique
du projet.
Lit la valeur saisi dans l'entrée standard.
Les fichiers n'étant pas encore créés dans le dossier "Travail" à
ce moment du programme, l'utilisateur doit donc ouvrir la fiche
du relevé topographique dans le dossier original.
Pour ne pas être bloquant (cas ou l'année n'est pas trouvable),
la saisi de '0' permet de ne rien mettre.
Retourne la valeur saisi.
"""
print("\nVeuillez saisir l'année de la date du relevé topographique.")
print("Pour cela ouvrez la fiche topo du dossier/sous-dossier original.")
correct = False
while(not correct):
saisi = input("Saisir 4 chiffres : ")
if saisi in "0":
return ""
correct = saisi.isdecimal() and len(saisi) == 4
return saisi
@ -690,6 +718,21 @@ def preparer_dossier_travail(projet):
# peuplement du dossier Travail avec les fichiers necessaires # peuplement du dossier Travail avec les fichiers necessaires
print("Copie des fichiers nécessaires et nomenclature de leurs noms...") print("Copie des fichiers nécessaires et nomenclature de leurs noms...")
# demande l'annnée du revelé topo à l'utilisateur
annee_topo = saisir_annee_topo()
# nom du dossier projet
dossier = f"{os.path.basename(os.getcwd())}"
dossier = formater(dossier)
print(f"dossier : {dossier}")
# les suffixes ajoutés en cas de plusieurs fichiers dans la même catégorie
letterCSV = "\0" if projet.nb_csvs <= 1 else "A" # condition ternaire en python
letterDWG = "\0" if projet.nb_dwgs <= 1 else "A"
letterSHEMAS = "\0" if projet.nb_shemas <= 1 else "A"
letterTOPO = "\0" if projet.nb_releves <= 1 else "A"
for fichier in projet.fichiers: for fichier in projet.fichiers:
if fichier.implication in "Necessaire": if fichier.implication in "Necessaire":
source = fichier.chemin source = fichier.chemin
@ -702,19 +745,54 @@ def preparer_dossier_travail(projet):
# Confirmation par Audrey qu'il n'y a nécessairement qu'un fichier CSV par projet --> Le script s'occupe de le renommer. # Confirmation par Audrey qu'il n'y a nécessairement qu'un fichier CSV par projet --> Le script s'occupe de le renommer.
# Mais parfois il peut y avoir plusieurs DWGs dont un seul est utile --> PASS -- Je laisse l'opérateur choisir lequel utiliser et le renommer manuellement. # Mais parfois il peut y avoir plusieurs DWGs dont un seul est utile --> PASS -- Je laisse l'opérateur choisir lequel utiliser et le renommer manuellement.
#{"CSV", "DWG", "SHEMAS", "TOPO", "Non-definie"}
#ANCIENNE VERSION
#-----------------------------------------------------------------
# match fichier.categorie :
# case "CSV":
# dest = f"{dossierCSVs}\\Point_{fichier.nom}_IN"
# case "DWG":
# dest = f"{dossierDWGs}\\Plan_{fichier.nom}"
# case "SHEMAS":
# dest = f"{dossierPDFs}\\{fichier.nom}"
# case "TOPO":
# dest = f"{dossierTOPOs}\\{fichier.nom}"
# case _:
# pass # TODO: voir pour les autres cas, normalement il n'y en a pas pour le moment
#-----------------------------------------------------------------
#{"CSV", "DWG", "SHEMAS", "TOPO", "Non-definie"} #{"CSV", "DWG", "SHEMAS", "TOPO", "Non-definie"}
match fichier.categorie : match fichier.categorie :
case "CSV": case "CSV":
dest = f"{dossierCSVs}\\Point_{fichier.nom}_IN" if projet.nb_csvs <= 1 :
dest = f"{dossierCSVs}\\{dossier}_IN_Points_{annee_topo}"
else :
dest = f"{dossierCSVs}\\{dossier}_IN_Points_{annee_topo}_{letterCSV}"
letterCSV = chr(ord(letterCSV)+1) # on incremente la lettre
case "DWG": case "DWG":
dest = f"{dossierDWGs}\\Plan_{fichier.nom}" if projet.nb_dwgs <= 1 :
dest = f"{dossierDWGs}\\{dossier}_Plan_{annee_topo}"
else :
dest = f"{dossierDWGs}\\{dossier}_IN_Points_{annee_topo}_{letterDWG}"
letterDWG = chr(ord(letterDWG)+1)
case "SHEMAS": case "SHEMAS":
dest = f"{dossierPDFs}\\{fichier.nom}" if projet.nb_shemas <= 1 :
dest = f"{dossierPDFs}\\{dossier}_Plan_{annee_topo}"
else :
dest = f"{dossierPDFs}\\{dossier}_IN_Points_{annee_topo}_{letterSHEMAS}"
letterSHEMAS = chr(ord(letterSHEMAS)+1)
case "TOPO": case "TOPO":
dest = f"{dossierTOPOs}\\{fichier.nom}" if projet.nb_releves <= 1 :
dest = f"{dossierTOPOs}\\{dossier}_Infos_{annee_topo}"
else :
dest = f"{dossierTOPOs}\\{dossier}_IN_Points_{annee_topo}_{letterTOPO}"
letterTOPO = chr(ord(letterTOPO)+1)
case _: case _:
pass # TODO: voir pour les autres cas, normalement il n'y en a pas pour le moment pass # TODO: voir pour les autres cas, normalement il n'y en a pas pour le moment
# renomme le fichier en conséquence
fichier.nom = os.path.basename(dest)
dest = dest + f".{fichier.extension}" dest = dest + f".{fichier.extension}"
print(f"dest : {dest}") print(f"dest : {dest}")
@ -740,13 +818,13 @@ def controler_longueur_noms(projet):
for fichier in projet.fichiers: for fichier in projet.fichiers:
match fichier.extension: match fichier.extension:
case "dwg": case "dwg":
if len(fichier.nom) > 25 : # Plan_ + 25 carac --> 30 carac if len(fichier.nom) > 38 : # 40 - 2 si lettres de suffixe "_A", "_B", etc
projet.notifier("DWG", f'"Plan_{fichier.nom}.dwg" : Nom trop long.') projet.notifier("DWG", f'"{fichier.nom}.dwg" : Nom trop long.')
case "csv" : case "csv" :
if len(fichier.nom) > 35 : # Point_ + 35 carac + _OUT --> 45 carac if len(fichier.nom) > 43 :
projet.notifier("CSV", f'"Point_{fichier.nom}_IN.csv" : Nom trop long.') projet.notifier("CSV", f'"{fichier.nom}.csv" : Nom trop long.')
case "pdf" : case "pdf" :
if len(fichier.nom) > 45 : if len(fichier.nom) > 43 :
projet.notifier("PDF", f'"{fichier.nom}.pdf" : Nom trop long.') projet.notifier("PDF", f'"{fichier.nom}.pdf" : Nom trop long.')
case _: case _:
if len(fichier.nom) > 45 : if len(fichier.nom) > 45 :
@ -1108,15 +1186,13 @@ def purger_et_finir_programme(projet):
## NOTE : Pour traiter le cas des sous-dossiers lors du dézippage ## NOTE : Pour traiter le cas des sous-dossiers lors du dézippage
# il vaut mieux que l'opérateur copie le fichier banbou.py dans chaque # il vaut mieux que l'opérateur copie le fichier banbou.py dans chaque
# sous-dossiers puis l'execute. # sous-dossiers puis l'execute.
## Donc : ## Donc Il est attendu que :
# - Il est attendu que le fichier script se situe dans le dossier, au # - le fichier script se situe DANS le dossier contenant que
# même niveau que les fichiers originaux. # les fichiers originaux.
# - Il est attendu que le modele du visa soit dans un autre dossier # - le modele du visa soit dans un autre dossier BIEN SPÉCIFIÉ
# BIEN SPÉCIFIÉ (je vais remettre un chemin absolue "Desktop\Banbou" car suivant l'opérateur, le dézippage crée des sous-sous-dossiers) # (je vais remettre un chemin absolue en dur "Desktop\Banbou"
# De plus, pour simplifier le dossier Travail se créera, dans le dossier # car suivant l'opérateur, le dézippage crée des sous-sous-dossiers,
# ou se situe le script, aux coté des fichiers originaux # et serait problématique pour un chemin relatif)
##TODO: Automatiser le cas des sous dossiers, avec un FOR each dossier FAIRE
## Et créer des dossiers Travail01, Travail02, etc
@ -1136,6 +1212,7 @@ if len(ce_Projet.nom) >=46 :
# explorer le dossier Trouvé et fabriquer la liste des fichiers nécessaires # explorer le dossier Trouvé et fabriquer la liste des fichiers nécessaires
ce_Projet.lister_fichiers(ce_Projet.racine) ce_Projet.lister_fichiers(ce_Projet.racine)
# calcule la taille des fichiers nécessaires
calculer_taille(ce_Projet) calculer_taille(ce_Projet)
# Ici le projet doit avoir toutes les données nécessaires pour fabriquer # Ici le projet doit avoir toutes les données nécessaires pour fabriquer
@ -1143,21 +1220,22 @@ calculer_taille(ce_Projet)
preparer_dossier_travail(ce_Projet) preparer_dossier_travail(ce_Projet)
# il faut UN SEUL fichier CSV NOTE: a voir si ce controle est obligatoire # Règle nouvellement ajouté : il faut normalement UN SEUL fichier CSV,
# l'opérateur ne devrait plus fusionner manuellement des fichiers CSV.
if ce_Projet.nb_csvs > 1: if ce_Projet.nb_csvs > 1:
print("\nAVERTISSEMENT : IL Y A PLUSIEURS FICHIERS CSV.") print("\nAVERTISSEMENT : IL Y A PLUSIEURS FICHIERS CSV.")
print("VEUILLEZ NE GARDER QU'UN SEUL FICHIER CSV DANS LE DOSSIER ORIGINAL.") print("VEUILLEZ NE GARDER QU'UN SEUL FICHIER CSV DANS LE DOSSIER ORIGINAL.")
purger_et_finir_programme(ce_Projet) purger_et_finir_programme(ce_Projet)
# On formatte le fichier CSV pour ArcGIS
# On formatte le/les fichier/s CSV pour ArcGIS
for fichier in ce_Projet.fichiers: for fichier in ce_Projet.fichiers:
if fichier.categorie in "CSV": if fichier.categorie in "CSV":
entree = ce_Projet.racine + "\\Travail\\Point_" + fichier.nom + "_IN" + ".csv" entree = ce_Projet.racine + "\\Travail\\" + fichier.nom + ".csv"
sortie = ce_Projet.racine + "\\Travail\\Point_" + fichier.nom + "_IN" + ".csv" sortie = ce_Projet.racine + "\\Travail\\" + fichier.nom + ".csv"
formater_vers_ArcGIS(ce_Projet, entree, sortie) formater_vers_ArcGIS(ce_Projet, entree, sortie)
# On controle les noms des fichiers copiés # On controle les noms des fichiers copiés
controler_longueur_noms(ce_Projet) controler_longueur_noms(ce_Projet)

View File

@ -8,7 +8,7 @@ recollement de fibre optiques.
Lorsque vous débutez sur Banbou, la première fois. Lorsque vous débutez sur Banbou, la première fois.
1. Sur le bureau, créez un dossier 📁 nommé *Banbou* (B majuscule). 1. **Sur le bureau**, créez un dossier 📁 nommé *Banbou* (B majuscule).
2. Copiez/collez 📋 dans celui ci, les fichiers : 2. Copiez/collez 📋 dans celui ci, les fichiers :
- `NEOBANBOU.py` 📄 - `NEOBANBOU.py` 📄
- `VISA_BANBOU.xlsx` 📄 - `VISA_BANBOU.xlsx` 📄
@ -32,7 +32,7 @@ Vous pouvez fermer les fenêtres apparues une fois le script terminé. Bien jou
Après avoir lu et compris le contenu de l'ancien script, et avoir apporté quelques modifications, j'ai plutot décidé de repartir de zéro. Après avoir lu et compris le contenu de l'ancien script, et avoir apporté quelques modifications, j'ai plutot décidé de repartir de zéro.
Ma première version était divisé en plusieurs modules et fichiers. Ma première version était divisé en plusieurs modules et fichiers.
Mais pour une question d'ergonomie dans l'utilisation, j'ai tout regroupé sur un seul fichier. Mais pour une question d'ergonomie d'utilisation pour l'opérateur, j'ai tout regroupé sur un seul fichier (comme l'ancien script).
### 🧪 Tests unitaires 🧪 ### 🧪 Tests unitaires 🧪
@ -67,31 +67,35 @@ Voici un explicatif du déroulé du programme :
#### 3. Classes et fonctions #### 3. Classes et fonctions
Il s'agit du regroupement des modules que j'avais externalisés dans l'ancienne version : Il s'agit du regroupement des modules que j'avais externalisés dans l'ancienne version.
Lignes :
- lignes 172-245 : Une représentation d'un fichier. - 172-245 : Représentation d'un fichier.
- lignes 251-256 : Une représentation d'une notification. - 251-256 : Représentation d'une notification.
- lignes 266-328 : Une fonction importante qui controle la validité des coordonnées d'un point géographique. - 266-328 : Fonction importante qui controle la validité des coordonnées d'un point géographique.
- lignes 337-472 : Des fonctions *Input/Output* sur des chaines de caractères (ex: formater un nom suivant une nomenclature donné, etc) - 333-502 : Fonctions E/S sur des chaines de caractères (ex: formater un nom suivant une nomenclature donné, etc)
- lignes 482-906 : Une représentation du projet. - 511-595 : Représentation du projet.
- lignes 916-1081 : Une fonction qui génère le Visa de controle. - 608-663 : Fonctions pour obtenir des elements d'environnement pour le projet : date, nom du dossier, etc.
- 666-808 : Fonction importante qui crée les dossiers nécessaires et copie les fichiers de travail.
- 811-831 : Fonction qui controle la longueur des noms de fichier.
- 833-985 : Fonctions importantes qui formate le fichier CSV.
- 995-1060 : Fonction qui génère le Visa de controle.
#### 4. Exécution du programme #### 4. Exécution du programme
>Note : Le programme principal parait donc très court car il va ensuite faire appel à toutes les autres fonctions vu plus haut. >Note : Le programme principal parait donc très court car il va ensuite faire appel à toutes les autres fonctions vu plus haut.
- lignes 1123-1134 : Le programme commence par créer un Projet et définie des valeurs utiles (date, chemin, etc) - 1200-1210 : Le programme commence par créer un Projet et définie des valeurs utiles (date, chemin, etc)
- ligne 1136 : Le programme parcourt le dossier où se trouve le script et analyse tous les fichiers trouvés (quel est son nom, son extension, est-il est utile pour le projet ? ) et stocke toutes ces infos dans une liste pour être utiliser plus tard. - 1213 : Le programme parcourt le dossier où se trouve le script et analyse tous les fichiers trouvés (quel est son nom, son extension, est-il est utile pour le projet ? ) et stocke toutes ces infos dans une liste pour être utiliser plus tard.
- ligne 1138 : calcule la taille **seulement** des fichiers qui vont être copiés dans le dossier de Travail. - 1216 : calcule la taille **seulement** des fichiers qui vont être copiés dans le dossier de Travail.
- ligne 1142 : reprend la liste créée et décide des dossiers à créer puis y copie les fichiers attendus. Ajoute les préfixes et suffixes aux noms de fichiers si nécessaire. - 1220 : reprend la liste créée et décide des dossiers à créer puis y copie les fichiers attendus.
- lignes 1151-1156 : le fichier CSV contenant les points est lu. Chaque ligne, contenant une coordonnée, est : - 1232-1236 : le fichier CSV contenant les points est lu. Chaque ligne, contenant une coordonnée, est :
- analysée, on vérifie de sa projection. - analysée, on vérifie de sa projection.
- formatée, on corrige certains défauts de syntaxe. Les lignes vides, etc. - formatée, on corrige certains défauts de syntaxe. Les lignes vides, etc.
- ajoutée à une liste. Cette liste est ensuite écrite dans un fichier. - ajoutée à une liste. Cette liste est ensuite écrite dans un fichier.
- ligne 1161 : Le programme controle la longueur de tous les noms de fichiers. - ligne 1240 : Le programme controle la longueur de tous les noms de fichiers.
- ligne 1166 : finalement le programme crée un visa à partir d'un modèle, et ajoute toutes les informations nécessaires, ainsi que toutes les notifications - ligne 1245 : finalement le programme crée un visa à partir d'un modèle, et ajoute toutes les informations nécessaires, ainsi que toutes les notifications sur des points de controles qui ne seraient pas passés.
sur des points de controles qui ne seraient pas passés.
### 🛂🚧 Les points de controles réalisés 🚧🛂 ### 🛂🚧 Les points de controles réalisés 🚧🛂