dapper-sqls 0.9.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,375 @@
1
+ # -*- coding: latin -*-
2
+
3
+ from itertools import groupby
4
+ import os
5
+ from .utils import create_content_orm, InformationSchemaTables, InformationSchemaRoutines, create_field, create_content_async_orm, create_params_routine, get_parameters_with_defaults
6
+
7
+ class TableBuilderData:
8
+ def __init__(self, table_schema : str, table_name : str, class_name : str, model : str, orm : str | None, async_orm : str | None):
9
+ self.table_schema = table_schema
10
+ self.table_name = table_name
11
+ self.class_name = class_name
12
+ self.model = model
13
+ self.orm = orm
14
+ self.async_orm = async_orm
15
+
16
+ class RoutineBuilderData:
17
+ def __init__(self, table_schema : str, stp_name : str, content_stp : str, content_async_stp : str):
18
+ self.table_schema = table_schema
19
+ self.stp_name = stp_name
20
+ self.content_stp = content_stp
21
+ self.content_async_stp = content_async_stp
22
+
23
+ class BuilderData(object):
24
+ def __init__(self, table_catalog : str):
25
+ self.table_catalog = table_catalog
26
+ self.talbes : list[TableBuilderData] = []
27
+ self.routines : list[RoutineBuilderData] = []
28
+
29
+ class ModelBuilder(object):
30
+
31
+ class TableOptions(object):
32
+ def __init__(self, table_name : str, *, create_orm=True, ignore_table=False):
33
+ self.table_name = table_name
34
+ self.create_orm = create_orm
35
+ self.ignore_table = ignore_table
36
+
37
+ class RoutineOptions(object):
38
+ def __init__(self, routine_name : str, ignore_routine=False):
39
+ self.routine_name = routine_name
40
+ self.ignore_routine = ignore_routine
41
+
42
+ def __init__(self, dapper):
43
+ self._dapper = dapper
44
+ self.query_tables = f"""
45
+ SELECT c.TABLE_CATALOG, c.TABLE_SCHEMA, c.TABLE_NAME, c.DATA_TYPE, c.COLUMN_NAME, c.IS_NULLABLE
46
+ FROM (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE') t
47
+ JOIN (
48
+ SELECT *
49
+ FROM INFORMATION_SCHEMA.COLUMNS
50
+ ) c ON t.TABLE_NAME = c.TABLE_NAME
51
+ """
52
+
53
+ self.query_routines = f"""
54
+ SELECT
55
+ p.SPECIFIC_NAME,
56
+ p.PARAMETER_NAME,
57
+ p.DATA_TYPE,
58
+ p.SPECIFIC_CATALOG,
59
+ p.SPECIFIC_SCHEMA,
60
+ p.ORDINAL_POSITION,
61
+ sm.definition AS PROCEDURE_DEFINITION
62
+ FROM
63
+ INFORMATION_SCHEMA.PARAMETERS p
64
+ JOIN
65
+ INFORMATION_SCHEMA.ROUTINES r ON p.SPECIFIC_NAME = r.SPECIFIC_NAME
66
+ JOIN
67
+ sys.sql_modules sm ON OBJECT_NAME(sm.object_id) = r.SPECIFIC_NAME
68
+ WHERE
69
+ r.ROUTINE_TYPE = 'PROCEDURE'
70
+ ORDER BY
71
+ p.SPECIFIC_NAME, p.ORDINAL_POSITION;
72
+ """
73
+
74
+ @property
75
+ def dapper(self):
76
+ return self._dapper
77
+
78
+ def get_info_model_db(self):
79
+ with self.dapper.query() as db:
80
+ information_schema_tables = db.fetchall(self.query_tables)
81
+ if not information_schema_tables.success:
82
+ return False
83
+ information_schema_tables = self.dapper.load(InformationSchemaTables, information_schema_tables)
84
+ information_schema_tables = [table for table in information_schema_tables if not table.TABLE_NAME.startswith('__')]
85
+
86
+ information_schema_tables.sort(key=lambda x: x.TABLE_NAME)
87
+ grouped_data = groupby(information_schema_tables, lambda x: x.TABLE_NAME)
88
+ grouped_list : list[list[InformationSchemaTables]] = [[obj for obj in group] for _, group in grouped_data]
89
+ if not grouped_list:
90
+ return False
91
+ return grouped_list
92
+
93
+ def get_info_routines_db(self):
94
+ with self.dapper.query() as db:
95
+ information_schema_routines = db.fetchall(self.query_routines)
96
+ if not information_schema_routines:
97
+ return []
98
+ information_schema_routines = self.dapper.load(InformationSchemaRoutines, information_schema_routines)
99
+
100
+ information_schema_routines.sort(key=lambda x: x.SPECIFIC_NAME)
101
+ grouped_data = groupby(information_schema_routines, lambda x: x.SPECIFIC_NAME)
102
+ grouped_list : list[list[InformationSchemaRoutines]] = [[obj for obj in group] for _, group in grouped_data]
103
+ if not grouped_list:
104
+ return []
105
+ return grouped_list
106
+
107
+ def create_model_db(self, dir_path : str, create_orm = True, create_stp = True, *, table_catalog : str | list[str] | tuple[str] = "all",
108
+ table_schema : str | list[str] | tuple[str] = "all",
109
+ tables_options : list[TableOptions] = [], routines_oprions : list[RoutineOptions] = []):
110
+
111
+ dict_tables_options = {}
112
+ if tables_options:
113
+ dict_tables_options = {options.table_name : options for options in tables_options}
114
+ create_orm = False
115
+ for options in tables_options:
116
+ if options.create_orm:
117
+ create_orm = True
118
+ break
119
+
120
+ dict_routines_options = {options.routine_name : options for options in routines_oprions}
121
+ if routines_oprions:
122
+ create_stp = False
123
+ for options in routines_oprions:
124
+ if not options.ignore_routine:
125
+ create_stp = True
126
+ break
127
+
128
+ information_db = self.get_info_model_db()
129
+ information_routines = []
130
+ if create_stp:
131
+ information_routines = self.get_info_routines_db()
132
+ if not information_db:
133
+ return False
134
+
135
+ table_catalog = [table_catalog] if isinstance(table_catalog, str) and table_catalog != "all" else table_catalog
136
+ table_schema = [table_schema] if isinstance(table_schema, str) and table_schema != "all" else table_schema
137
+
138
+ builder_data : dict[str, BuilderData] = {}
139
+ import_init_db = ""
140
+ for data in information_db:
141
+
142
+ if table_catalog != "all":
143
+ if data[0].TABLE_CATALOG not in table_catalog :
144
+ continue
145
+
146
+ if table_schema != "all":
147
+ if data[0].TABLE_SCHEMA not in table_schema :
148
+ continue
149
+
150
+ table_options = dict_tables_options.get(data[0].TABLE_NAME)
151
+ if table_options:
152
+ if table_options.ignore_table:
153
+ continue
154
+
155
+ content_model = '''# -*- coding: latin -*-
156
+
157
+ from dapper_sqls import TableBaseModel
158
+ from datetime import datetime
159
+ from pydantic import Field
160
+ from typing import Union
161
+
162
+ '''
163
+
164
+ table_name = data[0].TABLE_NAME
165
+ class_name = table_name.replace("TBL_", "")
166
+ schema = data[0].TABLE_SCHEMA
167
+
168
+ fields = [create_field(row) for row in data]
169
+ fields_str = "\n ".join(fields)
170
+
171
+ content_model += f'''
172
+ class {class_name}(TableBaseModel):
173
+ _TABLE_NAME: str = '[{schema}].[{table_name}]'
174
+
175
+ {fields_str}
176
+ \n
177
+ '''
178
+
179
+ fields_args = [create_field(row, "None") for row in data]
180
+ fields_args_str = ", ".join(fields_args)
181
+
182
+ table_create_orm = create_orm
183
+ if table_options:
184
+ table_create_orm = table_options.create_orm
185
+
186
+ content_orm = create_content_orm(class_name, fields_args_str) if table_create_orm else None
187
+ content_async_orm = create_content_async_orm(class_name, fields_args_str) if table_create_orm else None
188
+
189
+ catalog = data[0].TABLE_CATALOG
190
+ if catalog not in builder_data:
191
+ builder_data[catalog] = BuilderData(catalog)
192
+
193
+ table_builder_data = TableBuilderData(schema, table_name, class_name, content_model, content_orm, content_async_orm)
194
+ builder_data[catalog].talbes.append(table_builder_data)
195
+
196
+ for data in information_routines:
197
+
198
+ if table_catalog != "all":
199
+ if data[0].SPECIFIC_CATALOG not in table_catalog :
200
+ continue
201
+
202
+ if table_schema != "all":
203
+ if data[0].SPECIFIC_SCHEMA not in table_schema :
204
+ continue
205
+
206
+ routine_oprions = dict_routines_options.get(data[0].SPECIFIC_NAME)
207
+ if routine_oprions:
208
+ if routine_oprions.ignore_routine:
209
+ continue
210
+
211
+ defaults_values = get_parameters_with_defaults(data[0].PROCEDURE_DEFINITION)
212
+ params_routine = [create_params_routine(row, defaults_values) for row in data]
213
+ params_routine_str = ", ".join(params_routine)
214
+
215
+ stp_name = data[0].SPECIFIC_NAME.replace('STP_', '')
216
+ content_routine = f'''
217
+ def {stp_name}(self, *, {params_routine_str}):
218
+ return StpBuilder(self.dapper, '[{data[0].SPECIFIC_SCHEMA}].[{data[0].SPECIFIC_NAME}]',locals())'''
219
+
220
+ content_async_routine = f'''
221
+ def {stp_name}(self, *, {params_routine_str}):
222
+ return AsyncStpBuilder(self.async_dapper, '[{data[0].SPECIFIC_SCHEMA}].[{data[0].SPECIFIC_NAME}]', locals())'''
223
+
224
+ catalog = data[0].SPECIFIC_CATALOG
225
+ if catalog not in builder_data:
226
+ builder_data[catalog] = BuilderData(catalog)
227
+
228
+ builder_data[catalog].routines.append(RoutineBuilderData(data[0].SPECIFIC_SCHEMA, data[0].SPECIFIC_NAME, content_routine, content_async_routine))
229
+
230
+ for catalog, data in builder_data.items():
231
+ import_init_db += f"from .{catalog} import {catalog}\n"
232
+
233
+ dir_catalog = os.path.join(dir_path, catalog)
234
+ schema_data_tables : dict[str, list[TableBuilderData]] = {}
235
+ for table in data.talbes:
236
+ if table.table_schema not in schema_data_tables:
237
+ schema_data_tables[table.table_schema] = []
238
+
239
+ dir_schema = os.path.join(dir_catalog, table.table_schema)
240
+ dir_table = os.path.join(dir_schema, table.table_name)
241
+
242
+ if not os.path.exists(dir_table):
243
+ os.makedirs(dir_table)
244
+
245
+ table_options = dict_tables_options.get(table.table_name)
246
+
247
+ table_create_orm = create_orm
248
+ if table_options:
249
+ table_create_orm = table_options.create_orm
250
+
251
+ if table_create_orm:
252
+ with open(os.path.join(dir_table ,'orm.py'), 'w', encoding='utf-8') as file:
253
+ file.write(''.join(table.orm))
254
+
255
+ with open(os.path.join(dir_table ,'async_orm.py'), 'w', encoding='utf-8') as file:
256
+ file.write(''.join(table.async_orm))
257
+
258
+ with open(os.path.join(dir_table ,f'__init__.py'), 'w', encoding='utf-8') as file:
259
+ if table_create_orm:
260
+ file.write(f'from .orm import {table.class_name}ORM\nfrom .async_orm import Async{table.class_name}ORM\nfrom .model import {table.class_name}')
261
+ else:
262
+ file.write(f'from .model import {table.class_name}')
263
+
264
+ with open(os.path.join(dir_table ,'model.py'), 'w', encoding='utf-8') as file:
265
+ file.write(''.join(table.model))
266
+
267
+
268
+ schema_data_tables[table.table_schema].append(table)
269
+
270
+ schema_data_routine : dict[str, list[RoutineBuilderData]] = {}
271
+ content_file_routine = '''# -*- coding: latin -*-
272
+ from dapper_sqls import StpBuilder
273
+ from dapper_sqls import Dapper
274
+ from datetime import datetime
275
+ from typing import Union
276
+
277
+ class stp(object):
278
+
279
+ def __init__(self, dapper : Dapper):
280
+ self._dapper = dapper
281
+
282
+ @property
283
+ def dapper(self):
284
+ return self._dapper
285
+ '''
286
+
287
+ content_file_async_rounine = '''# -*- coding: latin -*-
288
+ from dapper_sqls import AsyncStpBuilder
289
+ from dapper_sqls import AsyncDapper
290
+ from datetime import datetime
291
+ from typing import Union
292
+
293
+ class async_stp(object):
294
+
295
+ def __init__(self, async_dapper : AsyncDapper):
296
+ self._async_dapper = async_dapper
297
+
298
+ @property
299
+ def async_dapper(self):
300
+ return self._async_dapper
301
+ '''
302
+
303
+ for routine in data.routines:
304
+ if routine.table_schema not in schema_data_routine:
305
+ schema_data_routine[routine.table_schema] = []
306
+
307
+ dir_schema = os.path.join(dir_catalog, routine.table_schema)
308
+ if not os.path.exists(dir_schema):
309
+ os.makedirs(dir_schema)
310
+
311
+ content_file_routine += f'{routine.content_stp}\n'
312
+ content_file_async_rounine += f'{routine.content_async_stp}\n'
313
+
314
+ if data.routines:
315
+ dir_schema = os.path.join(dir_catalog, data.routines[0].table_schema)
316
+ with open(os.path.join(dir_schema ,'routines.py'), 'w', encoding='utf-8') as file:
317
+ file.write(''.join(content_file_routine))
318
+ with open(os.path.join(dir_schema ,'async_routines.py'), 'w', encoding='utf-8') as file:
319
+ file.write(''.join(content_file_async_rounine))
320
+
321
+ import_init_catalog = ""
322
+ class_init_catalog = f"class {catalog}(object):\n"
323
+
324
+ for schema, data in schema_data_tables.items():
325
+ import_init_catalog += f"from .{schema} import schema_{schema}\n"
326
+ class_init_catalog += f"\n class {schema}(schema_{schema}):\n ...\n"
327
+ import_init_schema = ""
328
+ class_init_schema = ""
329
+ class_models_schema = " class models(object):\n"
330
+ class_orm_schema = " class orm(object):\n def __init__(self, dapper : Dapper):\n"
331
+ class_async_orm_schema = " class async_orm(object):\n def __init__(self, async_dapper : AsyncDapper):\n"
332
+ for table in data:
333
+
334
+ table_options = dict_tables_options.get(table.table_name)
335
+ table_create_orm = create_orm
336
+ if table_options:
337
+ table_create_orm = table_options.create_orm
338
+
339
+ dir_schema = os.path.join(dir_catalog, schema)
340
+ class_models_schema += f"\n class {table.class_name}({table.class_name}):\n ...\n"
341
+ if table_create_orm:
342
+ class_orm_schema += f" self.{table.class_name} = {table.class_name}ORM(dapper)\n"
343
+ class_async_orm_schema += f" self.{table.class_name} = Async{table.class_name}ORM(async_dapper)\n"
344
+ import_init_schema += f"from .{table.table_name} import {table.class_name}, {table.class_name}ORM, Async{table.class_name}ORM\n"
345
+ class_init_schema += f"\n class {table.class_name}ORM({table.class_name}ORM):\n ...\n"
346
+ class_init_schema += f"\n class Async{table.class_name}ORM(Async{table.class_name}ORM):\n ...\n"
347
+ else:
348
+ import_init_schema += f"from .{table.table_name} import {table.class_name}\n"
349
+
350
+ if information_routines :
351
+ import_init_schema += "from .routines import stp\nfrom .async_routines import async_stp\n"
352
+
353
+ class_stp = "\n class stp(stp):\n ...\n" if information_routines else ""
354
+ class_async_stp = "\n class async_stp(async_stp):\n ...\n" if information_routines else ""
355
+
356
+ class_schema = f"class schema_{schema}(object):\n"
357
+ if create_orm:
358
+ class_init_schema = f"{class_schema}{class_stp}{class_async_stp}\n{class_models_schema}\n{class_orm_schema}\n{class_async_orm_schema}\n{class_init_schema}"
359
+ content_init_schema = f"{import_init_schema}\nfrom dapper_sqls import Dapper, AsyncDapper\n\n{class_init_schema}"
360
+ else:
361
+ class_init_schema = f"{class_schema}{class_stp}{class_async_stp}\n{class_models_schema}"
362
+ content_init_schema = f"{import_init_schema}\n\n{class_init_schema}"
363
+
364
+ with open(os.path.join(dir_schema ,f'__init__.py'), 'w', encoding='utf-8') as file:
365
+ file.write(''.join(content_init_schema))
366
+
367
+ content_init_catalog = f"{import_init_catalog}\n\n{class_init_catalog}"
368
+ with open(os.path.join(dir_catalog ,f'__init__.py'), 'w', encoding='utf-8') as file:
369
+ file.write(''.join(content_init_catalog))
370
+
371
+ if builder_data:
372
+ #with open(os.path.join(dir_path ,f'__init__.py'), 'w', encoding='utf-8') as file:
373
+ # file.write(''.join(import_init_db))
374
+
375
+ return True