Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ajout d'une procédure pour l'import automatique de lames dans Geotrek-admin #4317

Open
bruhnild opened this issue Sep 24, 2024 · 0 comments

Comments

@bruhnild
Copy link
Contributor

bruhnild commented Sep 24, 2024

Description :

Actuellement, il n'existe pas de procédure permettant l'import automatique de lames dans Geotrek-admin. La procédure décrite dans la documentation officielle (documentation import) ne couvre que l'import des poteaux, mais ne permet pas l'import des lames (blades).

Le seul moyen d'importer des lames est de réaliser un script spécifique. Ce processus devrait être amélioré pour permettre un import automatique, au même titre que celui des poteaux.

Côté Makina Corpus nous avons déjà eu l'occasion de réaliser des scripts d'import pour ces données. Cela pourrait être un point de départ pour un territoire qui souhaiterai importer des données manuellement en attendant une commande d'import packagée directement avec Geotrek.

Le code suivant doit être inséré dans un fichier python pour ensuite être lancé. Une fois lancé il cherche un fichier csv contenant les lames, fait l'import et créé un second fichier avec les lames correctement importées (permettant ainsi de comparer les deux fichiers csv pour savoir quelle lame a été importée ou non) :

from ctypes import sizeof
from dataclasses import replace
from gettext import textdomain
import json
from django.contrib.gis.geos import Point
from geotrek.authent.models import Structure
from geotrek.core.models import Topology
from geotrek.signage.models import Blade, BladeType, Color, Direction, Line, Sealing, Signage, SignageType, LinePictogram
import csv
from psycopg2.errors import UniqueViolation
from django.db.utils import IntegrityError


##################################
structure = Structure.objects.get(name='Default')  # < Remplacer ici par la nom de la structure de rattachement des lames
provider = 'ORIGINE DES DONNEES'   # < A modifier
f = open('/opt/geotrek-admin/var/conf/lames.csv', 'r')   # < Chemin vers le fichier importé
f_out = open('/opt/geotrek-admin/var/conf/lames_importees.csv', 'w')  # < Chemin vers le second fichier créé
###############################

color_mapping = {
    'V': 'Vert',
    'J': 'Jaune',
    'G': 'Gris',
    'M': 'Marron',
    'B': 'Blanc',
    'GB': 'Gris',
    'Bois': 'Marron'
}


def create_or_update_line(blade, number, text, picto, alt):
    if number or text or picto or alt:
        lines = Line.objects.filter(blade=blade, number=number)
        line = None
        if lines.exists():
            if lines.count() == 1:
                line = lines.first()
                line.text = text
                line.pictogram = picto
                line.save()
        else:
            line, _ = Line.objects.get_or_create(
                blade=blade,
                number=number,
                text=text,
            )
            if picto and picto != "":
                line_pictogram, _ = LinePictogram.objects.get_or_create(label=picto)
                line.pictograms.add(line_pictogram)
        if alt:
            line.distance = float(alt)
            line.save()


def create_bl_blade(signa, color_str):
    blade_type, _ =BladeType.objects.get_or_create(label='Bague de lieudit (BL)')

    blade, created = Blade.objects.get_or_create(
        topology=signa.topo_object,
        signage=signa,
        number="-BL",
        type=blade_type,
    )
    if created:
        handled_blade, handled_pk = "created", blade.pk
    else:
        handled_blade, handled_pk = "updated", blade.pk
    blade.color = Color.objects.get(label=color_mapping[color_str])
    blade.save()
    create_or_update_line(blade, 1, text=row[3], picto="", alt="")
    return handled_blade, handled_pk


def update_blade(signa, blade_code, row, blade=None):
    type_str = row[1]
    color_str = row[2]
    direction_str = row[6]
    handled_blade = False
    type_mapping = {
        'BL': 'Bague de lieudit (BL)',
        'LD1': 'Lame LD1',
        'LD2': 'Lame LD2',
        'LD3': 'Lame LD3',
        'LDS1': 'Lame LD1',
        'LDS2': 'Lame LD2',
        'LDS3': 'Lame LD3',
        'LT1': 'Lame LT1',
        'LT2': 'Lame LT2',
        'LT3': 'Lame LT3',
        'BLR': 'Bague de lieudit réduite (BLr)',
        'LDR1': 'Lame LD1 réduite',
        'LDR2': 'Lame LD2 réduite',
        'LDR3': 'Lame LD3 réduite',
        'LTR1': 'Lame LT1 réduite',
        'LTR2': 'Lame LT2 réduite',
        'LTR3': 'Lame LT3 réduite',
        'PB-PR': 'Plaque Balisage PR (PB-PR)',
        'PB-GR': 'Plaque Balisage - GR (PB-GR)',
        'H22': 'Signalétique routière',
        'H23': 'Signalétique routière',
        'TR': 'Triangle Réglementaire (TR)',
        'PAR': "Plaque d'Avertissement (PLA)",
        'PLR': "Plaque Réglementaire (PLR)",
    }
    if type_str == 'PPI' or type_str == 'BL' and row[5].strip(" ").strip("m"):
        if row[5]:
            signa.printed_elevation = int(row[5].strip(" ").strip("m"))
            signa.save()
        handled_blade, handled_pk = create_bl_blade(signa, color_str=row[2])
        return (handled_blade, handled_pk)
    else:
        if type_str == 'PB':
            replace_with = row[3].split('-')
            if 'Changement parcours' in replace_with:
                type_str = "PB-PR"
            else:
                type_str = f"{replace_with[0]}-{replace_with[1]}"
        elif type_str == 'JB' or not type_str:
            return ("No type - skipped", 0)

        try:
            type_obj = BladeType.objects.get(label=type_mapping[type_str])
        except:
            return ("No type - skipped", 0)
        color = None
        if color_str:
            color = Color.objects.get(label=color_mapping[color_str])
        direction = None
        if direction_str:
            if direction_str == "CO" or direction_str == "H":
                direction_str = "Continuité"
            direction = Direction.objects.get(label=direction_str)
        if not blade:
            blade = Blade.objects.create(
                topology=signa.topo_object,
                signage=signa,
                number=blade_code,
                type=type_obj,
                color=color,
                direction=direction
            )
            # print(f"Blade {blade.pk} created")
            handled_blade, handled_pk = "created", blade.pk
        else:
            blade.type = type_obj
            blade.color = color
            blade.direction = direction
            blade.save()
            handled_blade, handled_pk = "updated", blade.pk
        row[5] = row[5].replace(',', '.')
        create_or_update_line(blade, 1, text=row[3], picto=row[4], alt=row[5].strip(" ").strip("m"))
        if row[7]:
            row[9] = row[9].replace(',', '.')
            create_or_update_line(blade, 2, text=row[7], picto=row[8], alt=row[9].strip(" ").strip("m"))
        if row[10]:
            row[12] = row[12].replace(',', '.')
            create_or_update_line(blade, 3, text=row[10], picto=row[11], alt=row[12].strip(" ").strip("m"))
        return handled_blade, handled_pk


csvreader = csv.reader(f)
output_writer = csv.writer(f_out, delimiter=',', quotechar=',', quoting=csv.QUOTE_MINIMAL)
i = 0
dw = None
fail = False
handled_blade = False
handled_pk = 0
for row in csvreader:
    if i < 2:
        pass

    # Code	Type	Coul	Texte Ligne 1	Picto	Alt/Dist	Sens	Texte Ligne 2
    # 0     1       2       3               4       5           6           7       
    # Picto	Alt/Dist	Texte Ligne 3	Picto	Alt/Dist
    # 8     9                10           11        12 
    else:
        handled_blade = False
        handled_pk = 0
        # print(row)
        try:
            signa_code = None
            blade_code = None
            status_str = row[1]
            code_str = row[0]
            slashes_count = code_str.count("/")
            # print(f"{code_str=}")

            if slashes_count == 2:
                signa_code = f"{code_str.split('/')[0]}/{code_str.split('/')[1]}"
                blade_code = f"{code_str.split('/')[2]}"
            elif slashes_count == 1:
                signa_code = code_str
            else:
                continue
            signa = Signage.objects.filter(deleted=False,
             code=signa_code)
            if signa.count() == 0:
                signa = Signage.objects.filter(deleted=False, code=signa_code.replace('/', '_'))
            if signa.count() == 0:
                row.append("No signage")
                continue
            if signa.count() > 1:
                row.append("Several signages")
                continue
            else:
                signa = signa.first()
            if blade_code:
                if blade_code == "BL":
                    blade_code = "-BL"
                blade = Blade.objects.filter(deleted=False, number=blade_code, signage=signa)
                if blade.count() == 0:
                    handled_blade, handled_pk = update_blade(signa, blade_code, row)
                elif blade.count() > 1:
                    continue
                elif blade.count() == 1:
                    blade = blade.first()
                    handled_blade, handled_pk = update_blade(signa, blade_code, row, blade)
        except UniqueViolation as e:
            row.append(str(e).replace(',', '').replace('\n', ' '))
        except IntegrityError as e:
            row.append(str(e).replace(',', '').replace('\n', ' '))
        if handled_blade and handled_pk:
            while len(row) < 12:
                row.append('')
            row.append(handled_blade)
            row.append(handled_pk)
        output_writer.writerow(row)
    i += 1

f.close()

Ce fichier doit ensuite être lancé dans une console Geotrek via la commande suivante : sudo geotrek shell < mon_script.py

Le fichier csv qui sert pour l'import doit être formaté de la manière suivante :

Code,Type,Coul,Texte Ligne 1,Picto,Alt/Dist,Sens,Texte Ligne 2,Picto,Alt/Dist,Texte Ligne 3,Picto,Alt/Dist,Charte SEN,Quantité
07100/01/A,BL,V,Les Vernèdes,,873,,,,,,,,Charte CD30,1
30153/02/A,BL,V,Malôns et Elze,,860,,,,,,,,Charte CD30,2
30153/03/A,BL,V,Le Frontal,,596,,,,,,,,Charte CD30,3

Attention il faut pour que cela fonctionne que le code de la lame référence le code du poteau signalétique correspondant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant