quickinteg 2.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,646 @@
1
+ #! python3 # noqa: E265
2
+
3
+ ################################################################################
4
+ # This file is part of quickinteg.
5
+
6
+ # quickinteg is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # quickinteg is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with quickinteg. If not, see <https://www.gnu.org/licenses/>.
18
+ ################################################################################
19
+
20
+ # ############################################################################
21
+ # ########## Libraries #############
22
+ # ##################################
23
+
24
+ # standard library
25
+ import logging
26
+ import os
27
+ import sqlite3
28
+ import string
29
+ import subprocess
30
+ import time
31
+ import unicodedata
32
+ from pathlib import Path
33
+
34
+ from . import processing_logging
35
+
36
+ # 3rd party - try embedded first (for QGIS), fallback to normal import (for CLI)
37
+ try:
38
+ from .external import xlsxwriter
39
+ except ImportError:
40
+ pass
41
+
42
+ # ############################################################################
43
+ # ########## Globals ###############
44
+ # ##################################
45
+
46
+ module_logger = logging.getLogger(__name__)
47
+ module_logger.setLevel(logging.DEBUG)
48
+
49
+
50
+ # ############################################################################
51
+ # ########## Functions #############
52
+ # ##################################
53
+
54
+
55
+ def log(message, logLevel="info"):
56
+ processing_logging.log(str(message), module_logger, logLevel)
57
+
58
+
59
+ def spatialite_connect(*args, **kwargs):
60
+ """returns a dbapi2.Connection to a SpatiaLite db
61
+ using the "mod_spatialite" extension (python3)
62
+
63
+ WARNING !! it messes up subprocesses on MacOS. Use sqlite3.connect when possible"""
64
+ import sqlite3
65
+
66
+ con = sqlite3.connect(*args, **kwargs)
67
+ con.enable_load_extension(True)
68
+ cur = con.cursor()
69
+ libs = [
70
+ # SpatiaLite >= 4.2 and Sqlite >= 3.7.17, should work on all platforms
71
+ ("mod_spatialite", "sqlite3_modspatialite_init"),
72
+ # SpatiaLite >= 4.2 and Sqlite < 3.7.17 (Travis)
73
+ ("mod_spatialite.so", "sqlite3_modspatialite_init"),
74
+ # SpatiaLite < 4.2 (linux)
75
+ ("libspatialite.so", "sqlite3_extension_init"),
76
+ ]
77
+ found = False
78
+ for lib, entry_point in libs:
79
+ try:
80
+ cur.execute(f"select load_extension('{lib}', '{entry_point}')")
81
+ except sqlite3.OperationalError as err:
82
+ if __debug__:
83
+ log(err, "debug")
84
+ continue
85
+ else:
86
+ found = True
87
+ break
88
+ if not found:
89
+ raise RuntimeError("Cannot find any suitable spatialite module")
90
+ cur.close()
91
+ con.enable_load_extension(False)
92
+ return con
93
+
94
+
95
+ def create_spatialite_db(file_name):
96
+ if Path(file_name).exists():
97
+ raise ValueError(f"La base de donnée {file_name} existe déjà")
98
+ else:
99
+ # Trick to create an empty spatialite db with ogr2ogr
100
+ cmd = f'echo \'{{"type": "FeatureCollection","features": []}}\' | ogr2ogr -f SQLite -dsco SPATIALITE=YES "{file_name}" /vsistdin/ -nln _qi_dummy'
101
+
102
+ # Run the process and wait for it to complete
103
+ result = subprocess.run(
104
+ cmd,
105
+ stdout=subprocess.PIPE,
106
+ stderr=subprocess.PIPE,
107
+ encoding="utf-8",
108
+ shell=True,
109
+ )
110
+
111
+
112
+ def table_exists(db_file, tbl_name):
113
+ """
114
+ Return true if table "tbl_name" exists in base "db_file"
115
+ """
116
+ with sqlite3.connect(db_file) as con:
117
+ # Use a normal try/finally block to ensure the cursor is closed.
118
+ try:
119
+ cur = con.cursor()
120
+ # Use parameterized query to prevent SQL injection
121
+ req = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?;"
122
+ cur.execute(req, (tbl_name,))
123
+ res = cur.fetchone()[0]
124
+ finally:
125
+ # Ensure the cursor is closed after use
126
+ cur.close()
127
+
128
+ # The connection will be closed automatically due to the 'with' statement.
129
+ return res != 0
130
+
131
+
132
+ def column_exists(db_file, tbl_name, column_name):
133
+ """
134
+ Return true if column "column_name" exists in table "tbl_name"
135
+ """
136
+ if table_exists(db_file, tbl_name):
137
+ with sqlite3.connect(db_file) as con:
138
+ cur = con.cursor()
139
+ req_search_for_column = (
140
+ f"PRAGMA table_info('{tbl_name}');" # Use parameter substitution for safety
141
+ )
142
+ cur.execute(req_search_for_column)
143
+ results = cur.fetchall()
144
+ columns = [row[1] for row in results] # second column of PRAGMA INFO is column name
145
+ return column_name in columns
146
+
147
+ return False # If the table does not exist, return False
148
+
149
+
150
+ def add_column(db_file, tbl_name, column_name):
151
+ if table_exists(db_file, tbl_name) and not column_exists(db_file, tbl_name, column_name):
152
+ log(f"Ajout de la colonne {column_name} à la table {tbl_name}", "debug")
153
+
154
+ with sqlite3.connect(db_file) as con:
155
+ cur = con.cursor()
156
+
157
+ # Execute the ALTER TABLE statement to add the column
158
+ req = f"ALTER TABLE {tbl_name} ADD COLUMN {column_name}"
159
+ cur.execute(req)
160
+
161
+
162
+ def get_srs(db_file, tbl_name):
163
+ """
164
+ Return CRS of the table
165
+ """
166
+ with sqlite3.connect(db_file) as con:
167
+ cur = con.cursor()
168
+ log(f"Vérification du SRS de la table {tbl_name} dans la base", "debug")
169
+ req = f"SELECT srid FROM geometry_columns WHERE f_table_name = '{tbl_name}';"
170
+ cur.execute(req)
171
+ out_sql = cur.fetchone()
172
+
173
+ if out_sql is None:
174
+ log("La table n'a pas de géometrie dans spatialite.", "debug")
175
+ return None
176
+ else:
177
+ srid = out_sql[0]
178
+ return f"EPSG:{srid}"
179
+
180
+
181
+ def geom_type(db_file, tbl_name):
182
+ """
183
+ Return geom type in text. Returns None in no geometry
184
+ Spatialites geoms :
185
+ 1 = POINT
186
+ 2 = LINESTRING
187
+ 3 = POLYGON
188
+ 4 = MULTIPOINT
189
+ 5 = MULTILINESTRING
190
+ 6 = MULTIPOLYGON
191
+ 7 = GEOMETRYCOLLECTION
192
+ """
193
+ with sqlite3.connect(db_file) as con: # Use context manager for the connection
194
+ cur = con.cursor() # Cursor managed by connection's context manager
195
+ log(f"Vérification de la géometrie de la table {tbl_name} dans la base", "debug")
196
+
197
+ # Using string formatting here because SQLite does not support parameter
198
+ # substitution for table names. Ensure that 'tbl_name' is safe to include.
199
+ req = f"SELECT geometry_type FROM geometry_columns WHERE f_table_name = '{tbl_name}';"
200
+ cur.execute(req)
201
+ out_sql = cur.fetchone()
202
+
203
+ if out_sql is None:
204
+ log("La table n'a pas de géometrie dans spatialite.", "debug")
205
+ return None
206
+ else:
207
+ dict_geoms = {
208
+ 1: "POINT",
209
+ 2: "LINESTRING",
210
+ 3: "POLYGON",
211
+ 4: "MULTIPOINT",
212
+ 5: "MULTILINESTRING",
213
+ 6: "MULTIPOLYGON",
214
+ 7: "GEOMETRYCOLLECTION",
215
+ }
216
+ geom_num = out_sql[0]
217
+ try:
218
+ return dict_geoms[geom_num]
219
+ except IndexError:
220
+ raise ValueError("Undefined geometry (%s)" % (geom_num))
221
+
222
+
223
+ def import_file_to_spl(ogr_target, shp_csv_file, tbl_name, options="", prefix=None):
224
+ """
225
+ Import un fichier shp ou csv vers une base spatialite avec ogr2ogr.
226
+ Le nom de la table sera le nom du fichier en minuscule sans l'extension
227
+
228
+ option : ajouter des options à la commande ogr2ogr
229
+ (par exemple -skipfailures)
230
+ prefix : si différent de '', ajoutera une colonne "import_prefix" à la
231
+ table. Chaque enregistrement prendra la valeur donnée en paramètre.
232
+ """
233
+ log(
234
+ "# Import du fichier %s dans la table %s de la bdd %s"
235
+ % (shp_csv_file, tbl_name, ogr_target)
236
+ )
237
+
238
+ file_type = os.path.splitext(os.path.basename(shp_csv_file))[1]
239
+ if file_type not in (".csv", ".shp"):
240
+ raise ValueError("Le type de fichier %s n'est pas supporté" % (file_type))
241
+
242
+ # Construction de la commande ogr2ogr
243
+ # On commence par les options passées en paramètre
244
+ ogr_options = options
245
+
246
+ if file_type == ".shp":
247
+ # ajout d'une option OGR
248
+ ogr_options += " -dim XY"
249
+ if not table_exists(ogr_target, tbl_name):
250
+ log(
251
+ "La table " + tbl_name + " n'existe pas. Elle sera créée par org2ogr",
252
+ logLevel="debug",
253
+ )
254
+ # On ajoute l'option pour une geometrie multi et on donne le nom "geom" pour la
255
+ # colonne de géometrie (sinon nom arbitraire OGR moche)
256
+ # ajout d'une option OGR
257
+ ogr_options += " -nlt PROMOTE_TO_MULTI -lco GEOMETRY_NAME=geom"
258
+ # Sinon la table existe, on vérifie sa geometry pour les parametrès d'import OGR
259
+ else:
260
+ log(
261
+ "La table %s existe déjà. Les données seront ajoutées" % (tbl_name),
262
+ logLevel="debug",
263
+ )
264
+ geom_type_ = geom_type(ogr_target, tbl_name)
265
+ if geom_type_ in ("POINT", "LINESTRING", "POLYGON"):
266
+ pass
267
+ elif geom_type_ in ("MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON"):
268
+ # ajout d'une option OGR
269
+ log(
270
+ "La table de destination à une géometrie multi. "
271
+ "Ajout du paramètre PROMOTE_TO_MULTI",
272
+ logLevel="debug",
273
+ )
274
+ ogr_options += " -nlt PROMOTE_TO_MULTI"
275
+ ogr_options += ' -t_srs "%s"' % (get_srs(ogr_target, tbl_name))
276
+ # Other option is :
277
+ # ogr_options += ' -nlt %s' %(geom_type_)
278
+ # but this option will create invalid geoms without warning
279
+ elif file_type == ".csv":
280
+ # ajout d'une option OGR
281
+ ogr_options += " -oo EMPTY_STRING_AS_NULL=YES"
282
+
283
+ if prefix is not None and prefix != "":
284
+ if table_exists(ogr_target, tbl_name) and not column_exists(
285
+ ogr_target, tbl_name, "import_prefix"
286
+ ):
287
+ add_column(ogr_target, tbl_name, "import_prefix")
288
+
289
+ # S'il y a un préfixe on ajoute une option -sql pour ajouter une colonne
290
+ shp_name_without_extension = os.path.splitext(os.path.basename(shp_csv_file))[0]
291
+ # ajout d'une option OGR
292
+ ogr_options += " -sql \"SELECT *, '%s' AS import_prefix FROM %s\"" % (
293
+ prefix,
294
+ shp_name_without_extension,
295
+ )
296
+ # Avec l'ajout de cette option, la requête plante si le nom du shp contient des charactères spéciaux
297
+ # On gère alors le cas proprement avec une exception
298
+ # Liste des charactères interdits (underscore _ autorisé)
299
+ invalidChars = set(string.punctuation.replace("_", ""))
300
+ if any(char in invalidChars for char in shp_name_without_extension):
301
+ raise ValueError(
302
+ "Impossible d'ajouter un préfixe d'import si les "
303
+ "noms de shp contiennent des caractères spéciaux. "
304
+ "Essayer en renommant les shapes à importer."
305
+ )
306
+ # On assemble la commande d'appel à OGR2OGR
307
+ cmd = f'ogr2ogr -f sqlite "{ogr_target}" "{shp_csv_file}" -nln {tbl_name} -append {ogr_options}'
308
+ cmd_skipfailures = f'ogr2ogr -f sqlite "{ogr_target}" "{shp_csv_file}" -nln {tbl_name} -append {ogr_options} -skipfailures'
309
+ log("OGR2OGR : %s" % (cmd), logLevel="debug")
310
+ # Si Erreur ogr, on ressaie avec skipfailures
311
+ # os.system(f'{cmd} || {{ echo "toto"; {cmd_skipfailures}; }}')
312
+
313
+ # Run the process and wait for it to complete
314
+ result = subprocess.run(
315
+ cmd,
316
+ stdout=subprocess.PIPE,
317
+ stderr=subprocess.PIPE,
318
+ encoding="utf-8",
319
+ shell=True,
320
+ )
321
+ # Access the stdout and stderr
322
+ stdout_output = result.stdout
323
+ stderr_output = result.stderr
324
+
325
+ # Get the exit status
326
+ exit_status = result.returncode
327
+ if stderr_output:
328
+ log(stderr_output, logLevel="warning")
329
+ if exit_status != 0:
330
+ log(
331
+ "L'import ogr2ogr a échoué. Nouvelle tentative avec -skipfailures. Une partie des donnée sera ignorée.",
332
+ logLevel="warning",
333
+ )
334
+ result = subprocess.run(
335
+ cmd_skipfailures,
336
+ stdout=subprocess.PIPE,
337
+ stderr=subprocess.PIPE,
338
+ encoding="utf-8",
339
+ shell=True,
340
+ )
341
+
342
+
343
+ def import_folder_to_spl(
344
+ folder,
345
+ ogr_target=None,
346
+ prefix=None,
347
+ include_csv=False,
348
+ ignore_grace_list=True,
349
+ recursive=False,
350
+ ):
351
+ if recursive:
352
+ shp_glob_pattern = "**/*.shp"
353
+ csv_glob_pattern = "**/*.csv"
354
+ else:
355
+ shp_glob_pattern = "*.shp"
356
+ csv_glob_pattern = "*.csv"
357
+
358
+ if ogr_target is None:
359
+ log("Aucune base de donnée renseignée. Création d'une nouvelle BDD")
360
+ ogr_target_ = str(Path(folder) / "base.sqlite")
361
+ create_spatialite_db(ogr_target_)
362
+ elif Path(ogr_target).exists():
363
+ # log("Création d'une sauvegarde de la BDD")
364
+ # copyfile(ogr_target, ogr_target + "_backup")
365
+ ogr_target_ = ogr_target
366
+ else:
367
+ raise ValueError("La base de donnée %s n'existe pas" % (ogr_target))
368
+
369
+ if include_csv:
370
+ list_files = list(Path(folder).glob(shp_glob_pattern)) + list(
371
+ Path(folder).glob(csv_glob_pattern)
372
+ )
373
+ log("Option CSV activée. Liste des fichier à importer :")
374
+ for i in list_files:
375
+ log(str(i))
376
+ else:
377
+ list_files = list(Path(folder).glob(shp_glob_pattern))
378
+ log("Liste des fichier à importer :")
379
+ for i in list_files:
380
+ log(str(i))
381
+
382
+ for file in list_files:
383
+ if ignore_grace_list and file.name.startswith("l_"):
384
+ log(
385
+ f"le fichier {os.path.basename(file)} sera ignoré car il correspond une liste de valeur GRACE THD",
386
+ logLevel="debug",
387
+ )
388
+ else:
389
+ # On prend le nom de fichier, sans l'extension et en minuscule, normalisé
390
+ # sera le nom de la table sqlite
391
+ tbl_name = file.stem.lower()
392
+ tbl_name = (
393
+ unicodedata.normalize("NFD", tbl_name).encode("ascii", "ignore").decode("utf-8")
394
+ )
395
+ # On remplace les espaces par _ dans le nom
396
+ tbl_name = tbl_name.replace(" ", "_")
397
+ if (not prefix) and recursive:
398
+ prefix_ = Path(file).parent.stem
399
+ else:
400
+ prefix_ = prefix
401
+ import_file_to_spl(ogr_target_, file, tbl_name, prefix=prefix_)
402
+
403
+
404
+ def extract_table_to_file(
405
+ spl_db, view_name, dir_name=None, file_name=None, sheet_name=None, file_type="csv"
406
+ ):
407
+ log(
408
+ f"Extraction de la vue {view_name} au format {file_type} vers {dir_name}/{file_name}.{file_type}"
409
+ )
410
+ start_time = time.time()
411
+ ogr_mapping = {
412
+ "csv": "-f CSV",
413
+ "xlsx": "-f XLSX",
414
+ "shp": "-f 'ESRI Shapefile'",
415
+ "sqlite": "-f sqlite -dsco SPATIALITE=YES",
416
+ }
417
+ if not dir_name:
418
+ dir_name = Path(spl_db).parent
419
+ if not file_name:
420
+ file_name = view_name
421
+ if not sheet_name:
422
+ sheet_name = view_name
423
+
424
+ targetfilepath = str(Path(dir_name) / (file_name + "." + file_type))
425
+ # if target file is the source DB, we do not overwrite to preserve database structure
426
+ # this allow some template tricks
427
+ if (
428
+ Path(spl_db).resolve() # source db
429
+ == (Path(dir_name) / (file_name + "." + file_type)).resolve() # target file
430
+ ):
431
+ overwrite_ = "-append"
432
+ else:
433
+ overwrite_ = "-overwrite"
434
+
435
+ ogr_command = f'ogr2ogr {ogr_mapping[file_type]} "{targetfilepath}" "{spl_db}" -sql "SELECT * FROM {view_name}" -nln {sheet_name} {overwrite_}'
436
+ log(ogr_command, logLevel="debug")
437
+
438
+ # Run the process and wait for it to complete
439
+ result = subprocess.run(
440
+ ogr_command,
441
+ stdout=subprocess.PIPE,
442
+ stderr=subprocess.PIPE,
443
+ encoding="utf-8",
444
+ shell=True,
445
+ )
446
+ # Access the stdout and stderr
447
+ stdout_output = result.stdout
448
+ stderr_output = result.stderr
449
+
450
+ # Get the exit status
451
+ if stderr_output:
452
+ log(stderr_output, logLevel="warning")
453
+
454
+ end_time = time.time()
455
+ duration = end_time - start_time
456
+ log(f"Elapsed time: {duration:.2f} seconds")
457
+
458
+
459
+ def execute_query_to_spl(sql_query, spl_db):
460
+ con = spatialite_connect(spl_db)
461
+ # on execute le fichier sql en tant que script
462
+ log("Exécution du script sur la base de donnée")
463
+ with con:
464
+ cur = con.cursor()
465
+ cur.executescript(sql_query)
466
+ con.close()
467
+
468
+
469
+ def execute_file_to_spl(filename, spl_db):
470
+ """Exécute le script .sql cible dans la base de donnée spl_db.
471
+ si le fichier est un dossier, execute chaque fichier .sql dans l'ordre alphabétique
472
+ """
473
+ # si le fichier est un fichier .sql
474
+ if Path(filename).suffix == ".sql":
475
+ # on lit le fichier sql
476
+ log("Lecture du fichier %s" % (filename))
477
+ with open(filename) as f:
478
+ sql_script = f.read()
479
+ log("Connexion à la base de donnée %s" % (spl_db))
480
+ con = spatialite_connect(spl_db)
481
+ # on execute le fichier sql en tant que script
482
+ log("Exécution du script sur la base de donnée")
483
+ with con:
484
+ cur = con.cursor()
485
+ cur.executescript(sql_script)
486
+ con.close()
487
+ # si le fichier est un dossier, on liste les fichier *.sql et on execute la fonction
488
+ elif Path(filename).is_dir():
489
+ list_file_sql = [file for file in Path(filename).glob("*.sql")]
490
+ # tri par ordre alphabétique
491
+ list_file_sql.sort()
492
+ log("Le fichier cible est un dossier. Liste des fichiers *.sql trouvés dans ce dossier : ")
493
+ for f in list_file_sql:
494
+ log(f)
495
+ for f in list_file_sql:
496
+ execute_file_to_spl(f, spl_db)
497
+ # sinon on renvoit une erreur
498
+ else:
499
+ raise ValueError("Le fichier cible n'est ni un fichier *.sql ni un dossier")
500
+
501
+
502
+ def export_preset_tables(spatialite_db, config_table_name=None):
503
+ """
504
+ On récupère le contenu de la table config_table_name
505
+ Cette table doit avoir le format suivant :
506
+ view_name VARCHAR(254), --> nom de la table ou vue à exporter
507
+ dest_file VARCHAR(254), --> nom du fichier de destination sans extention
508
+ dest_sheet VARCHAR(254), --> nom de l'onglet du fichier (si plusieurs fois le même fichier en xlsx)
509
+ dest_folder VARCHAR(254), --> chemin du sous-dossier d'export (chemin relatif). Doit commencer par "/"
510
+ file_type VARCHAR(254) --> type (csv, xlsx, shp)
511
+ """
512
+
513
+ log("Export des vues calculées")
514
+
515
+ # Default table of views to export changed name
516
+ if not config_table_name:
517
+ if table_exists(spatialite_db, "tactis_list_views_to_export"):
518
+ config_table_name = "tactis_list_views_to_export"
519
+ elif table_exists(spatialite_db, "_qi_exports"):
520
+ config_table_name = "_qi_exports"
521
+
522
+ with sqlite3.connect(spatialite_db) as con:
523
+ con.row_factory = sqlite3.Row
524
+ cur = con.cursor()
525
+ req = "SELECT * FROM %s;" % (config_table_name)
526
+ cur.execute(req)
527
+ rows = cur.fetchall()
528
+
529
+ for row in rows:
530
+ # On traite ligne par ligne.
531
+ # Récupération des paramètres
532
+ file_name = row["dest_file"]
533
+ view_name = row["view_name"]
534
+ sheet_name = row["dest_sheet"]
535
+ file_type = row["file_type"]
536
+ # On construit le chemin absolu dans dest_folder
537
+ dest_folder = str(
538
+ Path(spatialite_db).parents[0].joinpath(row["dest_folder"].strip("/").strip("\\"))
539
+ )
540
+ if not os.path.exists(dest_folder):
541
+ os.makedirs(dest_folder)
542
+ extract_table_to_file(
543
+ spatialite_db,
544
+ view_name,
545
+ dir_name=dest_folder,
546
+ file_name=file_name,
547
+ sheet_name=sheet_name,
548
+ file_type=file_type,
549
+ )
550
+
551
+
552
+ def check_preset_tables(spatialite_db, config_table_name):
553
+ """
554
+ Appelle les tables/vues définies dans la table "config_table_name" et
555
+ vérifie que leur appel de génère pas une erreur SQL. Fonction à effectuer
556
+ sur un template vide, sinon il y aura un temps de calcul des vues selon
557
+ la quantité de données.
558
+
559
+ Rappel structure table "config_table_name"
560
+ view_name VARCHAR(254), --> nom de la table ou vue à exporter
561
+ dest_file VARCHAR(254), --> nom du fichier de destination sans extention
562
+ dest_sheet VARCHAR(254), --> nom de l'onglet du fichier (si plusieurs fois le même fichier en xlsx)
563
+ dest_folder VARCHAR(254), --> chemin du sous-dossier d'export (chemin relatif). Doit commencer par "/"
564
+ file_type VARCHAR(254) --> type (csv, xlsx, shp)
565
+ """
566
+ log("Vérification de la validité des tables/vues listées dans la table%s" % (config_table_name))
567
+ con = spatialite_connect(spatialite_db)
568
+ con.row_factory = sqlite3.Row
569
+ cur = con.cursor()
570
+ req = "SELECT * FROM %s;" % (config_table_name)
571
+ cur.execute(req)
572
+ rows = cur.fetchall()
573
+ # Fermeture de la connexion sql
574
+ cur.close()
575
+ con.close()
576
+ for row in rows:
577
+ view_name = row["view_name"]
578
+ try:
579
+ con = spatialite_connect(spatialite_db)
580
+ cur = con.cursor()
581
+ req = "SELECT * FROM '%s'" % (view_name)
582
+ cur.execute(req)
583
+ cur.close()
584
+ con.close()
585
+ log("%s : OK" % (view_name))
586
+ except Exception as e:
587
+ log("%s : NOK" % (view_name), "warning")
588
+ log(e, "warning")
589
+
590
+
591
+ def delete_rows(db_file, value, table=None, column="import_prefix", condition="CONTIENT"):
592
+ con = spatialite_connect(db_file)
593
+ cur = con.cursor()
594
+ req = " PRAGMA foreign_keys = off; "
595
+ cur.executescript(req)
596
+
597
+ if table is None:
598
+ tables = list_tables(db_file, column_name=column)
599
+ else:
600
+ tables = [table]
601
+
602
+ log("Suppression des lignes répondant à la condition %s %s %s" % (column, condition, value))
603
+ log("Dans les tables suivantes : %s" % (tables))
604
+ for table_ in tables:
605
+ if condition == "EGAL":
606
+ req_del = " DELETE FROM '%s' WHERE %s = '%s' " % (table_, column, value)
607
+ req_count = " SELECT COUNT(*) FROM '%s' WHERE %s = '%s';" % (
608
+ table_,
609
+ column,
610
+ value,
611
+ )
612
+ elif condition == "CONTIENT":
613
+ req_del = " DELETE FROM '%s' WHERE %s LIKE '%%%s%%' " % (
614
+ table_,
615
+ column,
616
+ value,
617
+ )
618
+ req_count = " SELECT COUNT(*) FROM '%s' WHERE %s LIKE '%%%s%%';" % (
619
+ table_,
620
+ column,
621
+ value,
622
+ )
623
+
624
+ cur.execute(req_count)
625
+ n_lines_to_delete = cur.fetchone()[0]
626
+ log("Table %s : %s lignes à supprimer" % (table_, n_lines_to_delete))
627
+ cur.executescript(req_del)
628
+
629
+ cur.close()
630
+ con.close()
631
+
632
+
633
+ def list_tables(db_file, column_name=None):
634
+ with sqlite3.connect(db_file) as con:
635
+ cur = con.cursor()
636
+ # 'SpatialIndex', 'ElementaryGeometries', 'KNN' are not queriable without spatialite extension
637
+ req = "SELECT name FROM sqlite_master WHERE type='table' AND name NOT IN ('SpatialIndex', 'ElementaryGeometries', 'KNN', 'KNN2');"
638
+ cur.execute(req)
639
+ rows = cur.fetchall()
640
+
641
+ if column_name is None:
642
+ tables = [row[0] for row in rows]
643
+ else:
644
+ tables = [row[0] for row in rows if column_exists(db_file, row[0], column_name)]
645
+
646
+ return tables