SQLDbWrpr 4.3.0__py3-none-any.whl → 5.0.1__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 +1370 -1370
- sqldbwrpr-5.0.1.dist-info/METADATA +130 -0
- sqldbwrpr-5.0.1.dist-info/RECORD +6 -0
- {sqldbwrpr-4.3.0.dist-info → sqldbwrpr-5.0.1.dist-info}/WHEEL +1 -1
- {sqldbwrpr-4.3.0.dist-info → sqldbwrpr-5.0.1.dist-info/licenses}/LICENSE.txt +1 -1
- sqldbwrpr-4.3.0.dist-info/METADATA +0 -75
- sqldbwrpr-4.3.0.dist-info/RECORD +0 -6
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()
|