SQLDbWrpr 4.2.0__py3-none-any.whl → 5.0.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.
sqldbwrpr/sqldbwrpr.py CHANGED
@@ -1,1370 +1,1370 @@
1
- """Wrapper for importing CSV and Text files into MySQL and Postgresql.
2
-
3
- Only the MySQL implementation is working. The MSSQL ODBC implementation
4
- is giving me a lot of shit with the connection and I gave up for now to
5
- get it working.
6
- """
7
-
8
- import datetime
9
- import logging
10
- import os
11
- import sys
12
-
13
- import csvwrpr
14
- import displayfx
15
- import fixdate
16
- import mysql.connector
17
- import pyodbc
18
- from beetools import msg as bm
19
- from mysql.connector import Error
20
- from mysql.connector import errorcode
21
-
22
- # from pathlib import Path
23
-
24
-
25
- # _PROJ_DESC = __doc__.split("\n")[0]
26
- # _PROJ_PATH = Path(__file__)
27
- # _PROJ_NAME = _PROJ_PATH.stem
28
-
29
-
30
- class SQLDbWrpr:
31
- """This module creates a wrapper for the MySql database."""
32
-
33
- def __init__(
34
- self,
35
- p_host_name="localhost",
36
- p_user_name="",
37
- p_password="",
38
- p_recreate_db=False,
39
- p_db_name="",
40
- p_db_structure=None,
41
- p_batch_size=10000,
42
- p_bar_len=50,
43
- p_msg_width=50,
44
- p_verbose=False,
45
- p_db_port="3306",
46
- p_ssl_ca=None,
47
- p_ssl_key=None,
48
- p_ssl_cert=None,
49
- ):
50
- """Create database with supplied structure and return a connector to the database
51
-
52
- Parameters
53
- - p_host_name = Host to connect to
54
- - p_user_name = User name for connection
55
- - p_password = paswword of user
56
- - ReCreate = Recresate the database or connect to existing database
57
- - db_name =
58
- - table_details: Details of the tables to be created
59
- - batch_size: Bulk data will be managed by batch_size to commit
60
- - p_bar_len: Length for the progress bar
61
- - p_msg_width: Width of message before progress bar
62
- """
63
- self.logger_name = __name__
64
- self.logger = logging.getLogger(self.logger_name)
65
- self.logger.info("Start")
66
- self.success = False
67
- self.bar_len = p_bar_len
68
- self.batch_size = p_batch_size
69
- self.char_fields = {}
70
- self.conn = None
71
- self.cur = None
72
- self.db_name = p_db_name
73
- if p_db_structure:
74
- self.db_structure = p_db_structure
75
- else:
76
- self.db_structure = {}
77
- self.delimiter = ","
78
- self.fkey_ref_act = {
79
- "C": "CASCADE",
80
- "R": "RESTRICT",
81
- "D": "SET DEFAULT",
82
- "N": "SET NULL",
83
- }
84
- self.host_name = p_host_name
85
- self.idx_type = {"U": "UNIQUE", "F": "FULLTEXT", "S": "SPATIAL"}
86
- self.msg_width = p_msg_width
87
- self.non_char_fields = {}
88
- self._password = p_password
89
- self.re_create_db = p_recreate_db
90
- self.silent = p_verbose
91
- self.sort_order = {"A": "ASC", "D": "DESC"}
92
- self.table_load_order = []
93
- self.user_name = p_user_name
94
- self.get_db_field_types()
95
- self.db_port = p_db_port
96
-
97
- def close(self):
98
- """Close the connention"""
99
- if self.conn:
100
- self.conn.close()
101
-
102
- def create_db(self):
103
- """Create the database according to self.db_structure."""
104
- self.cur.execute("SHOW DATABASES")
105
- db_res = [str(x[0]) for x in self.cur.fetchall()]
106
- # if self.db_name.lower() in db_res:
107
- if self.db_name in db_res:
108
- try:
109
- self.cur.execute(f"DROP DATABASE {self.db_name}")
110
- self.conn.commit()
111
- except mysql.connector.Error as err:
112
- self._print_err_msg(err, "Could not drop the database")
113
- self.close()
114
- sys.exit()
115
- try:
116
- self.cur.execute(f'CREATE DATABASE {self.db_name} DEFAULT CHARACTER SET "utf8"')
117
- self.conn.commit()
118
- self.cur.execute(f"USE {self.db_name}")
119
- self.conn.commit()
120
- except mysql.connector.Error as err:
121
- self._print_err_msg(err, "Could not create the database")
122
- self.close()
123
- sys.exit()
124
- return True
125
-
126
- def create_tables(self):
127
- """Create db tables from MySQL.table_details dict"""
128
-
129
- def build_db(p_db_sql_str_set):
130
- """Description"""
131
- for sql_set in p_db_sql_str_set:
132
- try:
133
- self.cur.execute(sql_set[1])
134
- if self.silent:
135
- print(f"Created table = {sql_set[0]}")
136
- except mysql.connector.Error as err:
137
- print(f"Failed creating table = {sql_set[0]}: {err}\nForced termination of program")
138
- print(f"{sql_set[1]}")
139
- sys.exit()
140
- pass
141
-
142
- # end build_db
143
-
144
- def generate_db_sql(
145
- p_table_set_up_str,
146
- p_primary_key_str,
147
- p_idx_set_up_list,
148
- p_constraint_set_up_list,
149
- ):
150
- """Description"""
151
- table_set_up_str = p_table_set_up_str
152
- table_set_up_str += p_primary_key_str
153
- for idx_str in p_idx_set_up_list:
154
- table_set_up_str += idx_str
155
- for constraint_str in p_constraint_set_up_list:
156
- table_set_up_str += constraint_str[2]
157
- table_set_up_str = f"{table_set_up_str[:-2]})"
158
- return table_set_up_str
159
-
160
- # end generate_db_sql
161
-
162
- def build_constraints(p_table_name):
163
- # noinspection PySingleQuotedDocstring
164
- """Description"""
165
- constraint_list = []
166
- fkey_nr_list = []
167
- for field_name in self.db_structure[p_table_name]:
168
- fkey = get_foreign_key(p_table_name, field_name)
169
- if fkey["Present"]:
170
- fkey_nr_list.append(fkey["ForeignKeyNr"])
171
- # fkey_PROJ_NAME = 'fk_{}_{}'.format( fkey[ 'FKeyTable' ], fkey[ 'RefTable' ])
172
- fkey_str = (
173
- "CONSTRAINT fk_{}_{} FOREIGN KEY ({}) REFERENCES {} ({}) ON DELETE {} ON UPDATE {}, ".format(
174
- fkey["FKeyTable"],
175
- fkey["RefTable"],
176
- ".".join(fkey["FKeyFlds"]),
177
- fkey["RefTable"],
178
- ".".join(fkey["RefFields"]),
179
- self.fkey_ref_act[fkey["OnDelete"]],
180
- self.fkey_ref_act[fkey["OnUpdate"]],
181
- )
182
- )
183
- constraint_list.append([fkey["FKeyTable"], fkey["RefTable"], fkey_str])
184
- pass
185
- return constraint_list
186
-
187
- # def build_constraints
188
-
189
- def build_all_indexes(p_table_name):
190
- """Description"""
191
-
192
- # def build_primary_key_idx(p_table_name):
193
- # '''Description'''
194
- # idx_name_list = []
195
- # idx_str_list = []
196
- # pkey = get_primary_key(p_table_name)
197
- # idx_name = '{}_UNIQUE'.format('_'.join(pkey['Flds']))
198
- # idx_name_list.append(idx_name)
199
- # idx_str = 'UNIQUE INDEX pk_{} ({}) VISIBLE, '.format(
200
- # idx_name, ','.join(pkey['Flds'])
201
- # )
202
- # idx_str_list.append(idx_str)
203
- # return idx_str_list, idx_name_list
204
- #
205
- # # end build_primary_key_idx
206
-
207
- def build_unique_key_idx(p_table_name, p_dx_name_list, p_idx_str_list):
208
- """Description"""
209
- idx_list = {}
210
- idx_name_list = p_dx_name_list
211
- idx_str_list = p_idx_str_list
212
- for field_name in self.db_structure[p_table_name]:
213
- field_param_st_ref = self.db_structure[p_table_name][field_name]["Params"]
214
- if field_param_st_ref["Index"]:
215
- if field_param_st_ref["Index"][0] not in idx_list:
216
- idx_list[field_param_st_ref["Index"][0]] = [[field_name] + field_param_st_ref["Index"][1:]]
217
- else:
218
- idx_list[field_param_st_ref["Index"][0]].append(
219
- [field_name] + field_param_st_ref["Index"][1:]
220
- )
221
- for idx_instance in idx_list:
222
- idx_instance_order = sorted(idx_list[idx_instance], key=lambda x: x[1])
223
- idx_name = ""
224
- for field_det in idx_instance_order:
225
- idx_name += f"{field_det[0]}_"
226
- if field_det[3] == "U":
227
- idx_name = f"unq_{idx_name[:-1]}"
228
- else:
229
- idx_name = f"idx_{idx_name[:-1]}"
230
- if idx_name not in idx_name_list:
231
- idx_name_list.append(idx_name)
232
- if field_det[3] == "U":
233
- idx_str = f"{self.idx_type[field_det[3]]} INDEX {idx_name} ("
234
- else:
235
- idx_str = f"INDEX {idx_name} ("
236
- for field_det in idx_instance_order:
237
- idx_str += f"{field_det[0]} {self.sort_order[field_det[2]]}, "
238
- idx_str = idx_str[:-2] + ") VISIBLE, "
239
- idx_str_list.append(idx_str)
240
- return idx_str_list, idx_name_list
241
-
242
- # end build_unique_key_idx
243
-
244
- idx_name_list = []
245
- idx_list = []
246
- idx_list, idx_name_list = build_unique_key_idx(p_table_name, idx_name_list, idx_list)
247
- return idx_list
248
-
249
- # def build_all_indexes
250
-
251
- def build_primary_key_sql_str(p_table_name):
252
- """Description"""
253
- primary_key_det = get_primary_key(p_table_name)
254
- sql_str = "PRIMARY KEY ({}), ".format(",".join(primary_key_det["Flds"]))
255
- return sql_str
256
-
257
- # def build_primary_key_sql_str
258
-
259
- def build_table_sql_str(p_table_name):
260
- """Description"""
261
- sql_str = f"CREATE TABLE {p_table_name} ("
262
- for field_name in self.db_structure[p_table_name]:
263
- field_type_st_ref = self.db_structure[p_table_name][field_name]["Type"]
264
- field_param_st_ref = self.db_structure[p_table_name][field_name]["Params"]
265
- field_comment_st_ref = self.db_structure[p_table_name][field_name]["Comment"]
266
- sql_str += f"{field_name} {field_type_st_ref[0]}"
267
- if field_type_st_ref[0] == "varchar" or field_type_st_ref[0] == "char":
268
- sql_str += f" ({str(field_type_st_ref[1])})"
269
- elif field_type_st_ref[0] == "decimal":
270
- sql_str += f"({str(field_type_st_ref[1])}, {str(field_type_st_ref[2])})"
271
- if field_param_st_ref["AI"] == "Y":
272
- sql_str += " AUTO_INCREMENT"
273
- if field_param_st_ref["UN"] == "Y" and field_param_st_ref["AI"] != "Y":
274
- sql_str += " UNSIGNED"
275
- if field_param_st_ref["NN"] == "Y":
276
- sql_str += " NOT NULL"
277
- if field_param_st_ref["ZF"] == "Y":
278
- sql_str += " ZEROFILL"
279
- if field_param_st_ref["DEF"]:
280
- if field_type_st_ref[0] == "varchar" or field_type_st_ref[0] == "char":
281
- sql_str += ' DEFAULT "{}"'.format(field_param_st_ref["DEF"])
282
- else:
283
- sql_str += " DEFAULT {}".format(field_param_st_ref["DEF"])
284
- if field_comment_st_ref:
285
- sql_str += f' COMMENT "{field_comment_st_ref}"'
286
- sql_str += ", "
287
- return sql_str
288
-
289
- # end build_table_sql_str
290
-
291
- def get_foreign_key(p_table_name, p_field_name):
292
- """Description"""
293
- fkey = {
294
- "Present": False,
295
- "FKeyFlds": [],
296
- "RefFields": [],
297
- "FKeyTable": "",
298
- "RefTable": "",
299
- "ForeignKeyNr": False,
300
- "OnDelete": "N",
301
- "OnUpdate": "N",
302
- }
303
- fkey_source = self.db_structure[p_table_name][p_field_name]["Params"]["FKey"]
304
- if fkey_source:
305
- table_det = self.db_structure[p_table_name]
306
- fkey["ForeignKeyNr"] = fkey_source[0]
307
- fkey["FKeyTable"] = p_table_name
308
- fkey["RefTable"] = fkey_source[2]
309
- ref_field_pair_list = []
310
- for field in table_det:
311
- if table_det[field]["Params"]["FKey"]:
312
- if table_det[field]["Params"]["FKey"][0] == fkey["ForeignKeyNr"]:
313
- ref_field_pair_list.append(
314
- [
315
- field,
316
- table_det[field]["Params"]["FKey"][3],
317
- table_det[field]["Params"]["FKey"][1],
318
- ]
319
- )
320
- ref_field_pair_list = sorted(ref_field_pair_list, key=lambda x: x[2])
321
- fkey["FKeyFlds"], fkey["RefFields"], t_order = zip(*ref_field_pair_list)
322
- fkey["OnDelete"] = fkey_source[4]
323
- fkey["OnUpdate"] = fkey_source[5]
324
- fkey["Present"] = True
325
- return fkey
326
-
327
- # end get_foreign_key
328
-
329
- def get_primary_key(p_table_name):
330
- """Description"""
331
- pkey = {"Present": False, "Flds": (), "SortPairList": [], "SortPairStr": []}
332
- for field_name in self.db_structure[p_table_name]:
333
- pkey_field_det = self.db_structure[p_table_name][field_name]
334
- if pkey_field_det["Params"]["PrimaryKey"][0] == "Y":
335
- pkey["Flds"] += (field_name,)
336
- pkey["SortPairList"].append(
337
- (
338
- field_name,
339
- self.sort_order[pkey_field_det["Params"]["PrimaryKey"][1]],
340
- )
341
- )
342
- pkey["SortPairStr"].append(
343
- "{} {}".format(
344
- field_name,
345
- self.sort_order[pkey_field_det["Params"]["PrimaryKey"][1]],
346
- )
347
- )
348
- pkey["Present"] = True
349
- return pkey
350
-
351
- # end get_primary_key
352
-
353
- def order_table_build_list(p_db_sql_str_set, p_constraint_set_up_list):
354
- """Description"""
355
- db_sql_str_set = p_db_sql_str_set
356
- ordered = False
357
- while not ordered:
358
- ordered = True
359
- for constraint in p_constraint_set_up_list:
360
- fkey_pos_found = False
361
- i = 0
362
- fkey_pos = -1
363
- while not fkey_pos_found:
364
- if db_sql_str_set[i][0] == constraint[1]:
365
- fkey_pos_found = True
366
- fkey_pos = i
367
- else:
368
- i += 1
369
- table_pos_found = False
370
- i = 0
371
- tbl_pos = -1
372
- while not table_pos_found:
373
- if db_sql_str_set[i][0] == constraint[0]:
374
- table_pos_found = True
375
- tbl_pos = i
376
- else:
377
- i += 1
378
- if tbl_pos < fkey_pos:
379
- db_sql_str_set.insert(fkey_pos + 1, db_sql_str_set[tbl_pos])
380
- del db_sql_str_set[tbl_pos]
381
- ordered = False
382
- self.table_load_order = [x[0] for x in db_sql_str_set]
383
- return db_sql_str_set
384
-
385
- # end order_table_build_list
386
-
387
- def structure_validation():
388
- """Description"""
389
-
390
- def check_pkey_fkey_overlap(p_remove_fkey_pkey__overlap=True):
391
- """Description"""
392
-
393
- def partial_overlap(p_fkey, p_pkey):
394
- """Description"""
395
- is_overlap = False
396
- for field_name in p_fkey["FKeyFlds"]:
397
- if field_name in p_pkey["Flds"]:
398
- is_overlap = True
399
- return is_overlap
400
-
401
- # end partial_overlap
402
-
403
- def remove_fkey(p_fkey):
404
- """Description"""
405
- for field_name in self.db_structure[p_fkey["FKeyTable"]]:
406
- if self.db_structure[p_fkey["FKeyTable"]][field_name]["Params"]["FKey"]:
407
- if (
408
- self.db_structure[p_fkey["FKeyTable"]][field_name]["Params"]["FKey"][0]
409
- == p_fkey["ForeignKeyNr"]
410
- ):
411
- self.db_structure[p_fkey["FKeyTable"]][field_name]["Params"]["FKey"] = []
412
- pass
413
-
414
- # end remove_fkey
415
-
416
- for table_name in self.db_structure:
417
- pkey = get_primary_key(table_name)
418
- source_table = self.db_structure[table_name]
419
- for field_name in source_table:
420
- fkey = get_foreign_key(table_name, field_name)
421
- if fkey["Present"]:
422
- if pkey["Flds"] != fkey["FKeyFlds"] and partial_overlap(fkey, pkey):
423
- log_str = "The foreign key {}.{} and the primary key in {}.{} overlaps.".format(
424
- fkey["FKeyTable"],
425
- fkey["FKeyFlds"],
426
- table_name,
427
- pkey["Flds"],
428
- )
429
- self.logger.warning(log_str)
430
- if p_remove_fkey_pkey__overlap:
431
- remove_fkey(fkey)
432
- log_str = 'Current settings forced removed the foreign key "{}.{}"'.format(
433
- fkey["FKeyTable"], fkey["FKeyFlds"]
434
- )
435
- self.logger.warning(log_str)
436
- else:
437
- log_str = "This may cause a problem adding record to either {} or {}".format(
438
- fkey["FKeyTable"], table_name
439
- )
440
- self.logger.warning(log_str)
441
- pass
442
- pass
443
-
444
- # end check_pkey_ukey_overlap
445
- check_pkey_fkey_overlap()
446
- pass
447
-
448
- # end structure_validation()
449
-
450
- success = True
451
- structure_validation()
452
- table_set_up_list = ""
453
- idx_set_up_list = []
454
- constraint_set_up_list = []
455
- db_sql_str_set = []
456
- for table_name in self.db_structure:
457
- table_set_up_list = build_table_sql_str(table_name)
458
- primary_key_str = build_primary_key_sql_str(table_name)
459
- idx_set_up_list = build_all_indexes(table_name)
460
- tblconstraint_list = build_constraints(table_name)
461
- db_sql_str_set.append(
462
- [
463
- table_name,
464
- generate_db_sql(
465
- table_set_up_list,
466
- primary_key_str,
467
- idx_set_up_list,
468
- tblconstraint_list,
469
- ),
470
- ]
471
- )
472
- if tblconstraint_list:
473
- constraint_set_up_list += tblconstraint_list
474
- pass
475
- db_sql_str_set = order_table_build_list(db_sql_str_set, constraint_set_up_list)
476
- build_db(db_sql_str_set)
477
- return success
478
-
479
- def create_users(self, p_admin_user, p_new_users):
480
- c_user_name = 0
481
- self.cur.execute("SELECT User, Host FROM mysql.user")
482
- curr_users = self.cur.fetchall()
483
- for user in p_new_users:
484
- if not user[c_user_name] in curr_users:
485
- try:
486
- self.cur.execute(
487
- "CREATE USER IF NOT EXISTS '{}'@'{}' IDENTIFIED BY '{}'".format(
488
- user[0], self.host_name, user[1]
489
- )
490
- )
491
- except mysql.connector.Error as err:
492
- self._print_err_msg(err, "Could not create user")
493
- self.close()
494
- sys.exit()
495
- self.conn.commit()
496
- self.success = True
497
-
498
- def delete_users(self, p_admin_user, p_del_users):
499
- c_user_name = 0
500
- # c_password = 1
501
- c_host = 2
502
- self.cur.execute("SELECT User FROM mysql.user")
503
- curr_users = [x[0] for x in self.cur.fetchall()]
504
- for user in p_del_users:
505
- if user[c_user_name] in curr_users:
506
- try:
507
- self.cur.execute(f"DROP USER '{user[c_user_name]}'@'{user[c_host]}'")
508
- except mysql.connector.Error as err:
509
- self._print_err_msg(err, "Could not delete user")
510
- self.close()
511
- sys.exit()
512
- self.success = True
513
-
514
- def _err_broken_rec(self, p_sql_str, p_csv_db_slice):
515
- """Write broken record to logger"""
516
- # self.logger.critical( p_err )
517
- for row in p_csv_db_slice:
518
- try:
519
- self.cur.execute(p_sql_str, row)
520
- except Exception:
521
- self.logger.warning(f"{p_sql_str}\n{row}\nForced program termination")
522
- sys.exit()
523
- else:
524
- self.conn.commit()
525
- pass
526
- pass
527
-
528
- def export_to_csv(
529
- self,
530
- p_csv_path,
531
- p_table_name,
532
- p_delimeter="|",
533
- p_strip_chars="",
534
- p__vol_size=0,
535
- p_sql_query="",
536
- ):
537
- """Export a table to a csv file
538
-
539
- Parameters
540
- - p_csv_path - Path name of the file to be exported
541
- - p_table_name = '' - Table name to export
542
- - p_delimeter = '|' - Field delimiter to use
543
- - p_strip_chars = '' - characters to strip from text
544
- - p__vol_size = 0 - Create a multiple volume export. p__vol_size is
545
- the number of records per file. 0 will create
546
- only one volume.
547
- """
548
-
549
- def multi_volume_export(p_csv_path, p__vol_size):
550
- """Create multiple volumes in path with p__vol_size records
551
-
552
- Parameters
553
- - p_csv_path - Path name of the file to be exported
554
- - p__vol_size = 0 - Create a multiple volume export. p__vol_size is
555
- the number of records per file. 0 will create
556
- only one volume.
557
- """
558
- file_name_list = []
559
- header = p_delimeter.join(self.db_structure[p_table_name])
560
- prim_key_sql_str = "SELECT "
561
- all_sql_str = "SELECT " + header.replace(p_delimeter, ",") + " FROM " + p_table_name + " WHERE "
562
- for i, field in enumerate(self.db_structure[p_table_name]):
563
- if self.db_structure[p_table_name][field]["Params"]["PrimaryKey"][0] == "Y":
564
- prim_key_sql_str += field + ", "
565
- all_sql_str += field + " = %s and "
566
- prim_key_sql_str = prim_key_sql_str[:-2] + " FROM " + p_table_name
567
- all_sql_str = all_sql_str[:-5]
568
- print(f"Collecting {p_table_name} table records")
569
- self.cur.execute(prim_key_sql_str)
570
- prim_key_res = self.cur.fetchall()
571
- vol_cntr = 1
572
- # curr_vol_size = p__vol_size
573
- list_len = len(prim_key_res)
574
- msg = bm.display(
575
- f"Export records table = {p_table_name} ({list_len})",
576
- p_len=self.msg_width,
577
- )
578
- rec_cntr = 0
579
- pfx = displayfx.DisplayFx(list_len, p_msg=msg, p_bar_len=self.bar_len)
580
- csv_file = None
581
- for i, pkeys_rec in enumerate(prim_key_res):
582
- if rec_cntr == 0:
583
- if rec_cntr == 0 and vol_cntr > 1:
584
- csv_file.close()
585
- # if list_len - ((vol_cntr - 1) * p__vol_size) < p__vol_size:
586
- # curr_vol_size = list_len - ((vol_cntr - 1) * p__vol_size)
587
- if vol_cntr == 1:
588
- csv_vol_path = p_csv_path
589
- else:
590
- csv_vol_path = p_csv_path[:-4] + f"{vol_cntr:0>2}" + p_csv_path[-4:]
591
- file_name_list.append(os.path.split(csv_vol_path))
592
- csv_file = open(csv_vol_path, "w+")
593
- csv_file.write(header + "\n")
594
- self.cur.execute(all_sql_str, pkeys_rec)
595
- row_res = self.cur.fetchall()[0]
596
- csv_row = ""
597
- for j, field in enumerate(row_res):
598
- if field is None:
599
- field = "NULL"
600
- if j in self.char_fields[p_table_name]:
601
- csv_row += '"' + str(field) + '"' + p_delimeter
602
- else:
603
- csv_row += str(field) + p_delimeter
604
- for char in p_strip_chars:
605
- csv_row.replace(char, "")
606
- csv_file.write(csv_row[:-1] + "\n")
607
- if rec_cntr == p__vol_size:
608
- rec_cntr = 0
609
- vol_cntr += 1
610
- else:
611
- rec_cntr += 1
612
- pfx.update(i)
613
- csv_file.close()
614
- return file_name_list
615
-
616
- # end multi_volume_export
617
-
618
- def single_volume_export(p_csv_path, p_sql_query):
619
- """Create single volume in path with p__vol_size records
620
-
621
- Parameters
622
- - p_csv_path - Path name of the file to be exported
623
- """
624
- header = ""
625
- file_name_list = []
626
- file_name_list.append(os.path.split(p_csv_path))
627
- if not p_sql_query:
628
- header = p_delimeter.join(self.db_structure[p_table_name])
629
- sql_str = "SELECT " + header.replace(p_delimeter, ",") + " FROM " + p_table_name
630
- else:
631
- header = p_delimeter.join(p_sql_query[0])
632
- sql_str = p_sql_query[1]
633
- csv_file = open(p_csv_path, "w+")
634
- csv_file.write(header + "\n")
635
- print(f"Collecting {p_table_name} table records")
636
- self.cur.execute(sql_str)
637
- table_res = self.cur.fetchall()
638
- # cntr = 0
639
- list_len = len(table_res)
640
- msg = bm.display(
641
- f"Export records table = {p_table_name} ({list_len})",
642
- p_len=self.msg_width,
643
- )
644
- dfx = displayfx.DisplayFx(list_len, p_msg=msg, p_bar_len=self.bar_len)
645
- for i, row in enumerate(table_res):
646
- csv_row = ""
647
- for j, field in enumerate(row):
648
- # if not field:
649
- if field is None:
650
- field = "NULL"
651
- if j in self.char_fields[p_table_name]:
652
- csv_row += '"' + str(field) + '"' + p_delimeter
653
- else:
654
- csv_row += str(field) + p_delimeter
655
- for char in p_strip_chars:
656
- csv_row.replace(char, "")
657
- csv_file.write(csv_row[:-1] + "\n")
658
- dfx.update(i)
659
- csv_file.close()
660
- return file_name_list
661
-
662
- # end single_volume_export
663
-
664
- file_name_list = None
665
- try:
666
- self.cur.execute("SELECT COUNT(*) FROM " + p_table_name)
667
- except mysql.connector.Error as err:
668
- print(f"Err mesg: {err.msg}")
669
- print(err.msg)
670
- else:
671
- count_rec_res = self.cur.fetchall()[0][0]
672
- if p__vol_size > 0 and count_rec_res > p__vol_size and not p_sql_query:
673
- file_name_list = multi_volume_export(p_csv_path, p__vol_size)
674
- else:
675
- file_name_list = single_volume_export(p_csv_path, p_sql_query)
676
- # success = True
677
- return file_name_list
678
-
679
- def get_db_field_types(self):
680
- """Description"""
681
- for p_table_name in self.db_structure:
682
- self.char_fields[p_table_name] = []
683
- self.non_char_fields[p_table_name] = []
684
- for field in self.db_structure[p_table_name]:
685
- if (
686
- self.db_structure[p_table_name][field]["Type"][0] == "char"
687
- or self.db_structure[p_table_name][field]["Type"][0] == "varchar"
688
- ):
689
- self.char_fields[p_table_name].append(field)
690
- else:
691
- self.non_char_fields[p_table_name].append(field)
692
-
693
- def grant_rights(self, p_admin_user, p_user_rights):
694
- c_user_name = 0
695
- # c_password = 1
696
- c_host = 1
697
- c_db = 2
698
- c_table = 3
699
- c_rights = 4
700
- # success = True
701
- for right in p_user_rights:
702
- try:
703
- sql_str = "GRANT {} ON {}.{} TO '{}'@'{}'".format(
704
- ",".join(right[c_rights:]),
705
- right[c_db],
706
- right[c_table],
707
- right[c_user_name],
708
- right[c_host],
709
- )
710
- self.cur.execute(sql_str)
711
- self.conn.commit()
712
- sql_str = "GRANT {} ON {}.{} TO '{}'@'{}' WITH GRANT OPTION".format(
713
- ",".join(right[c_rights:]),
714
- right[c_db],
715
- right[c_table],
716
- right[c_user_name],
717
- right[c_host],
718
- )
719
- self.cur.execute(sql_str)
720
- self.conn.commit()
721
- except mysql.connector.Error as err:
722
- self._print_err_msg(err)
723
- self.close()
724
- sys.exit()
725
- self.success = True
726
-
727
- def import_csv(
728
- self,
729
- p_table_name,
730
- p_csv_file_name="",
731
- p_key="",
732
- p_header="",
733
- p_del_head=False,
734
- p_csv_db="",
735
- p_csv_corr_str_file_name="",
736
- p_vol_type="Multi",
737
- p_verbose=False,
738
- p_replace=False,
739
- ):
740
- """Import a csv file into a database table.
741
-
742
- Parameters
743
- - p_table_name
744
- Table name to import the csv data into
745
- - p_csv_file_name = ''
746
- Csv file name. Empty if structure contained in p_csv_db
747
- - p_key = ''
748
- Key used to insert in table
749
- - p_header = ''
750
- - Header of csv files
751
- - p_del_head = ''
752
- - Delete the header
753
- - p_csv_db = ''
754
- - Contains the csv table in a structure and makes p_csv_file_name obsolete.
755
- - p_csv_corr_str_file_name = ''
756
- - String that contains any strings that should be replace in the csv
757
- file before parsing
758
- - p_vol_type = 'Multi'
759
- - Multi - Read multiple volume
760
- - Single - Read single file
761
- - p_verbose = False
762
- - Determine if there are any output to screen
763
- - debug = False
764
- - Switch debug on
765
- - p_replace = False
766
- - False - INSERT into database
767
- - True - REPLACE into database
768
- """
769
-
770
- def import_volume(p_csv_db, p_header, p_verbose):
771
- """Description"""
772
-
773
- def convert_str_to_none(p_non_char_fields_idx, p_csv_db):
774
- """Description"""
775
- rows_to_del = []
776
- csv_db = p_csv_db
777
- list_len = len(csv_db)
778
- msg = bm.display(
779
- f"Convert empty strings to None ({list_len})",
780
- p_len=self.msg_width,
781
- )
782
- dfx = displayfx.DisplayFx(
783
- list_len,
784
- p_msg=msg,
785
- p_verbose=p_verbose,
786
- p_bar_len=self.bar_len,
787
- )
788
- for row_idx, row in enumerate(csv_db):
789
- found_none = False
790
- t_tow = list(csv_db[row_idx])
791
- for field in p_non_char_fields_idx:
792
- if t_tow[field] == "":
793
- t_tow[field] = None
794
- found_none = True
795
- if found_none:
796
- csv_db.append(tuple(t_tow))
797
- rows_to_del.append(row_idx)
798
- dfx.update(row_idx)
799
- list_len = len(rows_to_del)
800
- msg = bm.display(f"Cleanup ({list_len})", p_len=self.msg_width)
801
- dfx = displayfx.DisplayFx(
802
- list_len,
803
- p_msg=msg,
804
- p_verbose=p_verbose,
805
- p_bar_len=self.bar_len,
806
- )
807
- for i, row_idx in enumerate(sorted(rows_to_del, reverse=True)):
808
- del csv_db[row_idx]
809
- dfx.update(i)
810
- print()
811
- return csv_db
812
-
813
- # end convert_str_to_none
814
-
815
- def find_non_char_field_idx(p_csv_db):
816
- """Find the index of the fields that could potentially contain mepty strings."""
817
- non_char_fields_idx = []
818
- for header_field_name in self.non_char_fields[p_table_name]:
819
- for row_idx, data_field_name in enumerate(p_csv_db[0]):
820
- if header_field_name == data_field_name:
821
- non_char_fields_idx.append(row_idx)
822
- break
823
- return non_char_fields_idx
824
-
825
- # end find_non_char_field_idx
826
-
827
- def fix_dates(p_csv_db, p_table_name, p_header):
828
- """Ensure date and datetime fields in the database is valid."""
829
- c_field_idx = 0
830
- c_field_type = 1
831
- csv_db = p_csv_db
832
- idx = []
833
- # date_time_idx = []
834
- for i, field in enumerate(p_header):
835
- if field in self.db_structure[p_table_name]:
836
- if self.db_structure[p_table_name][field]["Type"][0] == "date":
837
- idx.append([i, "date"])
838
- elif self.db_structure[p_table_name][field]["Type"][0] == "datetime":
839
- idx.append([i, "datetime"])
840
- if idx:
841
- for i, row in enumerate(csv_db[1:]):
842
- for field_det in idx:
843
- if row[field_det[c_field_idx]] is not None:
844
- if field_det[c_field_type] == "date" and not isinstance(
845
- row[field_det[c_field_idx]], datetime.date
846
- ):
847
- fixed_date = fixdate.FixDate(
848
- # self.logger_name,
849
- row[field_det[c_field_idx]],
850
- p_out_format="%Y/%m/%d",
851
- ).date_str
852
- if isinstance(csv_db[i + 1], tuple):
853
- csv_db[i + 1] = (
854
- csv_db[i + 1][: field_det[c_field_idx]]
855
- + (fixed_date,)
856
- + csv_db[i + 1][field_det[c_field_idx] + 1 :]
857
- )
858
- if isinstance(csv_db[i + 1], list):
859
- csv_db[i + 1] = (
860
- csv_db[i + 1][: field_det[c_field_idx]]
861
- + [fixed_date]
862
- + csv_db[i + 1][field_det[c_field_idx] + 1 :]
863
- )
864
- pass
865
- # elif field_det[ c_field_type ] == 'datetime' and isinstance( row[ field_det[ c_field_idx ]], datetime.datetime ):
866
- # date, time = row[ field_det[ c_field_idx ]].split( ' ' )
867
- # date, time = row[ field_det[ c_field_idx ]].split( ' ' )
868
- # fixed_date = fixdate.FixDate( self.logger_name, date, p_out_format = '%Y/%m/%d').date_str
869
- # if isinstance( csv_db[ i + 1 ], tuple ):
870
- # csv_db[ i + 1 ] = csv_db[ i + 1 ][:field_det[ c_field_idx ]] + ( '{} {}'.format( fixed_date, time ), ) \
871
- # + csv_db[ i + 1 ][ field_det[ c_field_idx ] + 1:]
872
- # if isinstance( csv_db[ i + 1 ], list ):
873
- # csv_db[ i + 1 ] = csv_db[ i + 1 ][:field_det[ c_field_idx ]] + [ '{} {}'.format( fixed_date, time ) ] \
874
- # + csv_db[ i + 1 ][ field_det[ c_field_idx ] + 1:]
875
- pass
876
- pass
877
- return csv_db
878
-
879
- # end fix_dates
880
-
881
- def write_to_table(p_csv_db):
882
- """Write the data to a table"""
883
- i = 1
884
- j = 0 # In case batch size is more than all records
885
- list_len = len(p_csv_db)
886
- msg = bm.display(
887
- f"Populate table = {p_table_name} ({list_len})",
888
- p_len=self.msg_width,
889
- )
890
- dfx = displayfx.DisplayFx(
891
- list_len,
892
- p_msg=msg,
893
- p_verbose=p_verbose,
894
- p_bar_len=self.bar_len,
895
- )
896
- # sql_str = 'REPLACE'
897
- if p_replace:
898
- sql_str = "REPLACE"
899
- else:
900
- sql_str = "INSERT"
901
- sql_str = "{} INTO {} ({}) VALUES ({})".format(
902
- sql_str,
903
- p_table_name,
904
- ",".join([str(x) for x in header]),
905
- ",".join(["%s" for x in range(len(header))]),
906
- )
907
- for j in range(self.batch_size, list_len, self.batch_size):
908
- try:
909
- self.cur.executemany(sql_str, p_csv_db[i : j + 1])
910
- except Error as err:
911
- self.logger.error(err)
912
- self.conn.rollback()
913
- self._err_broken_rec(sql_str, p_csv_db[i : j + 1])
914
- finally:
915
- self.conn.commit()
916
- i = j + 1
917
- dfx.update(j)
918
- # New needs to be tested. Writing the records 1 by 1?
919
- # self.logger.debug('{}'.format(p_csv_db[j + 1 : len(p_csv_db)]))
920
- self.cur.executemany(sql_str, p_csv_db[j + 1 : len(p_csv_db)])
921
- self.conn.commit()
922
- if j < list_len:
923
- dfx.update(list_len)
924
- pass
925
-
926
- # end write_to_table
927
-
928
- csv_db = p_csv_db
929
- if p_header:
930
- header = p_header
931
- else:
932
- header = csv_db[0]
933
- csv_db = fix_dates(csv_db, p_table_name, header)
934
- if self.non_char_fields[p_table_name]:
935
- csv_db = convert_str_to_none(find_non_char_field_idx(p_csv_db), p_csv_db)
936
- write_to_table(csv_db)
937
- pass
938
-
939
- # end import_volume
940
-
941
- def import_single_volume(p_csv_db, p_header, p_verbose):
942
- """Description"""
943
- success = False
944
- # if not p_csv_db:
945
- # if os.path.isfile(p_csv_file_name):
946
- # csv_file_data = csvwrpr.CsvWrpr(
947
- # self.logger_name,
948
- # p_csv_file_name=p_csv_file_name,
949
- # p_key1=p_key,
950
- # p_header=p_header,
951
- # p_del_head=p_del_head,
952
- # p_struc_type=(),
953
- # p_csv_corr_str_file_name=p_csv_corr_str_file_name,
954
- # p_replace_header=replace_header,
955
- # p_verbose=p_verbose,
956
- # p_bar_len=self.bar_len,
957
- # p_msg_width=self.msg_width,
958
- # )
959
- # csv_db = csv_file_data.csv_db
960
- if p_csv_db:
961
- import_volume(p_csv_db, p_header, p_verbose)
962
- success = True
963
- return success
964
-
965
- # end import_single_volume
966
-
967
- def import_multi_volume(p_verbose, p_header):
968
- """Description"""
969
- vol_cntr = 1
970
- success = False
971
- vol_csv_file_name = p_csv_file_name
972
- while os.path.isfile(vol_csv_file_name):
973
- # x = csvwrpr.csv
974
- csv_file_data = csvwrpr.CsvWrpr(
975
- vol_csv_file_name,
976
- p_key1=p_key,
977
- p_header=p_header,
978
- p_del_head=p_del_head,
979
- p_struc_type=(),
980
- p_csv_corr_str_file_name=p_csv_corr_str_file_name,
981
- p_replace_header=replace_header,
982
- p_verbose=p_verbose,
983
- p_msg_width=self.msg_width,
984
- p_bar_len=self.bar_len,
985
- p_match_nr_of_fields=True,
986
- )
987
- csv_db = csv_file_data.csv_db
988
- if csv_db:
989
- import_volume(csv_db, p_header, p_verbose)
990
- success = True
991
- vol_cntr += 1
992
- vol_csv_file_name = p_csv_file_name[:-4] + f"{vol_cntr:0>2}" + p_csv_file_name[-4:]
993
- if not success:
994
- log_str = f"No data to import from {vol_csv_file_name}"
995
- self.logger.warning(log_str)
996
- return success
997
-
998
- # end import_multi_volume
999
-
1000
- if p_header:
1001
- replace_header = True
1002
- else:
1003
- replace_header = False
1004
- if p_vol_type == "Single" or p_csv_db:
1005
- success = import_single_volume(p_csv_db, p_header, p_verbose)
1006
- elif p_vol_type == "Multi":
1007
- success = import_multi_volume(p_verbose, p_header)
1008
- else:
1009
- success = False
1010
- return success
1011
-
1012
- def import_and_split_csv(
1013
- self,
1014
- p_split_struct,
1015
- p_data,
1016
- p_header="",
1017
- p_insert_header=False,
1018
- p_verbose=False,
1019
- p_debug=False,
1020
- ):
1021
- """Import a csv file into a database table.
1022
-
1023
- Parameters
1024
- - p_split_struct - { 'Seq01': { 'TableName': Desttable_name1, 'Key': TableKey, 'Replace': False, 'Flds': [[ OrgField1, DestField1, [ Command, Parm1, Parm2, Parm3 ]],
1025
- [ OrgField2, DestField2, [ Command, Parm1, Parm2, Parm3 ]],
1026
- [ ... ]]},
1027
- 'Seq02': { 'TableName': Desttable_name2, 'Key': TableKey, 'Replace': False, 'Flds': [[ OrgField1, DestField1, [ Command, Parm1, Parm2, Parm3 ]],
1028
- [ OrgField2, DestField3, [ Command, Parm1, Parm2, Parm3 ]],
1029
- [ ... ]]},
1030
- ... }
1031
- - SeqNN: Any iterate sequence to indicate the various tables the csv file should be split into ( seq01, seq02, seq03, ...)
1032
- - table_name (str): Mandatory key word (in the python dict structure) to indicate the table name in the database
1033
- - Desttable_name (str): The name of the table in the database to populate
1034
- - Key (str): Mandatory key word (in the python dict structure) to indicate the primary key field of the table
1035
- - TableKey (str): Destination table primary key
1036
- - Replace (boolean): Either use REPLACE or INSERT SQL statement to add records to the table. INSERT will cause
1037
- a failure when the record to be added is a duplicate.
1038
- - Fields (str): Mandatory key word (in the python dict structure) to list the fields in the table
1039
- - OrgFieldN (str): Field name from the csv file top copy to the database table
1040
- - DestFieldN (str): Destination filed where OrgFieldN will be copied into
1041
- - Command (int): 0 = Copy OrgFieldN to DestFieldN as is
1042
- Parm1 = Truncate OrgFieldN at Parm1 if it is a string and insert into DestFieldN. 0 for no truncation. Non 'str' will not be truncated
1043
- Parm2 = True if you do not want to add the row if the result is empty, else False
1044
- Parm3 = Insert a default value if the original field matched the list.
1045
- = [ list, Def ]
1046
- 1 = Insert fixed value into DestFieldN
1047
- Parm1 = The fixed value to insert into DestFieldN
1048
- Parm2 = True if you do not want to add the row if the result is empty, else False
1049
- 2 = Split OrgFieldN by ',' and insert the n'th occurrence defined in Parm1 into DestFieldN
1050
- Parm1 = The n'th occurrence from split of OrgFieldN to insert into DestFieldN
1051
- Parm2 = True if you do not want to add the row if the result is empty, else False
1052
- 3 = Combine the "year" value in OrgFieldN with "01/01" and insert into DestFieldN
1053
- Parm1 = Date
1054
- Parm2 = True if you do not want to add the row if the result is empty, else False
1055
- 4 = Value of OrgFieldN will be looked up in a dict and inserted into DestFieldN
1056
- Parm1 = Lookup table in form of dict
1057
- Parm2 = True if you do not want to add the row if the result is empty, else False
1058
- 5 = Copy sub string from OrgFieldN into DestFieldN
1059
- Parm1 = List with start and end value to copy from OrgFieldN
1060
- Parm2 = True if you do not want to add the row if the result is empty, else False
1061
- 6 = Insert auto number into DestFieldN
1062
- Parm1 = Start with the value and add 1 with each iteration
1063
- Parm2 = True if you do not want to add the row if the result is empty, else False
1064
- - p_data
1065
- - p_header = ''
1066
- """
1067
- if isinstance(p_data, list):
1068
- csv_file_data = p_data.copy()
1069
- elif isinstance(p_data, str):
1070
- csv_file_data = csvwrpr.CsvWrpr(
1071
- self.logger_name,
1072
- p_data,
1073
- "",
1074
- p_struc_type=(),
1075
- p_header=p_header,
1076
- p_verbose=p_verbose,
1077
- p_bar_len=self.bar_len,
1078
- p_msg_width=self.msg_width,
1079
- ).csv_db
1080
- else:
1081
- csv_file_data = ()
1082
- print("Incorrect data structure")
1083
- if p_insert_header and p_header:
1084
- header = [tuple(p_header)]
1085
- csv_file_data = header + csv_file_data
1086
- for seq in p_split_struct:
1087
- table = p_split_struct[seq]["TableName"]
1088
- new_header = ()
1089
- field_list = []
1090
- field_config = []
1091
- for field in p_split_struct[seq]["Flds"]:
1092
- field_config = []
1093
- t_str = (field[1],)
1094
- new_header = new_header + t_str
1095
- if field[0] != "None":
1096
- field_config.append(csv_file_data[0].index(field[0]))
1097
- else:
1098
- field_config.append(-1)
1099
- field_config = field_config + field[2]
1100
- field_list.append(field_config)
1101
- newcsv_db = [new_header]
1102
- table_len = len(csv_file_data[1:])
1103
- if isinstance(p_data, list):
1104
- msg = bm.display(
1105
- f"Split data to {table} ({table_len})",
1106
- p_len=self.msg_width,
1107
- )
1108
- else:
1109
- msg = bm.display(
1110
- f"Split {os.path.split(p_data)[1]} to {table} ({table_len})",
1111
- p_len=self.msg_width,
1112
- )
1113
- c_field_nr = 0
1114
- c_cmd_opy = 0
1115
- c_cmd_insert = 1
1116
- c_cmd_split = 2
1117
- c_cmd_date = 3
1118
- c_cmd_look_up = 4
1119
- c_cmd_copy_sub = 5
1120
- c_cmd_auto_inc = 6
1121
- c_no_trunc = 0
1122
- c_cmd = 1
1123
- c_parm1 = 2
1124
- c_parm2 = 3
1125
- c_parm3 = 4
1126
- c_parm3_rep_str = 0
1127
- c_parm3_def_str = 1
1128
- dfx = displayfx.DisplayFx(
1129
- len(csv_file_data[1:]),
1130
- p_msg=msg,
1131
- p_verbose=False,
1132
- p_bar_len=self.bar_len,
1133
- )
1134
- for i, row in enumerate(csv_file_data[1:]):
1135
- new_row = ()
1136
- add_row = True
1137
- for field_det in field_list:
1138
- t_str = ""
1139
- if field_det[c_cmd] == c_cmd_opy: # Copy / duplicate
1140
- if field_det[c_parm1] == c_no_trunc or isinstance(row[field_det[c_field_nr]], str):
1141
- t_str = row[field_det[c_field_nr]]
1142
- else:
1143
- t_str = row[field_det[c_field_nr]][0 : field_det[c_parm1]]
1144
- if len(field_det) > 4:
1145
- if t_str in field_det[c_parm3][c_parm3_rep_str]:
1146
- t_str = field_det[c_parm3][c_parm3_def_str]
1147
- elif field_det[c_cmd] == c_cmd_insert: # Insert fixed value
1148
- t_str = field_det[c_parm1]
1149
- elif field_det[c_cmd] == c_cmd_split: # Insert fixed value from split field
1150
- if row[field_det[c_field_nr]].count(",") >= field_det[c_parm1]:
1151
- t_str = row[field_det[c_field_nr]].split(",")[field_det[c_parm1]]
1152
- else:
1153
- t_str = ""
1154
- elif field_det[c_cmd] == c_cmd_date: # Insert special value
1155
- if field_det[c_parm1] == "Date":
1156
- t_str = row[field_det[c_field_nr]] + "/01/01"
1157
- else:
1158
- print("my_sql_db: 143 - Unknown value -", field_list[1])
1159
- elif field_det[c_cmd] == c_cmd_look_up: # Replace with look up value
1160
- if row[field_det[c_field_nr]] in field_det[c_parm1]:
1161
- t_str = field_det[c_parm1][row[field_det[0]]]
1162
- elif field_det[c_cmd] == c_cmd_copy_sub: # Replace with substring from original field
1163
- t_str = row[field_det[c_field_nr]][field_det[c_parm1][0] : field_det[c_parm1][1]]
1164
- elif field_det[c_cmd] == c_cmd_auto_inc: # Insert auto number
1165
- t_str = field_det[c_parm1]
1166
- field_det[c_parm1] += 1
1167
- if isinstance(t_str, str):
1168
- t_str = t_str.strip()
1169
- new_row = new_row + (t_str,)
1170
- if field_det[c_parm2] and not t_str:
1171
- add_row = add_row and False
1172
- break
1173
- if add_row:
1174
- newcsv_db.append(new_row)
1175
- dfx.update(i)
1176
- self.import_csv(
1177
- p_table_name=table,
1178
- p_csv_db=newcsv_db,
1179
- p_header=new_header,
1180
- p_verbose=p_verbose,
1181
- p_replace=p_split_struct[seq]["Replace"],
1182
- )
1183
-
1184
- @staticmethod
1185
- def _print_err_msg(p_err, p_msg=""):
1186
- msg = p_msg
1187
- if p_msg:
1188
- msg = f"{p_msg}\n"
1189
- print(
1190
- bm.error(
1191
- "{}Err No:\t\t{}\nSQL State:\t{}\nErr Msg:\t{}\nSystem terminated...".format(
1192
- msg, p_err.errno, p_err.sqlstate, p_err.msg
1193
- )
1194
- )
1195
- )
1196
- pass
1197
-
1198
-
1199
- class MySQL(SQLDbWrpr):
1200
- """This module creates a wrapper for the MySql database."""
1201
-
1202
- def __init__(
1203
- self,
1204
- p_host_name="localhost",
1205
- p_user_name="",
1206
- p_password="",
1207
- p_user_rights=False,
1208
- p_recreate_db=False,
1209
- p_db_name=None,
1210
- p_db_structure=None,
1211
- p_batch_size=10000,
1212
- p_bar_len=50,
1213
- p_msg_width=50,
1214
- p_verbose=False,
1215
- p_admin_username=False,
1216
- p_admin_user_password=False,
1217
- p_db_port="3306",
1218
- # p_ssl_ca=None,
1219
- # p_ssl_key=None,
1220
- # p_ssl_cert=None
1221
- **kwargs,
1222
- ):
1223
- """Description"""
1224
- super().__init__(
1225
- p_host_name=p_host_name,
1226
- p_user_name=p_user_name,
1227
- p_password=p_password,
1228
- p_db_name=p_db_name,
1229
- p_recreate_db=p_recreate_db,
1230
- p_db_structure=p_db_structure,
1231
- p_batch_size=p_batch_size,
1232
- p_bar_len=p_bar_len,
1233
- p_msg_width=p_msg_width,
1234
- p_verbose=p_verbose,
1235
- p_db_port=p_db_port,
1236
- # p_ssl_ca=p_ssl_ca,
1237
- # p_ssl_key=p_ssl_key,
1238
- # p_ssl_cert=p_ssl_cert,
1239
- )
1240
- try:
1241
- # import pdb;pdb.set_trace()
1242
- self.conn = mysql.connector.connect(
1243
- host=self.host_name,
1244
- user=self.user_name,
1245
- password=self._password,
1246
- database=None,
1247
- auth_plugin="mysql_native_password",
1248
- port=self.db_port,
1249
- # ssl_ca=self.ssl_ca,
1250
- # ssl_key=self.ssl_key,
1251
- # ssl_cert=self.ssl_cert
1252
- **kwargs,
1253
- )
1254
- self.cur = self.conn.cursor()
1255
- except mysql.connector.Error as err:
1256
- print(
1257
- bm.error(
1258
- f"Error {err}:'({self.user_name}'@'{self.host_name}')",
1259
- )
1260
- )
1261
- if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
1262
- print(bm.error(f"User '{self.user_name}'@'{self.host_name}' does not exist\nAtempt to create it..."))
1263
- if p_admin_username and p_admin_user_password and p_user_rights:
1264
- try:
1265
- self.conn = mysql.connector.connect(
1266
- host=self.host_name,
1267
- user=p_admin_username,
1268
- password=p_admin_user_password,
1269
- database=None,
1270
- auth_plugin="mysql_native_password",
1271
- port=self.db_port,
1272
- )
1273
- except mysql.connector.Error as err:
1274
- self._print_err_msg(
1275
- err,
1276
- "Admin user name and/or password not supplied or incorrect",
1277
- )
1278
- if self.conn.is_connected():
1279
- self.cur = self.conn.cursor()
1280
- self.create_users(
1281
- [p_admin_username, p_admin_user_password],
1282
- [[p_user_name, p_password]],
1283
- )
1284
- self.grant_rights([p_admin_username, p_admin_user_password], [p_user_rights])
1285
- else:
1286
- print(bm.error("Could not connect\nSystem terminated"))
1287
- sys.exit()
1288
- else:
1289
- self._print_err_msg(
1290
- err,
1291
- "User name and/or password and/or user access rights not supplied or incorrect",
1292
- )
1293
- sys.exit()
1294
- self.close()
1295
- if not self.conn.is_connected():
1296
- self.conn = mysql.connector.connect(
1297
- host=self.host_name,
1298
- user=self.user_name,
1299
- password=self._password,
1300
- database=None,
1301
- auth_plugin="mysql_native_password",
1302
- )
1303
- self.cur = self.conn.cursor()
1304
- if self.re_create_db:
1305
- if self.create_db():
1306
- self.create_tables()
1307
- elif self.db_name:
1308
- self.conn.cmd_init_db(self.db_name)
1309
- self.conn.commit()
1310
- self.success = True
1311
- pass
1312
-
1313
-
1314
- class MSSQL(SQLDbWrpr):
1315
- """This module creates a wrapper for the MySql database."""
1316
-
1317
- def __init__(
1318
- self,
1319
- p_host_name="localhost",
1320
- p_user_name="",
1321
- p_password="",
1322
- p_recreate_db=False,
1323
- p_db_name=None,
1324
- p_db_structure=None,
1325
- p_batch_size=10000,
1326
- p_bar_len=50,
1327
- p_msg_width=50,
1328
- p_verbose=False,
1329
- ):
1330
- """Description"""
1331
- super().__init__(
1332
- p_host_name=p_host_name,
1333
- p_user_name=p_user_name,
1334
- p_password=p_password,
1335
- p_db_name=p_db_name,
1336
- p_recreate_db=p_recreate_db,
1337
- p_db_structure=p_db_structure,
1338
- p_batch_size=p_batch_size,
1339
- p_bar_len=p_bar_len,
1340
- p_msg_width=p_msg_width,
1341
- p_verbose=p_verbose,
1342
- )
1343
- try:
1344
- self.host_name = "156.38.224.15,1433"
1345
- self.user_name = "chessaco_chessanew"
1346
- self._password = "@Jv&F77%"
1347
- self.db_name = "chessaco_analytics"
1348
- self.driver = "{ODBC Driver 17 for SQL Server}"
1349
- # driver = pyodbc.drivers()
1350
- # con_str_1 = 'DRIVER={};SERVER={};DATABASE={};UID={};PWD={}'.format( self.driver, self.host_name, self.db_name, self.user_name, self._password )
1351
- con_str_2 = (
1352
- "DRIVER={ODBC Driver 17 for SQL Server};SERVER="
1353
- + self.host_name
1354
- + ";DATABASE="
1355
- + self.db_name
1356
- + ";UID="
1357
- + self.user_name
1358
- + ";PWD="
1359
- + self._password
1360
- )
1361
- self.conn = pyodbc.connect(con_str_2)
1362
- pass
1363
- except Error as err:
1364
- self.logger.error(err)
1365
- self.success = self.conn.is_connected()
1366
- if self.conn.is_connected():
1367
- self.cur = self.conn.cursor()
1368
- if self.re_create_db:
1369
- self.create_db()
1370
- self.success = self.create_tables()
1
+ """Wrapper for importing CSV and Text files into MySQL and Postgresql.
2
+
3
+ Only the MySQL implementation is working. The MSSQL ODBC implementation
4
+ is giving me a lot of shit with the connection and I gave up for now to
5
+ get it working.
6
+ """
7
+
8
+ import datetime
9
+ import logging
10
+ import os
11
+ import sys
12
+
13
+ import csvwrpr
14
+ import displayfx
15
+ import fixdate
16
+ import mysql.connector
17
+ import pyodbc
18
+ from beetools import msg as bm
19
+ from mysql.connector import Error
20
+ from mysql.connector import errorcode
21
+
22
+ # from pathlib import Path
23
+
24
+
25
+ # _PROJ_DESC = __doc__.split("\n")[0]
26
+ # _PROJ_PATH = Path(__file__)
27
+ # _PROJ_NAME = _PROJ_PATH.stem
28
+
29
+
30
+ class SQLDbWrpr:
31
+ """This module creates a wrapper for the MySql database."""
32
+
33
+ def __init__(
34
+ self,
35
+ p_host_name="localhost",
36
+ p_user_name="",
37
+ p_password="",
38
+ p_recreate_db=False,
39
+ p_db_name="",
40
+ p_db_structure=None,
41
+ p_batch_size=10000,
42
+ p_bar_len=50,
43
+ p_msg_width=50,
44
+ p_verbose=False,
45
+ p_db_port="3306",
46
+ p_ssl_ca=None,
47
+ p_ssl_key=None,
48
+ p_ssl_cert=None,
49
+ ):
50
+ """Create database with supplied structure and return a connector to the database
51
+
52
+ Parameters
53
+ - p_host_name = Host to connect to
54
+ - p_user_name = User name for connection
55
+ - p_password = paswword of user
56
+ - ReCreate = Recresate the database or connect to existing database
57
+ - db_name =
58
+ - table_details: Details of the tables to be created
59
+ - batch_size: Bulk data will be managed by batch_size to commit
60
+ - p_bar_len: Length for the progress bar
61
+ - p_msg_width: Width of message before progress bar
62
+ """
63
+ self.logger_name = __name__
64
+ self.logger = logging.getLogger(self.logger_name)
65
+ self.logger.info("Start")
66
+ self.success = False
67
+ self.bar_len = p_bar_len
68
+ self.batch_size = p_batch_size
69
+ self.char_fields = {}
70
+ self.conn = None
71
+ self.cur = None
72
+ self.db_name = p_db_name
73
+ if p_db_structure:
74
+ self.db_structure = p_db_structure
75
+ else:
76
+ self.db_structure = {}
77
+ self.delimiter = ","
78
+ self.fkey_ref_act = {
79
+ "C": "CASCADE",
80
+ "R": "RESTRICT",
81
+ "D": "SET DEFAULT",
82
+ "N": "SET NULL",
83
+ }
84
+ self.host_name = p_host_name
85
+ self.idx_type = {"U": "UNIQUE", "F": "FULLTEXT", "S": "SPATIAL"}
86
+ self.msg_width = p_msg_width
87
+ self.non_char_fields = {}
88
+ self._password = p_password
89
+ self.re_create_db = p_recreate_db
90
+ self.silent = p_verbose
91
+ self.sort_order = {"A": "ASC", "D": "DESC"}
92
+ self.table_load_order = []
93
+ self.user_name = p_user_name
94
+ self.get_db_field_types()
95
+ self.db_port = p_db_port
96
+
97
+ def close(self):
98
+ """Close the connention"""
99
+ if self.conn:
100
+ self.conn.close()
101
+
102
+ def create_db(self):
103
+ """Create the database according to self.db_structure."""
104
+ self.cur.execute("SHOW DATABASES")
105
+ db_res = [x[0].decode() if isinstance(x[0], (bytearray, bytes)) else str(x[0]) for x in self.cur.fetchall()]
106
+ # if self.db_name.lower() in db_res:
107
+ if self.db_name in db_res:
108
+ try:
109
+ self.cur.execute(f"DROP DATABASE {self.db_name}")
110
+ self.conn.commit()
111
+ except mysql.connector.Error as err:
112
+ self._print_err_msg(err, "Could not drop the database")
113
+ self.close()
114
+ sys.exit()
115
+ try:
116
+ self.cur.execute(f'CREATE DATABASE {self.db_name} DEFAULT CHARACTER SET "utf8"')
117
+ self.conn.commit()
118
+ self.cur.execute(f"USE {self.db_name}")
119
+ self.conn.commit()
120
+ except mysql.connector.Error as err:
121
+ self._print_err_msg(err, "Could not create the database")
122
+ self.close()
123
+ sys.exit()
124
+ return True
125
+
126
+ def create_tables(self):
127
+ """Create db tables from MySQL.table_details dict"""
128
+
129
+ def build_db(p_db_sql_str_set):
130
+ """Description"""
131
+ for sql_set in p_db_sql_str_set:
132
+ try:
133
+ self.cur.execute(sql_set[1])
134
+ if self.silent:
135
+ print(f"Created table = {sql_set[0]}")
136
+ except mysql.connector.Error as err:
137
+ print(f"Failed creating table = {sql_set[0]}: {err}\nForced termination of program")
138
+ print(f"{sql_set[1]}")
139
+ sys.exit()
140
+ pass
141
+
142
+ # end build_db
143
+
144
+ def generate_db_sql(
145
+ p_table_set_up_str,
146
+ p_primary_key_str,
147
+ p_idx_set_up_list,
148
+ p_constraint_set_up_list,
149
+ ):
150
+ """Description"""
151
+ table_set_up_str = p_table_set_up_str
152
+ table_set_up_str += p_primary_key_str
153
+ for idx_str in p_idx_set_up_list:
154
+ table_set_up_str += idx_str
155
+ for constraint_str in p_constraint_set_up_list:
156
+ table_set_up_str += constraint_str[2]
157
+ table_set_up_str = f"{table_set_up_str[:-2]})"
158
+ return table_set_up_str
159
+
160
+ # end generate_db_sql
161
+
162
+ def build_constraints(p_table_name):
163
+ # noinspection PySingleQuotedDocstring
164
+ """Description"""
165
+ constraint_list = []
166
+ fkey_nr_list = []
167
+ for field_name in self.db_structure[p_table_name]:
168
+ fkey = get_foreign_key(p_table_name, field_name)
169
+ if fkey["Present"]:
170
+ fkey_nr_list.append(fkey["ForeignKeyNr"])
171
+ # fkey_PROJ_NAME = 'fk_{}_{}'.format( fkey[ 'FKeyTable' ], fkey[ 'RefTable' ])
172
+ fkey_str = (
173
+ "CONSTRAINT fk_{}_{} FOREIGN KEY ({}) REFERENCES {} ({}) ON DELETE {} ON UPDATE {}, ".format(
174
+ fkey["FKeyTable"],
175
+ fkey["RefTable"],
176
+ ".".join(fkey["FKeyFlds"]),
177
+ fkey["RefTable"],
178
+ ".".join(fkey["RefFields"]),
179
+ self.fkey_ref_act[fkey["OnDelete"]],
180
+ self.fkey_ref_act[fkey["OnUpdate"]],
181
+ )
182
+ )
183
+ constraint_list.append([fkey["FKeyTable"], fkey["RefTable"], fkey_str])
184
+ pass
185
+ return constraint_list
186
+
187
+ # def build_constraints
188
+
189
+ def build_all_indexes(p_table_name):
190
+ """Description"""
191
+
192
+ # def build_primary_key_idx(p_table_name):
193
+ # '''Description'''
194
+ # idx_name_list = []
195
+ # idx_str_list = []
196
+ # pkey = get_primary_key(p_table_name)
197
+ # idx_name = '{}_UNIQUE'.format('_'.join(pkey['Flds']))
198
+ # idx_name_list.append(idx_name)
199
+ # idx_str = 'UNIQUE INDEX pk_{} ({}) VISIBLE, '.format(
200
+ # idx_name, ','.join(pkey['Flds'])
201
+ # )
202
+ # idx_str_list.append(idx_str)
203
+ # return idx_str_list, idx_name_list
204
+ #
205
+ # # end build_primary_key_idx
206
+
207
+ def build_unique_key_idx(p_table_name, p_dx_name_list, p_idx_str_list):
208
+ """Description"""
209
+ idx_list = {}
210
+ idx_name_list = p_dx_name_list
211
+ idx_str_list = p_idx_str_list
212
+ for field_name in self.db_structure[p_table_name]:
213
+ field_param_st_ref = self.db_structure[p_table_name][field_name]["Params"]
214
+ if field_param_st_ref["Index"]:
215
+ if field_param_st_ref["Index"][0] not in idx_list:
216
+ idx_list[field_param_st_ref["Index"][0]] = [[field_name] + field_param_st_ref["Index"][1:]]
217
+ else:
218
+ idx_list[field_param_st_ref["Index"][0]].append(
219
+ [field_name] + field_param_st_ref["Index"][1:]
220
+ )
221
+ for idx_instance in idx_list:
222
+ idx_instance_order = sorted(idx_list[idx_instance], key=lambda x: x[1])
223
+ idx_name = ""
224
+ for field_det in idx_instance_order:
225
+ idx_name += f"{field_det[0]}_"
226
+ if field_det[3] == "U":
227
+ idx_name = f"unq_{idx_name[:-1]}"
228
+ else:
229
+ idx_name = f"idx_{idx_name[:-1]}"
230
+ if idx_name not in idx_name_list:
231
+ idx_name_list.append(idx_name)
232
+ if field_det[3] == "U":
233
+ idx_str = f"{self.idx_type[field_det[3]]} INDEX {idx_name} ("
234
+ else:
235
+ idx_str = f"INDEX {idx_name} ("
236
+ for field_det in idx_instance_order:
237
+ idx_str += f"{field_det[0]} {self.sort_order[field_det[2]]}, "
238
+ idx_str = idx_str[:-2] + ") VISIBLE, "
239
+ idx_str_list.append(idx_str)
240
+ return idx_str_list, idx_name_list
241
+
242
+ # end build_unique_key_idx
243
+
244
+ idx_name_list = []
245
+ idx_list = []
246
+ idx_list, idx_name_list = build_unique_key_idx(p_table_name, idx_name_list, idx_list)
247
+ return idx_list
248
+
249
+ # def build_all_indexes
250
+
251
+ def build_primary_key_sql_str(p_table_name):
252
+ """Description"""
253
+ primary_key_det = get_primary_key(p_table_name)
254
+ sql_str = "PRIMARY KEY ({}), ".format(",".join(primary_key_det["Flds"]))
255
+ return sql_str
256
+
257
+ # def build_primary_key_sql_str
258
+
259
+ def build_table_sql_str(p_table_name):
260
+ """Description"""
261
+ sql_str = f"CREATE TABLE {p_table_name} ("
262
+ for field_name in self.db_structure[p_table_name]:
263
+ field_type_st_ref = self.db_structure[p_table_name][field_name]["Type"]
264
+ field_param_st_ref = self.db_structure[p_table_name][field_name]["Params"]
265
+ field_comment_st_ref = self.db_structure[p_table_name][field_name]["Comment"]
266
+ sql_str += f"{field_name} {field_type_st_ref[0]}"
267
+ if field_type_st_ref[0] == "varchar" or field_type_st_ref[0] == "char":
268
+ sql_str += f" ({str(field_type_st_ref[1])})"
269
+ elif field_type_st_ref[0] == "decimal":
270
+ sql_str += f"({str(field_type_st_ref[1])}, {str(field_type_st_ref[2])})"
271
+ if field_param_st_ref["AI"] == "Y":
272
+ sql_str += " AUTO_INCREMENT"
273
+ if field_param_st_ref["UN"] == "Y" and field_param_st_ref["AI"] != "Y":
274
+ sql_str += " UNSIGNED"
275
+ if field_param_st_ref["NN"] == "Y":
276
+ sql_str += " NOT NULL"
277
+ if field_param_st_ref["ZF"] == "Y":
278
+ sql_str += " ZEROFILL"
279
+ if field_param_st_ref["DEF"]:
280
+ if field_type_st_ref[0] == "varchar" or field_type_st_ref[0] == "char":
281
+ sql_str += ' DEFAULT "{}"'.format(field_param_st_ref["DEF"])
282
+ else:
283
+ sql_str += " DEFAULT {}".format(field_param_st_ref["DEF"])
284
+ if field_comment_st_ref:
285
+ sql_str += f' COMMENT "{field_comment_st_ref}"'
286
+ sql_str += ", "
287
+ return sql_str
288
+
289
+ # end build_table_sql_str
290
+
291
+ def get_foreign_key(p_table_name, p_field_name):
292
+ """Description"""
293
+ fkey = {
294
+ "Present": False,
295
+ "FKeyFlds": [],
296
+ "RefFields": [],
297
+ "FKeyTable": "",
298
+ "RefTable": "",
299
+ "ForeignKeyNr": False,
300
+ "OnDelete": "N",
301
+ "OnUpdate": "N",
302
+ }
303
+ fkey_source = self.db_structure[p_table_name][p_field_name]["Params"]["FKey"]
304
+ if fkey_source:
305
+ table_det = self.db_structure[p_table_name]
306
+ fkey["ForeignKeyNr"] = fkey_source[0]
307
+ fkey["FKeyTable"] = p_table_name
308
+ fkey["RefTable"] = fkey_source[2]
309
+ ref_field_pair_list = []
310
+ for field in table_det:
311
+ if table_det[field]["Params"]["FKey"]:
312
+ if table_det[field]["Params"]["FKey"][0] == fkey["ForeignKeyNr"]:
313
+ ref_field_pair_list.append(
314
+ [
315
+ field,
316
+ table_det[field]["Params"]["FKey"][3],
317
+ table_det[field]["Params"]["FKey"][1],
318
+ ]
319
+ )
320
+ ref_field_pair_list = sorted(ref_field_pair_list, key=lambda x: x[2])
321
+ fkey["FKeyFlds"], fkey["RefFields"], t_order = zip(*ref_field_pair_list)
322
+ fkey["OnDelete"] = fkey_source[4]
323
+ fkey["OnUpdate"] = fkey_source[5]
324
+ fkey["Present"] = True
325
+ return fkey
326
+
327
+ # end get_foreign_key
328
+
329
+ def get_primary_key(p_table_name):
330
+ """Description"""
331
+ pkey = {"Present": False, "Flds": (), "SortPairList": [], "SortPairStr": []}
332
+ for field_name in self.db_structure[p_table_name]:
333
+ pkey_field_det = self.db_structure[p_table_name][field_name]
334
+ if pkey_field_det["Params"]["PrimaryKey"][0] == "Y":
335
+ pkey["Flds"] += (field_name,)
336
+ pkey["SortPairList"].append(
337
+ (
338
+ field_name,
339
+ self.sort_order[pkey_field_det["Params"]["PrimaryKey"][1]],
340
+ )
341
+ )
342
+ pkey["SortPairStr"].append(
343
+ "{} {}".format(
344
+ field_name,
345
+ self.sort_order[pkey_field_det["Params"]["PrimaryKey"][1]],
346
+ )
347
+ )
348
+ pkey["Present"] = True
349
+ return pkey
350
+
351
+ # end get_primary_key
352
+
353
+ def order_table_build_list(p_db_sql_str_set, p_constraint_set_up_list):
354
+ """Description"""
355
+ db_sql_str_set = p_db_sql_str_set
356
+ ordered = False
357
+ while not ordered:
358
+ ordered = True
359
+ for constraint in p_constraint_set_up_list:
360
+ fkey_pos_found = False
361
+ i = 0
362
+ fkey_pos = -1
363
+ while not fkey_pos_found:
364
+ if db_sql_str_set[i][0] == constraint[1]:
365
+ fkey_pos_found = True
366
+ fkey_pos = i
367
+ else:
368
+ i += 1
369
+ table_pos_found = False
370
+ i = 0
371
+ tbl_pos = -1
372
+ while not table_pos_found:
373
+ if db_sql_str_set[i][0] == constraint[0]:
374
+ table_pos_found = True
375
+ tbl_pos = i
376
+ else:
377
+ i += 1
378
+ if tbl_pos < fkey_pos:
379
+ db_sql_str_set.insert(fkey_pos + 1, db_sql_str_set[tbl_pos])
380
+ del db_sql_str_set[tbl_pos]
381
+ ordered = False
382
+ self.table_load_order = [x[0] for x in db_sql_str_set]
383
+ return db_sql_str_set
384
+
385
+ # end order_table_build_list
386
+
387
+ def structure_validation():
388
+ """Description"""
389
+
390
+ def check_pkey_fkey_overlap(p_remove_fkey_pkey__overlap=True):
391
+ """Description"""
392
+
393
+ def partial_overlap(p_fkey, p_pkey):
394
+ """Description"""
395
+ is_overlap = False
396
+ for field_name in p_fkey["FKeyFlds"]:
397
+ if field_name in p_pkey["Flds"]:
398
+ is_overlap = True
399
+ return is_overlap
400
+
401
+ # end partial_overlap
402
+
403
+ def remove_fkey(p_fkey):
404
+ """Description"""
405
+ for field_name in self.db_structure[p_fkey["FKeyTable"]]:
406
+ if self.db_structure[p_fkey["FKeyTable"]][field_name]["Params"]["FKey"]:
407
+ if (
408
+ self.db_structure[p_fkey["FKeyTable"]][field_name]["Params"]["FKey"][0]
409
+ == p_fkey["ForeignKeyNr"]
410
+ ):
411
+ self.db_structure[p_fkey["FKeyTable"]][field_name]["Params"]["FKey"] = []
412
+ pass
413
+
414
+ # end remove_fkey
415
+
416
+ for table_name in self.db_structure:
417
+ pkey = get_primary_key(table_name)
418
+ source_table = self.db_structure[table_name]
419
+ for field_name in source_table:
420
+ fkey = get_foreign_key(table_name, field_name)
421
+ if fkey["Present"]:
422
+ if pkey["Flds"] != fkey["FKeyFlds"] and partial_overlap(fkey, pkey):
423
+ log_str = "The foreign key {}.{} and the primary key in {}.{} overlaps.".format(
424
+ fkey["FKeyTable"],
425
+ fkey["FKeyFlds"],
426
+ table_name,
427
+ pkey["Flds"],
428
+ )
429
+ self.logger.warning(log_str)
430
+ if p_remove_fkey_pkey__overlap:
431
+ remove_fkey(fkey)
432
+ log_str = 'Current settings forced removed the foreign key "{}.{}"'.format(
433
+ fkey["FKeyTable"], fkey["FKeyFlds"]
434
+ )
435
+ self.logger.warning(log_str)
436
+ else:
437
+ log_str = "This may cause a problem adding record to either {} or {}".format(
438
+ fkey["FKeyTable"], table_name
439
+ )
440
+ self.logger.warning(log_str)
441
+ pass
442
+ pass
443
+
444
+ # end check_pkey_ukey_overlap
445
+ check_pkey_fkey_overlap()
446
+ pass
447
+
448
+ # end structure_validation()
449
+
450
+ success = True
451
+ structure_validation()
452
+ table_set_up_list = ""
453
+ idx_set_up_list = []
454
+ constraint_set_up_list = []
455
+ db_sql_str_set = []
456
+ for table_name in self.db_structure:
457
+ table_set_up_list = build_table_sql_str(table_name)
458
+ primary_key_str = build_primary_key_sql_str(table_name)
459
+ idx_set_up_list = build_all_indexes(table_name)
460
+ tblconstraint_list = build_constraints(table_name)
461
+ db_sql_str_set.append(
462
+ [
463
+ table_name,
464
+ generate_db_sql(
465
+ table_set_up_list,
466
+ primary_key_str,
467
+ idx_set_up_list,
468
+ tblconstraint_list,
469
+ ),
470
+ ]
471
+ )
472
+ if tblconstraint_list:
473
+ constraint_set_up_list += tblconstraint_list
474
+ pass
475
+ db_sql_str_set = order_table_build_list(db_sql_str_set, constraint_set_up_list)
476
+ build_db(db_sql_str_set)
477
+ return success
478
+
479
+ def create_users(self, p_admin_user, p_new_users):
480
+ c_user_name = 0
481
+ self.cur.execute("SELECT User, Host FROM mysql.user")
482
+ curr_users = self.cur.fetchall()
483
+ for user in p_new_users:
484
+ if not user[c_user_name] in curr_users:
485
+ try:
486
+ self.cur.execute(
487
+ "CREATE USER IF NOT EXISTS '{}'@'{}' IDENTIFIED BY '{}'".format(
488
+ user[0], self.host_name, user[1]
489
+ )
490
+ )
491
+ except mysql.connector.Error as err:
492
+ self._print_err_msg(err, "Could not create user")
493
+ self.close()
494
+ sys.exit()
495
+ self.conn.commit()
496
+ self.success = True
497
+
498
+ def delete_users(self, p_admin_user, p_del_users):
499
+ c_user_name = 0
500
+ # c_password = 1
501
+ c_host = 2
502
+ self.cur.execute("SELECT User FROM mysql.user")
503
+ curr_users = [x[0] for x in self.cur.fetchall()]
504
+ for user in p_del_users:
505
+ if user[c_user_name] in curr_users:
506
+ try:
507
+ self.cur.execute(f"DROP USER '{user[c_user_name]}'@'{user[c_host]}'")
508
+ except mysql.connector.Error as err:
509
+ self._print_err_msg(err, "Could not delete user")
510
+ self.close()
511
+ sys.exit()
512
+ self.success = True
513
+
514
+ def _err_broken_rec(self, p_sql_str, p_csv_db_slice):
515
+ """Write broken record to logger"""
516
+ # self.logger.critical( p_err )
517
+ for row in p_csv_db_slice:
518
+ try:
519
+ self.cur.execute(p_sql_str, row)
520
+ except Exception:
521
+ self.logger.warning(f"{p_sql_str}\n{row}\nForced program termination")
522
+ sys.exit()
523
+ else:
524
+ self.conn.commit()
525
+ pass
526
+ pass
527
+
528
+ def export_to_csv(
529
+ self,
530
+ p_csv_path,
531
+ p_table_name,
532
+ p_delimeter="|",
533
+ p_strip_chars="",
534
+ p__vol_size=0,
535
+ p_sql_query="",
536
+ ):
537
+ """Export a table to a csv file
538
+
539
+ Parameters
540
+ - p_csv_path - Path name of the file to be exported
541
+ - p_table_name = '' - Table name to export
542
+ - p_delimeter = '|' - Field delimiter to use
543
+ - p_strip_chars = '' - characters to strip from text
544
+ - p__vol_size = 0 - Create a multiple volume export. p__vol_size is
545
+ the number of records per file. 0 will create
546
+ only one volume.
547
+ """
548
+
549
+ def multi_volume_export(p_csv_path, p__vol_size):
550
+ """Create multiple volumes in path with p__vol_size records
551
+
552
+ Parameters
553
+ - p_csv_path - Path name of the file to be exported
554
+ - p__vol_size = 0 - Create a multiple volume export. p__vol_size is
555
+ the number of records per file. 0 will create
556
+ only one volume.
557
+ """
558
+ file_name_list = []
559
+ header = p_delimeter.join(self.db_structure[p_table_name])
560
+ prim_key_sql_str = "SELECT "
561
+ all_sql_str = "SELECT " + header.replace(p_delimeter, ",") + " FROM " + p_table_name + " WHERE "
562
+ for i, field in enumerate(self.db_structure[p_table_name]):
563
+ if self.db_structure[p_table_name][field]["Params"]["PrimaryKey"][0] == "Y":
564
+ prim_key_sql_str += field + ", "
565
+ all_sql_str += field + " = %s and "
566
+ prim_key_sql_str = prim_key_sql_str[:-2] + " FROM " + p_table_name
567
+ all_sql_str = all_sql_str[:-5]
568
+ print(f"Collecting {p_table_name} table records")
569
+ self.cur.execute(prim_key_sql_str)
570
+ prim_key_res = self.cur.fetchall()
571
+ vol_cntr = 1
572
+ # curr_vol_size = p__vol_size
573
+ list_len = len(prim_key_res)
574
+ msg = bm.display(
575
+ f"Export records table = {p_table_name} ({list_len})",
576
+ p_len=self.msg_width,
577
+ )
578
+ rec_cntr = 0
579
+ pfx = displayfx.DisplayFx(list_len, p_msg=msg, p_bar_len=self.bar_len)
580
+ csv_file = None
581
+ for i, pkeys_rec in enumerate(prim_key_res):
582
+ if rec_cntr == 0:
583
+ if rec_cntr == 0 and vol_cntr > 1:
584
+ csv_file.close()
585
+ # if list_len - ((vol_cntr - 1) * p__vol_size) < p__vol_size:
586
+ # curr_vol_size = list_len - ((vol_cntr - 1) * p__vol_size)
587
+ if vol_cntr == 1:
588
+ csv_vol_path = p_csv_path
589
+ else:
590
+ csv_vol_path = p_csv_path[:-4] + f"{vol_cntr:0>2}" + p_csv_path[-4:]
591
+ file_name_list.append(os.path.split(csv_vol_path))
592
+ csv_file = open(csv_vol_path, "w+")
593
+ csv_file.write(header + "\n")
594
+ self.cur.execute(all_sql_str, pkeys_rec)
595
+ row_res = self.cur.fetchall()[0]
596
+ csv_row = ""
597
+ for j, field in enumerate(row_res):
598
+ if field is None:
599
+ field = "NULL"
600
+ if j in self.char_fields[p_table_name]:
601
+ csv_row += '"' + str(field) + '"' + p_delimeter
602
+ else:
603
+ csv_row += str(field) + p_delimeter
604
+ for char in p_strip_chars:
605
+ csv_row.replace(char, "")
606
+ csv_file.write(csv_row[:-1] + "\n")
607
+ if rec_cntr == p__vol_size:
608
+ rec_cntr = 0
609
+ vol_cntr += 1
610
+ else:
611
+ rec_cntr += 1
612
+ pfx.update(i)
613
+ csv_file.close()
614
+ return file_name_list
615
+
616
+ # end multi_volume_export
617
+
618
+ def single_volume_export(p_csv_path, p_sql_query):
619
+ """Create single volume in path with p__vol_size records
620
+
621
+ Parameters
622
+ - p_csv_path - Path name of the file to be exported
623
+ """
624
+ header = ""
625
+ file_name_list = []
626
+ file_name_list.append(os.path.split(p_csv_path))
627
+ if not p_sql_query:
628
+ header = p_delimeter.join(self.db_structure[p_table_name])
629
+ sql_str = "SELECT " + header.replace(p_delimeter, ",") + " FROM " + p_table_name
630
+ else:
631
+ header = p_delimeter.join(p_sql_query[0])
632
+ sql_str = p_sql_query[1]
633
+ csv_file = open(p_csv_path, "w+")
634
+ csv_file.write(header + "\n")
635
+ print(f"Collecting {p_table_name} table records")
636
+ self.cur.execute(sql_str)
637
+ table_res = self.cur.fetchall()
638
+ # cntr = 0
639
+ list_len = len(table_res)
640
+ msg = bm.display(
641
+ f"Export records table = {p_table_name} ({list_len})",
642
+ p_len=self.msg_width,
643
+ )
644
+ dfx = displayfx.DisplayFx(list_len, p_msg=msg, p_bar_len=self.bar_len)
645
+ for i, row in enumerate(table_res):
646
+ csv_row = ""
647
+ for j, field in enumerate(row):
648
+ # if not field:
649
+ if field is None:
650
+ field = "NULL"
651
+ if j in self.char_fields[p_table_name]:
652
+ csv_row += '"' + str(field) + '"' + p_delimeter
653
+ else:
654
+ csv_row += str(field) + p_delimeter
655
+ for char in p_strip_chars:
656
+ csv_row.replace(char, "")
657
+ csv_file.write(csv_row[:-1] + "\n")
658
+ dfx.update(i)
659
+ csv_file.close()
660
+ return file_name_list
661
+
662
+ # end single_volume_export
663
+
664
+ file_name_list = None
665
+ try:
666
+ self.cur.execute("SELECT COUNT(*) FROM " + p_table_name)
667
+ except mysql.connector.Error as err:
668
+ print(f"Err mesg: {err.msg}")
669
+ print(err.msg)
670
+ else:
671
+ count_rec_res = self.cur.fetchall()[0][0]
672
+ if p__vol_size > 0 and count_rec_res > p__vol_size and not p_sql_query:
673
+ file_name_list = multi_volume_export(p_csv_path, p__vol_size)
674
+ else:
675
+ file_name_list = single_volume_export(p_csv_path, p_sql_query)
676
+ # success = True
677
+ return file_name_list
678
+
679
+ def get_db_field_types(self):
680
+ """Description"""
681
+ for p_table_name in self.db_structure:
682
+ self.char_fields[p_table_name] = []
683
+ self.non_char_fields[p_table_name] = []
684
+ for field in self.db_structure[p_table_name]:
685
+ if (
686
+ self.db_structure[p_table_name][field]["Type"][0] == "char"
687
+ or self.db_structure[p_table_name][field]["Type"][0] == "varchar"
688
+ ):
689
+ self.char_fields[p_table_name].append(field)
690
+ else:
691
+ self.non_char_fields[p_table_name].append(field)
692
+
693
+ def grant_rights(self, p_admin_user, p_user_rights):
694
+ c_user_name = 0
695
+ # c_password = 1
696
+ c_host = 1
697
+ c_db = 2
698
+ c_table = 3
699
+ c_rights = 4
700
+ # success = True
701
+ for right in p_user_rights:
702
+ try:
703
+ sql_str = "GRANT {} ON {}.{} TO '{}'@'{}'".format(
704
+ ",".join(right[c_rights:]),
705
+ right[c_db],
706
+ right[c_table],
707
+ right[c_user_name],
708
+ right[c_host],
709
+ )
710
+ self.cur.execute(sql_str)
711
+ self.conn.commit()
712
+ sql_str = "GRANT {} ON {}.{} TO '{}'@'{}' WITH GRANT OPTION".format(
713
+ ",".join(right[c_rights:]),
714
+ right[c_db],
715
+ right[c_table],
716
+ right[c_user_name],
717
+ right[c_host],
718
+ )
719
+ self.cur.execute(sql_str)
720
+ self.conn.commit()
721
+ except mysql.connector.Error as err:
722
+ self._print_err_msg(err)
723
+ self.close()
724
+ sys.exit()
725
+ self.success = True
726
+
727
+ def import_csv(
728
+ self,
729
+ p_table_name,
730
+ p_csv_file_name="",
731
+ p_key="",
732
+ p_header="",
733
+ p_del_head=False,
734
+ p_csv_db="",
735
+ p_csv_corr_str_file_name="",
736
+ p_vol_type="Multi",
737
+ p_verbose=False,
738
+ p_replace=False,
739
+ ):
740
+ """Import a csv file into a database table.
741
+
742
+ Parameters
743
+ - p_table_name
744
+ Table name to import the csv data into
745
+ - p_csv_file_name = ''
746
+ Csv file name. Empty if structure contained in p_csv_db
747
+ - p_key = ''
748
+ Key used to insert in table
749
+ - p_header = ''
750
+ - Header of csv files
751
+ - p_del_head = ''
752
+ - Delete the header
753
+ - p_csv_db = ''
754
+ - Contains the csv table in a structure and makes p_csv_file_name obsolete.
755
+ - p_csv_corr_str_file_name = ''
756
+ - String that contains any strings that should be replace in the csv
757
+ file before parsing
758
+ - p_vol_type = 'Multi'
759
+ - Multi - Read multiple volume
760
+ - Single - Read single file
761
+ - p_verbose = False
762
+ - Determine if there are any output to screen
763
+ - debug = False
764
+ - Switch debug on
765
+ - p_replace = False
766
+ - False - INSERT into database
767
+ - True - REPLACE into database
768
+ """
769
+
770
+ def import_volume(p_csv_db, p_header, p_verbose):
771
+ """Description"""
772
+
773
+ def convert_str_to_none(p_non_char_fields_idx, p_csv_db):
774
+ """Description"""
775
+ rows_to_del = []
776
+ csv_db = p_csv_db
777
+ list_len = len(csv_db)
778
+ msg = bm.display(
779
+ f"Convert empty strings to None ({list_len})",
780
+ p_len=self.msg_width,
781
+ )
782
+ dfx = displayfx.DisplayFx(
783
+ list_len,
784
+ p_msg=msg,
785
+ p_verbose=p_verbose,
786
+ p_bar_len=self.bar_len,
787
+ )
788
+ for row_idx, row in enumerate(csv_db):
789
+ found_none = False
790
+ t_tow = list(csv_db[row_idx])
791
+ for field in p_non_char_fields_idx:
792
+ if t_tow[field] == "":
793
+ t_tow[field] = None
794
+ found_none = True
795
+ if found_none:
796
+ csv_db.append(tuple(t_tow))
797
+ rows_to_del.append(row_idx)
798
+ dfx.update(row_idx)
799
+ list_len = len(rows_to_del)
800
+ msg = bm.display(f"Cleanup ({list_len})", p_len=self.msg_width)
801
+ dfx = displayfx.DisplayFx(
802
+ list_len,
803
+ p_msg=msg,
804
+ p_verbose=p_verbose,
805
+ p_bar_len=self.bar_len,
806
+ )
807
+ for i, row_idx in enumerate(sorted(rows_to_del, reverse=True)):
808
+ del csv_db[row_idx]
809
+ dfx.update(i)
810
+ print()
811
+ return csv_db
812
+
813
+ # end convert_str_to_none
814
+
815
+ def find_non_char_field_idx(p_csv_db):
816
+ """Find the index of the fields that could potentially contain mepty strings."""
817
+ non_char_fields_idx = []
818
+ for header_field_name in self.non_char_fields[p_table_name]:
819
+ for row_idx, data_field_name in enumerate(p_csv_db[0]):
820
+ if header_field_name == data_field_name:
821
+ non_char_fields_idx.append(row_idx)
822
+ break
823
+ return non_char_fields_idx
824
+
825
+ # end find_non_char_field_idx
826
+
827
+ def fix_dates(p_csv_db, p_table_name, p_header):
828
+ """Ensure date and datetime fields in the database is valid."""
829
+ c_field_idx = 0
830
+ c_field_type = 1
831
+ csv_db = p_csv_db
832
+ idx = []
833
+ # date_time_idx = []
834
+ for i, field in enumerate(p_header):
835
+ if field in self.db_structure[p_table_name]:
836
+ if self.db_structure[p_table_name][field]["Type"][0] == "date":
837
+ idx.append([i, "date"])
838
+ elif self.db_structure[p_table_name][field]["Type"][0] == "datetime":
839
+ idx.append([i, "datetime"])
840
+ if idx:
841
+ for i, row in enumerate(csv_db[1:]):
842
+ for field_det in idx:
843
+ if row[field_det[c_field_idx]] is not None:
844
+ if field_det[c_field_type] == "date" and not isinstance(
845
+ row[field_det[c_field_idx]], datetime.date
846
+ ):
847
+ fixed_date = fixdate.FixDate(
848
+ # self.logger_name,
849
+ row[field_det[c_field_idx]],
850
+ p_out_format="%Y/%m/%d",
851
+ ).date_str
852
+ if isinstance(csv_db[i + 1], tuple):
853
+ csv_db[i + 1] = (
854
+ csv_db[i + 1][: field_det[c_field_idx]]
855
+ + (fixed_date,)
856
+ + csv_db[i + 1][field_det[c_field_idx] + 1 :]
857
+ )
858
+ if isinstance(csv_db[i + 1], list):
859
+ csv_db[i + 1] = (
860
+ csv_db[i + 1][: field_det[c_field_idx]]
861
+ + [fixed_date]
862
+ + csv_db[i + 1][field_det[c_field_idx] + 1 :]
863
+ )
864
+ pass
865
+ # elif field_det[ c_field_type ] == 'datetime' and isinstance( row[ field_det[ c_field_idx ]], datetime.datetime ):
866
+ # date, time = row[ field_det[ c_field_idx ]].split( ' ' )
867
+ # date, time = row[ field_det[ c_field_idx ]].split( ' ' )
868
+ # fixed_date = fixdate.FixDate( self.logger_name, date, p_out_format = '%Y/%m/%d').date_str
869
+ # if isinstance( csv_db[ i + 1 ], tuple ):
870
+ # csv_db[ i + 1 ] = csv_db[ i + 1 ][:field_det[ c_field_idx ]] + ( '{} {}'.format( fixed_date, time ), ) \
871
+ # + csv_db[ i + 1 ][ field_det[ c_field_idx ] + 1:]
872
+ # if isinstance( csv_db[ i + 1 ], list ):
873
+ # csv_db[ i + 1 ] = csv_db[ i + 1 ][:field_det[ c_field_idx ]] + [ '{} {}'.format( fixed_date, time ) ] \
874
+ # + csv_db[ i + 1 ][ field_det[ c_field_idx ] + 1:]
875
+ pass
876
+ pass
877
+ return csv_db
878
+
879
+ # end fix_dates
880
+
881
+ def write_to_table(p_csv_db):
882
+ """Write the data to a table"""
883
+ i = 1
884
+ j = 0 # In case batch size is more than all records
885
+ list_len = len(p_csv_db)
886
+ msg = bm.display(
887
+ f"Populate table = {p_table_name} ({list_len})",
888
+ p_len=self.msg_width,
889
+ )
890
+ dfx = displayfx.DisplayFx(
891
+ list_len,
892
+ p_msg=msg,
893
+ p_verbose=p_verbose,
894
+ p_bar_len=self.bar_len,
895
+ )
896
+ # sql_str = 'REPLACE'
897
+ if p_replace:
898
+ sql_str = "REPLACE"
899
+ else:
900
+ sql_str = "INSERT"
901
+ sql_str = "{} INTO {} ({}) VALUES ({})".format(
902
+ sql_str,
903
+ p_table_name,
904
+ ",".join([str(x) for x in header]),
905
+ ",".join(["%s" for x in range(len(header))]),
906
+ )
907
+ for j in range(self.batch_size, list_len, self.batch_size):
908
+ try:
909
+ self.cur.executemany(sql_str, p_csv_db[i : j + 1])
910
+ except Error as err:
911
+ self.logger.error(err)
912
+ self.conn.rollback()
913
+ self._err_broken_rec(sql_str, p_csv_db[i : j + 1])
914
+ finally:
915
+ self.conn.commit()
916
+ i = j + 1
917
+ dfx.update(j)
918
+ # New needs to be tested. Writing the records 1 by 1?
919
+ # self.logger.debug('{}'.format(p_csv_db[j + 1 : len(p_csv_db)]))
920
+ self.cur.executemany(sql_str, p_csv_db[j + 1 : len(p_csv_db)])
921
+ self.conn.commit()
922
+ if j < list_len:
923
+ dfx.update(list_len)
924
+ pass
925
+
926
+ # end write_to_table
927
+
928
+ csv_db = p_csv_db
929
+ if p_header:
930
+ header = p_header
931
+ else:
932
+ header = csv_db[0]
933
+ csv_db = fix_dates(csv_db, p_table_name, header)
934
+ if self.non_char_fields[p_table_name]:
935
+ csv_db = convert_str_to_none(find_non_char_field_idx(p_csv_db), p_csv_db)
936
+ write_to_table(csv_db)
937
+ pass
938
+
939
+ # end import_volume
940
+
941
+ def import_single_volume(p_csv_db, p_header, p_verbose):
942
+ """Description"""
943
+ success = False
944
+ # if not p_csv_db:
945
+ # if os.path.isfile(p_csv_file_name):
946
+ # csv_file_data = csvwrpr.CsvWrpr(
947
+ # self.logger_name,
948
+ # p_csv_file_name=p_csv_file_name,
949
+ # p_key1=p_key,
950
+ # p_header=p_header,
951
+ # p_del_head=p_del_head,
952
+ # p_struc_type=(),
953
+ # p_csv_corr_str_file_name=p_csv_corr_str_file_name,
954
+ # p_replace_header=replace_header,
955
+ # p_verbose=p_verbose,
956
+ # p_bar_len=self.bar_len,
957
+ # p_msg_width=self.msg_width,
958
+ # )
959
+ # csv_db = csv_file_data.csv_db
960
+ if p_csv_db:
961
+ import_volume(p_csv_db, p_header, p_verbose)
962
+ success = True
963
+ return success
964
+
965
+ # end import_single_volume
966
+
967
+ def import_multi_volume(p_verbose, p_header):
968
+ """Description"""
969
+ vol_cntr = 1
970
+ success = False
971
+ vol_csv_file_name = p_csv_file_name
972
+ while os.path.isfile(vol_csv_file_name):
973
+ # x = csvwrpr.csv
974
+ csv_file_data = csvwrpr.CsvWrpr(
975
+ vol_csv_file_name,
976
+ p_key1=p_key,
977
+ p_header=p_header,
978
+ p_del_head=p_del_head,
979
+ p_struc_type=(),
980
+ p_csv_corr_str_file_name=p_csv_corr_str_file_name,
981
+ p_replace_header=replace_header,
982
+ p_verbose=p_verbose,
983
+ p_msg_width=self.msg_width,
984
+ p_bar_len=self.bar_len,
985
+ p_match_nr_of_fields=True,
986
+ )
987
+ csv_db = csv_file_data.csv_db
988
+ if csv_db:
989
+ import_volume(csv_db, p_header, p_verbose)
990
+ success = True
991
+ vol_cntr += 1
992
+ vol_csv_file_name = p_csv_file_name[:-4] + f"{vol_cntr:0>2}" + p_csv_file_name[-4:]
993
+ if not success:
994
+ log_str = f"No data to import from {vol_csv_file_name}"
995
+ self.logger.warning(log_str)
996
+ return success
997
+
998
+ # end import_multi_volume
999
+
1000
+ if p_header:
1001
+ replace_header = True
1002
+ else:
1003
+ replace_header = False
1004
+ if p_vol_type == "Single" or p_csv_db:
1005
+ success = import_single_volume(p_csv_db, p_header, p_verbose)
1006
+ elif p_vol_type == "Multi":
1007
+ success = import_multi_volume(p_verbose, p_header)
1008
+ else:
1009
+ success = False
1010
+ return success
1011
+
1012
+ def import_and_split_csv(
1013
+ self,
1014
+ p_split_struct,
1015
+ p_data,
1016
+ p_header="",
1017
+ p_insert_header=False,
1018
+ p_verbose=False,
1019
+ p_debug=False,
1020
+ ):
1021
+ """Import a csv file into a database table.
1022
+
1023
+ Parameters
1024
+ - p_split_struct - { 'Seq01': { 'TableName': Desttable_name1, 'Key': TableKey, 'Replace': False, 'Flds': [[ OrgField1, DestField1, [ Command, Parm1, Parm2, Parm3 ]],
1025
+ [ OrgField2, DestField2, [ Command, Parm1, Parm2, Parm3 ]],
1026
+ [ ... ]]},
1027
+ 'Seq02': { 'TableName': Desttable_name2, 'Key': TableKey, 'Replace': False, 'Flds': [[ OrgField1, DestField1, [ Command, Parm1, Parm2, Parm3 ]],
1028
+ [ OrgField2, DestField3, [ Command, Parm1, Parm2, Parm3 ]],
1029
+ [ ... ]]},
1030
+ ... }
1031
+ - SeqNN: Any iterate sequence to indicate the various tables the csv file should be split into ( seq01, seq02, seq03, ...)
1032
+ - table_name (str): Mandatory key word (in the python dict structure) to indicate the table name in the database
1033
+ - Desttable_name (str): The name of the table in the database to populate
1034
+ - Key (str): Mandatory key word (in the python dict structure) to indicate the primary key field of the table
1035
+ - TableKey (str): Destination table primary key
1036
+ - Replace (boolean): Either use REPLACE or INSERT SQL statement to add records to the table. INSERT will cause
1037
+ a failure when the record to be added is a duplicate.
1038
+ - Fields (str): Mandatory key word (in the python dict structure) to list the fields in the table
1039
+ - OrgFieldN (str): Field name from the csv file top copy to the database table
1040
+ - DestFieldN (str): Destination filed where OrgFieldN will be copied into
1041
+ - Command (int): 0 = Copy OrgFieldN to DestFieldN as is
1042
+ Parm1 = Truncate OrgFieldN at Parm1 if it is a string and insert into DestFieldN. 0 for no truncation. Non 'str' will not be truncated
1043
+ Parm2 = True if you do not want to add the row if the result is empty, else False
1044
+ Parm3 = Insert a default value if the original field matched the list.
1045
+ = [ list, Def ]
1046
+ 1 = Insert fixed value into DestFieldN
1047
+ Parm1 = The fixed value to insert into DestFieldN
1048
+ Parm2 = True if you do not want to add the row if the result is empty, else False
1049
+ 2 = Split OrgFieldN by ',' and insert the n'th occurrence defined in Parm1 into DestFieldN
1050
+ Parm1 = The n'th occurrence from split of OrgFieldN to insert into DestFieldN
1051
+ Parm2 = True if you do not want to add the row if the result is empty, else False
1052
+ 3 = Combine the "year" value in OrgFieldN with "01/01" and insert into DestFieldN
1053
+ Parm1 = Date
1054
+ Parm2 = True if you do not want to add the row if the result is empty, else False
1055
+ 4 = Value of OrgFieldN will be looked up in a dict and inserted into DestFieldN
1056
+ Parm1 = Lookup table in form of dict
1057
+ Parm2 = True if you do not want to add the row if the result is empty, else False
1058
+ 5 = Copy sub string from OrgFieldN into DestFieldN
1059
+ Parm1 = List with start and end value to copy from OrgFieldN
1060
+ Parm2 = True if you do not want to add the row if the result is empty, else False
1061
+ 6 = Insert auto number into DestFieldN
1062
+ Parm1 = Start with the value and add 1 with each iteration
1063
+ Parm2 = True if you do not want to add the row if the result is empty, else False
1064
+ - p_data
1065
+ - p_header = ''
1066
+ """
1067
+ if isinstance(p_data, list):
1068
+ csv_file_data = p_data.copy()
1069
+ elif isinstance(p_data, str):
1070
+ csv_file_data = csvwrpr.CsvWrpr(
1071
+ self.logger_name,
1072
+ p_data,
1073
+ "",
1074
+ p_struc_type=(),
1075
+ p_header=p_header,
1076
+ p_verbose=p_verbose,
1077
+ p_bar_len=self.bar_len,
1078
+ p_msg_width=self.msg_width,
1079
+ ).csv_db
1080
+ else:
1081
+ csv_file_data = ()
1082
+ print("Incorrect data structure")
1083
+ if p_insert_header and p_header:
1084
+ header = [tuple(p_header)]
1085
+ csv_file_data = header + csv_file_data
1086
+ for seq in p_split_struct:
1087
+ table = p_split_struct[seq]["TableName"]
1088
+ new_header = ()
1089
+ field_list = []
1090
+ field_config = []
1091
+ for field in p_split_struct[seq]["Flds"]:
1092
+ field_config = []
1093
+ t_str = (field[1],)
1094
+ new_header = new_header + t_str
1095
+ if field[0] != "None":
1096
+ field_config.append(csv_file_data[0].index(field[0]))
1097
+ else:
1098
+ field_config.append(-1)
1099
+ field_config = field_config + field[2]
1100
+ field_list.append(field_config)
1101
+ newcsv_db = [new_header]
1102
+ table_len = len(csv_file_data[1:])
1103
+ if isinstance(p_data, list):
1104
+ msg = bm.display(
1105
+ f"Split data to {table} ({table_len})",
1106
+ p_len=self.msg_width,
1107
+ )
1108
+ else:
1109
+ msg = bm.display(
1110
+ f"Split {os.path.split(p_data)[1]} to {table} ({table_len})",
1111
+ p_len=self.msg_width,
1112
+ )
1113
+ c_field_nr = 0
1114
+ c_cmd_opy = 0
1115
+ c_cmd_insert = 1
1116
+ c_cmd_split = 2
1117
+ c_cmd_date = 3
1118
+ c_cmd_look_up = 4
1119
+ c_cmd_copy_sub = 5
1120
+ c_cmd_auto_inc = 6
1121
+ c_no_trunc = 0
1122
+ c_cmd = 1
1123
+ c_parm1 = 2
1124
+ c_parm2 = 3
1125
+ c_parm3 = 4
1126
+ c_parm3_rep_str = 0
1127
+ c_parm3_def_str = 1
1128
+ dfx = displayfx.DisplayFx(
1129
+ len(csv_file_data[1:]),
1130
+ p_msg=msg,
1131
+ p_verbose=False,
1132
+ p_bar_len=self.bar_len,
1133
+ )
1134
+ for i, row in enumerate(csv_file_data[1:]):
1135
+ new_row = ()
1136
+ add_row = True
1137
+ for field_det in field_list:
1138
+ t_str = ""
1139
+ if field_det[c_cmd] == c_cmd_opy: # Copy / duplicate
1140
+ if field_det[c_parm1] == c_no_trunc or isinstance(row[field_det[c_field_nr]], str):
1141
+ t_str = row[field_det[c_field_nr]]
1142
+ else:
1143
+ t_str = row[field_det[c_field_nr]][0 : field_det[c_parm1]]
1144
+ if len(field_det) > 4:
1145
+ if t_str in field_det[c_parm3][c_parm3_rep_str]:
1146
+ t_str = field_det[c_parm3][c_parm3_def_str]
1147
+ elif field_det[c_cmd] == c_cmd_insert: # Insert fixed value
1148
+ t_str = field_det[c_parm1]
1149
+ elif field_det[c_cmd] == c_cmd_split: # Insert fixed value from split field
1150
+ if row[field_det[c_field_nr]].count(",") >= field_det[c_parm1]:
1151
+ t_str = row[field_det[c_field_nr]].split(",")[field_det[c_parm1]]
1152
+ else:
1153
+ t_str = ""
1154
+ elif field_det[c_cmd] == c_cmd_date: # Insert special value
1155
+ if field_det[c_parm1] == "Date":
1156
+ t_str = row[field_det[c_field_nr]] + "/01/01"
1157
+ else:
1158
+ print("my_sql_db: 143 - Unknown value -", field_list[1])
1159
+ elif field_det[c_cmd] == c_cmd_look_up: # Replace with look up value
1160
+ if row[field_det[c_field_nr]] in field_det[c_parm1]:
1161
+ t_str = field_det[c_parm1][row[field_det[0]]]
1162
+ elif field_det[c_cmd] == c_cmd_copy_sub: # Replace with substring from original field
1163
+ t_str = row[field_det[c_field_nr]][field_det[c_parm1][0] : field_det[c_parm1][1]]
1164
+ elif field_det[c_cmd] == c_cmd_auto_inc: # Insert auto number
1165
+ t_str = field_det[c_parm1]
1166
+ field_det[c_parm1] += 1
1167
+ if isinstance(t_str, str):
1168
+ t_str = t_str.strip()
1169
+ new_row = new_row + (t_str,)
1170
+ if field_det[c_parm2] and not t_str:
1171
+ add_row = add_row and False
1172
+ break
1173
+ if add_row:
1174
+ newcsv_db.append(new_row)
1175
+ dfx.update(i)
1176
+ self.import_csv(
1177
+ p_table_name=table,
1178
+ p_csv_db=newcsv_db,
1179
+ p_header=new_header,
1180
+ p_verbose=p_verbose,
1181
+ p_replace=p_split_struct[seq]["Replace"],
1182
+ )
1183
+
1184
+ @staticmethod
1185
+ def _print_err_msg(p_err, p_msg=""):
1186
+ msg = p_msg
1187
+ if p_msg:
1188
+ msg = f"{p_msg}\n"
1189
+ print(
1190
+ bm.error(
1191
+ "{}Err No:\t\t{}\nSQL State:\t{}\nErr Msg:\t{}\nSystem terminated...".format(
1192
+ msg, p_err.errno, p_err.sqlstate, p_err.msg
1193
+ )
1194
+ )
1195
+ )
1196
+ pass
1197
+
1198
+
1199
+ class MySQL(SQLDbWrpr):
1200
+ """This module creates a wrapper for the MySql database."""
1201
+
1202
+ def __init__(
1203
+ self,
1204
+ p_host_name="localhost",
1205
+ p_user_name="",
1206
+ p_password="",
1207
+ p_user_rights=False,
1208
+ p_recreate_db=False,
1209
+ p_db_name=None,
1210
+ p_db_structure=None,
1211
+ p_batch_size=10000,
1212
+ p_bar_len=50,
1213
+ p_msg_width=50,
1214
+ p_verbose=False,
1215
+ p_admin_username=False,
1216
+ p_admin_user_password=False,
1217
+ p_db_port="3306",
1218
+ # p_ssl_ca=None,
1219
+ # p_ssl_key=None,
1220
+ # p_ssl_cert=None
1221
+ **kwargs,
1222
+ ):
1223
+ """Description"""
1224
+ super().__init__(
1225
+ p_host_name=p_host_name,
1226
+ p_user_name=p_user_name,
1227
+ p_password=p_password,
1228
+ p_db_name=p_db_name,
1229
+ p_recreate_db=p_recreate_db,
1230
+ p_db_structure=p_db_structure,
1231
+ p_batch_size=p_batch_size,
1232
+ p_bar_len=p_bar_len,
1233
+ p_msg_width=p_msg_width,
1234
+ p_verbose=p_verbose,
1235
+ p_db_port=p_db_port,
1236
+ # p_ssl_ca=p_ssl_ca,
1237
+ # p_ssl_key=p_ssl_key,
1238
+ # p_ssl_cert=p_ssl_cert,
1239
+ )
1240
+ try:
1241
+ # import pdb;pdb.set_trace()
1242
+ self.conn = mysql.connector.connect(
1243
+ host=self.host_name,
1244
+ user=self.user_name,
1245
+ password=self._password,
1246
+ database=None,
1247
+ auth_plugin="mysql_native_password",
1248
+ port=self.db_port,
1249
+ # ssl_ca=self.ssl_ca,
1250
+ # ssl_key=self.ssl_key,
1251
+ # ssl_cert=self.ssl_cert
1252
+ **kwargs,
1253
+ )
1254
+ self.cur = self.conn.cursor()
1255
+ except mysql.connector.Error as err:
1256
+ print(
1257
+ bm.error(
1258
+ f"Error {err}:'({self.user_name}'@'{self.host_name}')",
1259
+ )
1260
+ )
1261
+ if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
1262
+ print(bm.error(f"User '{self.user_name}'@'{self.host_name}' does not exist\nAtempt to create it..."))
1263
+ if p_admin_username and p_admin_user_password and p_user_rights:
1264
+ try:
1265
+ self.conn = mysql.connector.connect(
1266
+ host=self.host_name,
1267
+ user=p_admin_username,
1268
+ password=p_admin_user_password,
1269
+ database=None,
1270
+ auth_plugin="mysql_native_password",
1271
+ port=self.db_port,
1272
+ )
1273
+ except mysql.connector.Error as err:
1274
+ self._print_err_msg(
1275
+ err,
1276
+ "Admin user name and/or password not supplied or incorrect",
1277
+ )
1278
+ if self.conn.is_connected():
1279
+ self.cur = self.conn.cursor()
1280
+ self.create_users(
1281
+ [p_admin_username, p_admin_user_password],
1282
+ [[p_user_name, p_password]],
1283
+ )
1284
+ self.grant_rights([p_admin_username, p_admin_user_password], [p_user_rights])
1285
+ else:
1286
+ print(bm.error("Could not connect\nSystem terminated"))
1287
+ sys.exit()
1288
+ else:
1289
+ self._print_err_msg(
1290
+ err,
1291
+ "User name and/or password and/or user access rights not supplied or incorrect",
1292
+ )
1293
+ sys.exit()
1294
+ self.close()
1295
+ if not self.conn.is_connected():
1296
+ self.conn = mysql.connector.connect(
1297
+ host=self.host_name,
1298
+ user=self.user_name,
1299
+ password=self._password,
1300
+ database=None,
1301
+ auth_plugin="mysql_native_password",
1302
+ )
1303
+ self.cur = self.conn.cursor()
1304
+ if self.re_create_db:
1305
+ if self.create_db():
1306
+ self.create_tables()
1307
+ elif self.db_name:
1308
+ self.conn.cmd_init_db(self.db_name)
1309
+ self.conn.commit()
1310
+ self.success = True
1311
+ pass
1312
+
1313
+
1314
+ class MSSQL(SQLDbWrpr):
1315
+ """This module creates a wrapper for the MySql database."""
1316
+
1317
+ def __init__(
1318
+ self,
1319
+ p_host_name="localhost",
1320
+ p_user_name="",
1321
+ p_password="",
1322
+ p_recreate_db=False,
1323
+ p_db_name=None,
1324
+ p_db_structure=None,
1325
+ p_batch_size=10000,
1326
+ p_bar_len=50,
1327
+ p_msg_width=50,
1328
+ p_verbose=False,
1329
+ ):
1330
+ """Description"""
1331
+ super().__init__(
1332
+ p_host_name=p_host_name,
1333
+ p_user_name=p_user_name,
1334
+ p_password=p_password,
1335
+ p_db_name=p_db_name,
1336
+ p_recreate_db=p_recreate_db,
1337
+ p_db_structure=p_db_structure,
1338
+ p_batch_size=p_batch_size,
1339
+ p_bar_len=p_bar_len,
1340
+ p_msg_width=p_msg_width,
1341
+ p_verbose=p_verbose,
1342
+ )
1343
+ try:
1344
+ self.host_name = "156.38.224.15,1433"
1345
+ self.user_name = "chessaco_chessanew"
1346
+ self._password = "@Jv&F77%"
1347
+ self.db_name = "chessaco_analytics"
1348
+ self.driver = "{ODBC Driver 17 for SQL Server}"
1349
+ # driver = pyodbc.drivers()
1350
+ # con_str_1 = 'DRIVER={};SERVER={};DATABASE={};UID={};PWD={}'.format( self.driver, self.host_name, self.db_name, self.user_name, self._password )
1351
+ con_str_2 = (
1352
+ "DRIVER={ODBC Driver 17 for SQL Server};SERVER="
1353
+ + self.host_name
1354
+ + ";DATABASE="
1355
+ + self.db_name
1356
+ + ";UID="
1357
+ + self.user_name
1358
+ + ";PWD="
1359
+ + self._password
1360
+ )
1361
+ self.conn = pyodbc.connect(con_str_2)
1362
+ pass
1363
+ except Error as err:
1364
+ self.logger.error(err)
1365
+ self.success = self.conn.is_connected()
1366
+ if self.conn.is_connected():
1367
+ self.cur = self.conn.cursor()
1368
+ if self.re_create_db:
1369
+ self.create_db()
1370
+ self.success = self.create_tables()