dprojectstools 0.0.30__tar.gz → 0.0.32__tar.gz
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.
- {dprojectstools-0.0.30/src/dprojectstools.egg-info → dprojectstools-0.0.32}/PKG-INFO +1 -1
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/pyproject.toml +1 -1
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/commands/commands.py +49 -4
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/console/__init__.py +1 -0
- dprojectstools-0.0.32/src/dprojectstools/console/output.py +97 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/db/db_inspector.py +13 -3
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/db/db_schema.py +5 -2
- dprojectstools-0.0.32/src/dprojectstools/db/generator_json.py +140 -0
- dprojectstools-0.0.32/src/dprojectstools/db/generator_sql.py +237 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/db/generator_vb.py +221 -149
- {dprojectstools-0.0.30 → dprojectstools-0.0.32/src/dprojectstools.egg-info}/PKG-INFO +1 -1
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools.egg-info/SOURCES.txt +2 -0
- dprojectstools-0.0.30/src/dprojectstools/db/generator_sql.py +0 -135
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/LICENSE +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/README.md +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/setup.cfg +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/backups/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/backups/restic.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/clipboard/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/clipboard/clipboard.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/commands/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/commands/p.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/config/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/config/config.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/console/keys.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/console/readKey.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/console/read_password.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/console/sequences.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/crypto/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/crypto/aes.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/db/__main__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/db/generator_cs.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/db/generator_xml.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/docker/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/docker/docker.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/git/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/git/git.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/i18n/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/i18n/translator.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/i18n/xgettext.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/images/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/images/fonticons.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/math/bc.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/secrets/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/secrets/__main__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/secrets/secrets.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/utils/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/utils/env.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/utils/process.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/utils/versions.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/utils/xml.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/xeditor/__init__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/xeditor/__main__.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/xeditor/hightlight_env.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/xeditor/hightlight_json.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/xeditor/hightlight_md.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/xeditor/hightlight_none.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools/xeditor/xeditor.py +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools.egg-info/dependency_links.txt +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools.egg-info/entry_points.txt +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools.egg-info/requires.txt +0 -0
- {dprojectstools-0.0.30 → dprojectstools-0.0.32}/src/dprojectstools.egg-info/top_level.txt +0 -0
|
@@ -131,6 +131,26 @@ class CommandsManager:
|
|
|
131
131
|
# self.registerFunction(instance, obj, prefix)
|
|
132
132
|
self._registerOrder += 1
|
|
133
133
|
|
|
134
|
+
def registerDirectory(self, directory: str, prefix: str = ""):
|
|
135
|
+
# register main decorated functions from all modules in a directory
|
|
136
|
+
directory = os.path.abspath(directory)
|
|
137
|
+
for filename in sorted(os.listdir(directory)):
|
|
138
|
+
if not filename.endswith(".py") or filename.startswith("_") or filename == "__init__.py":
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
file_path = os.path.join(directory, filename)
|
|
142
|
+
module_name = f"_cmd_{os.path.splitext(filename)[0]}"
|
|
143
|
+
|
|
144
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
145
|
+
if spec is None or spec.loader is None:
|
|
146
|
+
raise ImportError(f"Cannot load module from: {file_path}")
|
|
147
|
+
|
|
148
|
+
module = importlib.util.module_from_spec(spec)
|
|
149
|
+
sys.modules[module_name] = module
|
|
150
|
+
spec.loader.exec_module(module)
|
|
151
|
+
|
|
152
|
+
self.register(module=module, prefix=prefix)
|
|
153
|
+
|
|
134
154
|
def registerFunction(self, instance, func, prefix: str = ""):
|
|
135
155
|
# create a command fron function
|
|
136
156
|
func_name = func.__name__
|
|
@@ -215,14 +235,19 @@ class CommandsManager:
|
|
|
215
235
|
metadata = param.annotation.__metadata__[0]
|
|
216
236
|
flag_char = metadata.chr
|
|
217
237
|
# required
|
|
218
|
-
flag_required = (param.default == inspect.Parameter.empty or param.default == None)
|
|
238
|
+
# flag_required = (param.default == inspect.Parameter.empty or param.default == None)
|
|
239
|
+
flag_required = (param.default == inspect.Parameter.empty)
|
|
240
|
+
if command_name[0] == "module" and command_name[1] == "build":
|
|
241
|
+
flag_required = flag_required
|
|
242
|
+
|
|
219
243
|
# type
|
|
220
244
|
flag_type = None
|
|
221
245
|
if hasattr(param.annotation, "__metadata__"):
|
|
222
246
|
flag_type = param.annotation.__origin__.__name__
|
|
223
247
|
# argument default
|
|
224
248
|
flag_default = None
|
|
225
|
-
if param.default != inspect.Parameter.empty and param.default != None:
|
|
249
|
+
# if param.default != inspect.Parameter.empty and param.default != None:
|
|
250
|
+
if param.default != inspect.Parameter.empty:
|
|
226
251
|
flag_default = param.default
|
|
227
252
|
# crea CommandFlag
|
|
228
253
|
flag = CommandFlag(flag_name, flag_alias, flag_title, flag_subtitle, flag_required, flag_type, flag_default, flag_char)
|
|
@@ -519,6 +544,16 @@ class CommandsManager:
|
|
|
519
544
|
# subcommands
|
|
520
545
|
if len(subcommands) > 0:
|
|
521
546
|
print("Commands:")
|
|
547
|
+
# compute indent_subcommands
|
|
548
|
+
for subcommand in subcommands:
|
|
549
|
+
subcommand_name = subcommand.name
|
|
550
|
+
if command != None:
|
|
551
|
+
subcommand_name = subcommand_name[len(command.name):]
|
|
552
|
+
if subcommand.alias != None:
|
|
553
|
+
subcommand_name = subcommand.alias
|
|
554
|
+
if indent_subcommands < len(' '.join(subcommand_name)) + 2:
|
|
555
|
+
indent_subcommands = len(' '.join(subcommand_name)) + 2
|
|
556
|
+
# print
|
|
522
557
|
for subcommand in subcommands:
|
|
523
558
|
subcommand_name = subcommand.name
|
|
524
559
|
if command != None:
|
|
@@ -640,11 +675,13 @@ class CommandsManager:
|
|
|
640
675
|
|
|
641
676
|
# exec Command
|
|
642
677
|
command_to_execute = None
|
|
678
|
+
command_to_execute_args_len = 0
|
|
643
679
|
for command in reversed(self._commands):
|
|
644
680
|
command_name = command.alias if command.alias else command.name
|
|
645
681
|
if len(command_name) <= len(self._argv) - 1:
|
|
646
682
|
if command_name == self._argv[1:len(command_name)+1]:
|
|
647
683
|
command_to_execute = command
|
|
684
|
+
command_to_execute_args_len = len(command_name)
|
|
648
685
|
break
|
|
649
686
|
if command_to_execute:
|
|
650
687
|
break
|
|
@@ -700,7 +737,8 @@ class CommandsManager:
|
|
|
700
737
|
self._argv.append("--" + flag.alias)
|
|
701
738
|
self._argv.append(flag_value_separator.join(flag_values))
|
|
702
739
|
# read flags
|
|
703
|
-
command_args = self._argv[len(command_to_execute.name)+1:]
|
|
740
|
+
#command_args = self._argv[len(command_to_execute.name)+1:]
|
|
741
|
+
command_args = self._argv[command_to_execute_args_len+1:]
|
|
704
742
|
command_args_dict = {}
|
|
705
743
|
command_args_errors = []
|
|
706
744
|
for flag in command_to_execute.flags:
|
|
@@ -741,6 +779,10 @@ class CommandsManager:
|
|
|
741
779
|
command_args_dict[flag.name] = flag_value
|
|
742
780
|
command_args.pop(index + 1)
|
|
743
781
|
command_args.pop(index)
|
|
782
|
+
# get invalid flags
|
|
783
|
+
for argument in command_args:
|
|
784
|
+
if argument.startswith("-"):
|
|
785
|
+
command_args_errors.append(f"invalid flag: {argument}")
|
|
744
786
|
# read arguments
|
|
745
787
|
for argument in command_to_execute.arguments:
|
|
746
788
|
argument_value = None
|
|
@@ -786,7 +828,10 @@ class CommandsManager:
|
|
|
786
828
|
return func_bounded(**command_args_dict)
|
|
787
829
|
else:
|
|
788
830
|
# invoke
|
|
789
|
-
|
|
831
|
+
result = command_to_execute.func(**command_args_dict)
|
|
832
|
+
if isinstance(result, bool):
|
|
833
|
+
result = 1 if not result else 0
|
|
834
|
+
return result
|
|
790
835
|
|
|
791
836
|
|
|
792
837
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import yaml
|
|
3
|
+
from dataclasses import asdict, is_dataclass
|
|
4
|
+
from .sequences import *
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
# print utils
|
|
8
|
+
def print_info(message: str = "") -> None:
|
|
9
|
+
GRAY = "\033[90m"
|
|
10
|
+
RESET = "\033[0m"
|
|
11
|
+
if sys.stdout.isatty():
|
|
12
|
+
print(f"{GRAY}{message}{RESET}", file=sys.stdout, flush=True)
|
|
13
|
+
else:
|
|
14
|
+
print(message, file=sys.stdout, flush=True)
|
|
15
|
+
|
|
16
|
+
def print_warning(message: str = "") -> None:
|
|
17
|
+
YELLOW = "\033[33m"
|
|
18
|
+
RESET = "\033[0m"
|
|
19
|
+
if sys.stdout.isatty():
|
|
20
|
+
print(f"{YELLOW}{message}{RESET}", file=sys.stderr, flush=True)
|
|
21
|
+
else:
|
|
22
|
+
print(message, file=sys.stdout, flush=True)
|
|
23
|
+
|
|
24
|
+
def print_error(message: str = "") -> None:
|
|
25
|
+
RED = "\033[31m"
|
|
26
|
+
RESET = "\033[0m"
|
|
27
|
+
if sys.stdout.isatty():
|
|
28
|
+
print(f"{RED}{message}{RESET}", file=sys.stderr, flush=True)
|
|
29
|
+
else:
|
|
30
|
+
print(message, file=sys.stderr, flush=True)
|
|
31
|
+
|
|
32
|
+
def print_table(items, fields) -> str:
|
|
33
|
+
def resolve_value(item, field_path):
|
|
34
|
+
value = item
|
|
35
|
+
for part in field_path.split("."):
|
|
36
|
+
if isinstance(value, dict):
|
|
37
|
+
value = value.get(part, "")
|
|
38
|
+
else:
|
|
39
|
+
value = getattr(value, part, "")
|
|
40
|
+
return value
|
|
41
|
+
def normalize_value(value):
|
|
42
|
+
if isinstance(value, list):
|
|
43
|
+
return ", ".join(str(x) for x in value)
|
|
44
|
+
return str(value)
|
|
45
|
+
|
|
46
|
+
headers = [field.split(".")[-1].upper() for field in fields]
|
|
47
|
+
|
|
48
|
+
rows = []
|
|
49
|
+
for item in items:
|
|
50
|
+
row = []
|
|
51
|
+
for field in fields:
|
|
52
|
+
value = resolve_value(item, field)
|
|
53
|
+
row.append(normalize_value(value))
|
|
54
|
+
rows.append(row)
|
|
55
|
+
|
|
56
|
+
widths = []
|
|
57
|
+
for i, header in enumerate(headers):
|
|
58
|
+
width = len(header)
|
|
59
|
+
for row in rows:
|
|
60
|
+
width = max(width, len(row[i]))
|
|
61
|
+
widths.append(width)
|
|
62
|
+
|
|
63
|
+
header_line = " ".join(headers[i].ljust(widths[i]) for i in range(len(headers)))
|
|
64
|
+
sep_line = " ".join("-" * widths[i] for i in range(len(headers)))
|
|
65
|
+
body_lines = [
|
|
66
|
+
" ".join(row[i].ljust(widths[i]) for i in range(len(fields)))
|
|
67
|
+
for row in rows
|
|
68
|
+
]
|
|
69
|
+
print("\n".join([header_line, sep_line] + body_lines))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# yaml utils
|
|
73
|
+
def to_yaml_safe(value):
|
|
74
|
+
if isinstance(value, Path):
|
|
75
|
+
return str(value)
|
|
76
|
+
if isinstance(value, dict):
|
|
77
|
+
return {k: to_yaml_safe(v) for k, v in value.items()}
|
|
78
|
+
if isinstance(value, list):
|
|
79
|
+
return [to_yaml_safe(v) for v in value]
|
|
80
|
+
if isinstance(value, tuple):
|
|
81
|
+
return [to_yaml_safe(v) for v in value]
|
|
82
|
+
return value
|
|
83
|
+
|
|
84
|
+
class IndentYamlDumper(yaml.SafeDumper):
|
|
85
|
+
def increase_indent(self, flow=False, indentless=False):
|
|
86
|
+
return super().increase_indent(flow, False)
|
|
87
|
+
|
|
88
|
+
def print_yaml( value: object, indent: int = 2) -> None:
|
|
89
|
+
if is_dataclass(value):
|
|
90
|
+
value = asdict(value)
|
|
91
|
+
value = to_yaml_safe(value)
|
|
92
|
+
print_info(yaml.dump(value,
|
|
93
|
+
Dumper=IndentYamlDumper,
|
|
94
|
+
sort_keys=False,
|
|
95
|
+
allow_unicode=True,
|
|
96
|
+
default_flow_style=False,
|
|
97
|
+
indent=indent))
|
|
@@ -6,7 +6,7 @@ def datatype_name_to_class(column_type_name):
|
|
|
6
6
|
return DataType.Bigint
|
|
7
7
|
elif column_type_name == "BINARY":
|
|
8
8
|
return DataType.Binary
|
|
9
|
-
elif column_type_name == "BLOB":
|
|
9
|
+
elif column_type_name == "BLOB" or column_type_name == "IMAGE":
|
|
10
10
|
return DataType.Varbinary
|
|
11
11
|
elif column_type_name == "BOOLEAN" or column_type_name == "BIT":
|
|
12
12
|
return DataType.Boolean
|
|
@@ -230,6 +230,14 @@ class Inspector:
|
|
|
230
230
|
# sql server
|
|
231
231
|
class InspectorSqlServer(Inspector):
|
|
232
232
|
|
|
233
|
+
def get_table_names(self):
|
|
234
|
+
result = []
|
|
235
|
+
for table in self._inspector.get_table_names():
|
|
236
|
+
if table == "dtproperties":
|
|
237
|
+
continue
|
|
238
|
+
result.append(table)
|
|
239
|
+
return result
|
|
240
|
+
|
|
233
241
|
def get_database_collation(self):
|
|
234
242
|
return self.execute_scalar("SELECT DATABASEPROPERTYEX(DB_NAME(), 'Collation') AS Collation")
|
|
235
243
|
|
|
@@ -244,7 +252,10 @@ class InspectorSqlServer(Inspector):
|
|
|
244
252
|
def get_procedure_names(self):
|
|
245
253
|
result = []
|
|
246
254
|
for row in self.execute_fetchall("SELECT name FROM sys.procedures ORDER BY name"):
|
|
247
|
-
|
|
255
|
+
name = row[0]
|
|
256
|
+
if name.startswith("sp_") or name.startswith("fn_") or name.startswith("xp_") or name.startswith("dt_"):
|
|
257
|
+
continue
|
|
258
|
+
result.append(name)
|
|
248
259
|
return result
|
|
249
260
|
|
|
250
261
|
def get_procedure(self, procedure_name):
|
|
@@ -281,7 +292,6 @@ class InspectorPostgresql(Inspector):
|
|
|
281
292
|
class InspectorOracle(Inspector):
|
|
282
293
|
|
|
283
294
|
|
|
284
|
-
|
|
285
295
|
def get_database_collation(self):
|
|
286
296
|
return self.execute_scalar("SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET'")
|
|
287
297
|
def get_table_names(self):
|
|
@@ -2,6 +2,7 @@ from xml.etree import ElementTree
|
|
|
2
2
|
from xml.dom import minidom
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from enum import Enum
|
|
5
|
+
from flask import json
|
|
5
6
|
from lxml import etree
|
|
6
7
|
import os
|
|
7
8
|
|
|
@@ -35,7 +36,9 @@ class DataType(Enum):
|
|
|
35
36
|
for member in DataType:
|
|
36
37
|
if member.name.lower() == value:
|
|
37
38
|
return member
|
|
38
|
-
|
|
39
|
+
def to_str(self):
|
|
40
|
+
return self.name.lower()
|
|
41
|
+
|
|
39
42
|
class OnDelete(Enum):
|
|
40
43
|
NO_ACTION = 1
|
|
41
44
|
CASCADE = 2
|
|
@@ -158,7 +161,7 @@ class Schema:
|
|
|
158
161
|
self.scripts = []
|
|
159
162
|
|
|
160
163
|
# static methods
|
|
161
|
-
def create(source: str, create_inspector):
|
|
164
|
+
def create(source: str, create_inspector = None):
|
|
162
165
|
if os.path.exists(source):
|
|
163
166
|
with open(source, "r", encoding='utf-8') as file:
|
|
164
167
|
xml = file.read()
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from xml.etree import ElementTree
|
|
3
|
+
from xml.dom import minidom
|
|
4
|
+
from xml.sax.saxutils import escape
|
|
5
|
+
from .db_schema import OnDelete, OnUpdate
|
|
6
|
+
|
|
7
|
+
def remove_none(obj):
|
|
8
|
+
if isinstance(obj, dict):
|
|
9
|
+
return {
|
|
10
|
+
k: remove_none(v)
|
|
11
|
+
for k, v in obj.items()
|
|
12
|
+
if v is not None
|
|
13
|
+
}
|
|
14
|
+
elif isinstance(obj, list):
|
|
15
|
+
return [
|
|
16
|
+
remove_none(item)
|
|
17
|
+
for item in obj
|
|
18
|
+
if item is not None
|
|
19
|
+
]
|
|
20
|
+
else:
|
|
21
|
+
return obj
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GeneratorJson:
|
|
25
|
+
|
|
26
|
+
def __init__(self, schema):
|
|
27
|
+
self._separator = ";"
|
|
28
|
+
self._schema = schema
|
|
29
|
+
|
|
30
|
+
def create(schema):
|
|
31
|
+
return GeneratorJson(schema)
|
|
32
|
+
|
|
33
|
+
def generate(self):
|
|
34
|
+
xml_database = ElementTree.Element("database")
|
|
35
|
+
result = {
|
|
36
|
+
"name": self._schema.name,
|
|
37
|
+
"description": self._schema.description if self._schema.description else None,
|
|
38
|
+
"collation": self._schema.collation if self._schema.collation else None,
|
|
39
|
+
"tables": [],
|
|
40
|
+
"views": [],
|
|
41
|
+
"procedures": [],
|
|
42
|
+
"sequences": [],
|
|
43
|
+
"scripts": []
|
|
44
|
+
}
|
|
45
|
+
for table in self._schema.tables:
|
|
46
|
+
tableDict = {
|
|
47
|
+
"description": table.description if table.description else None,
|
|
48
|
+
"columns": [],
|
|
49
|
+
"primary_key": {
|
|
50
|
+
"name": table.primary_key.name,
|
|
51
|
+
"description": table.primary_key.description if table.primary_key.description else None,
|
|
52
|
+
"columns": table.primary_key.columns
|
|
53
|
+
} if table.primary_key else None,
|
|
54
|
+
"foreign_keys": [],
|
|
55
|
+
"indexes": [],
|
|
56
|
+
"records": []
|
|
57
|
+
}
|
|
58
|
+
result["tables"].append({table.name: tableDict})
|
|
59
|
+
for column in table.columns:
|
|
60
|
+
columnDict = {
|
|
61
|
+
"name": column.name,
|
|
62
|
+
"description": column.description if column.description else None,
|
|
63
|
+
"data_type": column.data_type.to_str(),
|
|
64
|
+
"is_autoincrement": True if column.is_autoincrement else None,
|
|
65
|
+
"null": True if column.null else None,
|
|
66
|
+
"size": column.size if column.size > 0 else None,
|
|
67
|
+
"default": column.default if column.default else None,
|
|
68
|
+
"precision": column.precision if column.precision > 0 else None,
|
|
69
|
+
"scale": column.scale if column.scale > 0 else None,
|
|
70
|
+
"collation": column.collation if column.collation else None
|
|
71
|
+
}
|
|
72
|
+
tableDict["columns"].append(columnDict)
|
|
73
|
+
for foreign_key in table.foreign_keys:
|
|
74
|
+
foreignKeyDict = {
|
|
75
|
+
"name": foreign_key.name,
|
|
76
|
+
"description": foreign_key.description if foreign_key.description else None,
|
|
77
|
+
"columns": foreign_key.columns,
|
|
78
|
+
"ref_table": foreign_key.ref_table,
|
|
79
|
+
"ref_columns": foreign_key.ref_columns,
|
|
80
|
+
"on_delete": foreign_key.on_delete.name if foreign_key.on_delete != OnDelete.NO_ACTION else None,
|
|
81
|
+
"on_update": foreign_key.on_update.name if foreign_key.on_update != OnUpdate.NO_ACTION else None
|
|
82
|
+
}
|
|
83
|
+
tableDict["foreign_keys"].append(foreignKeyDict)
|
|
84
|
+
for index in table.indexes:
|
|
85
|
+
indexDict = {
|
|
86
|
+
"name": index.name,
|
|
87
|
+
"description": index.description if index.description else None,
|
|
88
|
+
"unique": index.unique if index.unique else None,
|
|
89
|
+
"columns": index.columns
|
|
90
|
+
}
|
|
91
|
+
tableDict["indexes"].append(indexDict)
|
|
92
|
+
for record in table.records:
|
|
93
|
+
recordDict = {
|
|
94
|
+
"values": record.values
|
|
95
|
+
}
|
|
96
|
+
tableDict["records"].append(recordDict)
|
|
97
|
+
for view in self._schema.views:
|
|
98
|
+
viewDict = {
|
|
99
|
+
"name": view.name,
|
|
100
|
+
"description": view.description if view.description else None,
|
|
101
|
+
"content": view.content
|
|
102
|
+
}
|
|
103
|
+
result["views"].append(viewDict)
|
|
104
|
+
for procedure in self._schema.procedures:
|
|
105
|
+
procedureDict = {
|
|
106
|
+
"name": procedure.name,
|
|
107
|
+
"description": procedure.description if procedure.description else None,
|
|
108
|
+
"arguments": [],
|
|
109
|
+
"content": procedure.content
|
|
110
|
+
}
|
|
111
|
+
result["procedures"].append(procedureDict)
|
|
112
|
+
for argument in procedure.arguments:
|
|
113
|
+
argumentDict = {
|
|
114
|
+
"name": argument.name,
|
|
115
|
+
"description": argument.description if argument.description else None,
|
|
116
|
+
"data_type": argument.data_type.to_str(),
|
|
117
|
+
"size": argument.size if argument.size > 0 else None,
|
|
118
|
+
"precision": argument.precision if argument.precision > 0 else None,
|
|
119
|
+
"scale": argument.scale if argument.scale > 0 else None,
|
|
120
|
+
"null": True if argument.null else None,
|
|
121
|
+
"direction": argument.direction
|
|
122
|
+
}
|
|
123
|
+
procedureDict["arguments"].append(argumentDict)
|
|
124
|
+
for sequence in self._schema.sequences:
|
|
125
|
+
sequenceDict = {
|
|
126
|
+
"name": sequence.name,
|
|
127
|
+
"description": sequence.description if sequence.description else None,
|
|
128
|
+
"init_value": sequence.init_value,
|
|
129
|
+
"increment_by": sequence.increment_by
|
|
130
|
+
}
|
|
131
|
+
result["sequences"].append(sequenceDict)
|
|
132
|
+
for script in self._schema.scripts:
|
|
133
|
+
scriptDict = {
|
|
134
|
+
"name": script.name,
|
|
135
|
+
"description": script.description if script.description else None,
|
|
136
|
+
"content": script.content
|
|
137
|
+
}
|
|
138
|
+
result["scripts"].append(scriptDict)
|
|
139
|
+
clean_obj = remove_none(result)
|
|
140
|
+
return json.dumps(clean_obj, indent=4)
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
from .db_schema import OnDelete, OnUpdate
|
|
2
|
+
|
|
3
|
+
class GeneratorSql:
|
|
4
|
+
|
|
5
|
+
def __init__(self, schema):
|
|
6
|
+
self._separator = ";"
|
|
7
|
+
self._schema = schema
|
|
8
|
+
|
|
9
|
+
# methods
|
|
10
|
+
def create(schema):
|
|
11
|
+
return GeneratorSql(schema)
|
|
12
|
+
|
|
13
|
+
# to override
|
|
14
|
+
def identity(self):
|
|
15
|
+
return "IDENTITY"
|
|
16
|
+
def default(self, value):
|
|
17
|
+
if value == "now":
|
|
18
|
+
return "getDate()"
|
|
19
|
+
return value
|
|
20
|
+
def data_type_name(self, column):
|
|
21
|
+
return column.data_type.name.upper()
|
|
22
|
+
def disable_foreign_keys(self):
|
|
23
|
+
return ""
|
|
24
|
+
def enable_foreign_keys(self):
|
|
25
|
+
return ""
|
|
26
|
+
|
|
27
|
+
# methods
|
|
28
|
+
def generate(self):
|
|
29
|
+
sql = []
|
|
30
|
+
for table in self._schema.tables:
|
|
31
|
+
sql.append(self.create_table(table))
|
|
32
|
+
# fks
|
|
33
|
+
sql.append(f"--- fks")
|
|
34
|
+
for table in self._schema.tables:
|
|
35
|
+
for foreign_key in table.foreign_keys:
|
|
36
|
+
line = self.create_table_foreign_key(table, foreign_key)
|
|
37
|
+
sql.append(line)
|
|
38
|
+
# inserts
|
|
39
|
+
sql.append(f"--- inserts")
|
|
40
|
+
for table in self._schema.tables:
|
|
41
|
+
for record in table.records:
|
|
42
|
+
sql.append(self.insert_record(table, record))
|
|
43
|
+
# return
|
|
44
|
+
return "\n".join(sql)
|
|
45
|
+
|
|
46
|
+
def create_table(self, table):
|
|
47
|
+
sql = []
|
|
48
|
+
sql.append(f"--- table {table.name}")
|
|
49
|
+
sql.append(f"CREATE TABLE {table.name} (")
|
|
50
|
+
column_index = 0
|
|
51
|
+
for column in table.columns:
|
|
52
|
+
line = []
|
|
53
|
+
line.append(" ")
|
|
54
|
+
line.append(column.name)
|
|
55
|
+
if column.precision > 0 or column.scale > 0:
|
|
56
|
+
line.append(f"{self.data_type_name(column)}({column.precision},{column.scale})")
|
|
57
|
+
elif column.size > 0:
|
|
58
|
+
line.append(f"{self.data_type_name(column)}({column.size})")
|
|
59
|
+
else:
|
|
60
|
+
line.append(f"{self.data_type_name(column)}")
|
|
61
|
+
if column.is_autoincrement:
|
|
62
|
+
line.append(self.identity())
|
|
63
|
+
if column.null:
|
|
64
|
+
line.append(f"NULL")
|
|
65
|
+
else:
|
|
66
|
+
line.append(f"NOT NULL")
|
|
67
|
+
if column.default != None:
|
|
68
|
+
line.append(f" DEFAULT {self.default(column.default)}")
|
|
69
|
+
if column.description != "":
|
|
70
|
+
line.append(f"--- {column.description}")
|
|
71
|
+
sql.append(" ".join(line) + ",")
|
|
72
|
+
column_index += 1
|
|
73
|
+
if table.primary_key != None:
|
|
74
|
+
sql.append(f" PRIMARY KEY ({",".join(table.primary_key.columns)}), ")
|
|
75
|
+
sql.append(f"){self._separator}")
|
|
76
|
+
for index in table.indexes:
|
|
77
|
+
sql.append(self.create_table_index(table, index))
|
|
78
|
+
return "\n".join(sql)
|
|
79
|
+
|
|
80
|
+
def alter_table(self, table):
|
|
81
|
+
return ""
|
|
82
|
+
|
|
83
|
+
def drop_table(self, table):
|
|
84
|
+
return f"DROP TABLE {table.name}{self._separator}"
|
|
85
|
+
|
|
86
|
+
def create_table_foreign_key(self, table, foreign_key):
|
|
87
|
+
line = f"ALTER TABLE {table.name} ADD CONSTRAINT {foreign_key.name} FOREIGN KEY ({",".join(foreign_key.columns)}) REFERENCES {foreign_key.ref_table} ({",".join(foreign_key.ref_columns)}) "
|
|
88
|
+
if foreign_key.on_delete != OnDelete.NO_ACTION:
|
|
89
|
+
line += "ON DELETE " + foreign_key.on_delete.name.replace("_"," ")
|
|
90
|
+
if foreign_key.on_update != OnUpdate.NO_ACTION:
|
|
91
|
+
line += "ON UPDATE " + foreign_key.on_update.name.replace("_"," ")
|
|
92
|
+
return line + self._separator
|
|
93
|
+
|
|
94
|
+
def create_table_index(self, table, index):
|
|
95
|
+
return f"CREATE {"UNIQUE" if index.unique else ""} INDEX {index.name} ON {table.name} ({' '.join(index.columns)}){self._separator}"
|
|
96
|
+
|
|
97
|
+
def insert_record(self, table, record):
|
|
98
|
+
sql = []
|
|
99
|
+
sql.append(f"INSERT INTO {table.name} (")
|
|
100
|
+
index = 0
|
|
101
|
+
for key in record.values:
|
|
102
|
+
if index > 0:
|
|
103
|
+
sql.append(",")
|
|
104
|
+
sql.append(key)
|
|
105
|
+
index += 1
|
|
106
|
+
sql.append(") VALUES (")
|
|
107
|
+
index = 0
|
|
108
|
+
for key in record.values:
|
|
109
|
+
if index > 0:
|
|
110
|
+
sql.append(",")
|
|
111
|
+
value = record.values[key]
|
|
112
|
+
sql.append(self.format_value(value))
|
|
113
|
+
index += 1
|
|
114
|
+
sql.append(f"){self._separator}")
|
|
115
|
+
return " ".join(sql)
|
|
116
|
+
|
|
117
|
+
def format_value(self, value):
|
|
118
|
+
return "'" + value.replace("'","''") + "'"
|
|
119
|
+
|
|
120
|
+
def create_view(self, view):
|
|
121
|
+
pass
|
|
122
|
+
def alter_view(self, view):
|
|
123
|
+
pass
|
|
124
|
+
def drop_view(self, view):
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
def create_procedure(self, procedure):
|
|
128
|
+
pass
|
|
129
|
+
def alter_procedure(self, procedure):
|
|
130
|
+
pass
|
|
131
|
+
def drop_procedure(self, procedure):
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
def create_sequence(self, sequence):
|
|
135
|
+
pass
|
|
136
|
+
def alter_sequence(self, sequence):
|
|
137
|
+
pass
|
|
138
|
+
def drop_sequence(self, sequence):
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
def generate_diff(self, other_schema):
|
|
142
|
+
result = []
|
|
143
|
+
for table in self._schema.tables:
|
|
144
|
+
# get table with same name in other schema
|
|
145
|
+
other_table = None
|
|
146
|
+
for target_table in other_schema.tables:
|
|
147
|
+
if target_table.name == table.name:
|
|
148
|
+
other_table = target_table
|
|
149
|
+
break
|
|
150
|
+
# if not found, create table
|
|
151
|
+
if other_table == None:
|
|
152
|
+
result.append(self.create_table(table))
|
|
153
|
+
# add fks
|
|
154
|
+
for foreign_key in table.foreign_keys:
|
|
155
|
+
result.append(self.create_table_foreign_key(table, foreign_key))
|
|
156
|
+
# add indexes
|
|
157
|
+
for index in table.indexes:
|
|
158
|
+
result.append(self.create_table_index(table, index))
|
|
159
|
+
continue
|
|
160
|
+
else:
|
|
161
|
+
# add new columns
|
|
162
|
+
for column in table.columns:
|
|
163
|
+
other_column = None
|
|
164
|
+
for target_column in other_table.columns:
|
|
165
|
+
if target_column.name == column.name:
|
|
166
|
+
other_column = target_column
|
|
167
|
+
break
|
|
168
|
+
if other_column == None:
|
|
169
|
+
result.append(self.alter_table(table))
|
|
170
|
+
# drop unexisting old columns
|
|
171
|
+
pass
|
|
172
|
+
# change columns data types, defaults, nulls, autoincrements, ...
|
|
173
|
+
pass
|
|
174
|
+
|
|
175
|
+
# create fks
|
|
176
|
+
for table in self._schema.tables:
|
|
177
|
+
# get table with same name in other schema
|
|
178
|
+
other_table = None
|
|
179
|
+
for target_table in other_schema.tables:
|
|
180
|
+
if target_table.name == table.name:
|
|
181
|
+
other_table = target_table
|
|
182
|
+
break
|
|
183
|
+
# for each fk
|
|
184
|
+
if other_table != None:
|
|
185
|
+
for foreign_key in table.foreign_keys:
|
|
186
|
+
other_foreign_key = None
|
|
187
|
+
for target_foreign_key in other_table.foreign_keys:
|
|
188
|
+
if target_foreign_key.name == foreign_key.name:
|
|
189
|
+
other_foreign_key = target_foreign_key
|
|
190
|
+
break
|
|
191
|
+
if other_foreign_key == None:
|
|
192
|
+
result.append(self.create_table_foreign_key(table, foreign_key))
|
|
193
|
+
# drop unexisting foreign keys
|
|
194
|
+
pass
|
|
195
|
+
# create indexes
|
|
196
|
+
for table in self._schema.tables:
|
|
197
|
+
# get table with same name in other schema
|
|
198
|
+
other_table = None
|
|
199
|
+
for target_table in other_schema.tables:
|
|
200
|
+
if target_table.name == table.name:
|
|
201
|
+
other_table = target_table
|
|
202
|
+
break
|
|
203
|
+
# for each index
|
|
204
|
+
if other_table != None:
|
|
205
|
+
for index in table.indexes:
|
|
206
|
+
other_index = None
|
|
207
|
+
for target_index in other_table.indexes:
|
|
208
|
+
if target_index.name == index.name:
|
|
209
|
+
other_index = target_index
|
|
210
|
+
break
|
|
211
|
+
if other_index == None:
|
|
212
|
+
result.append(self.create_table_index(table, index))
|
|
213
|
+
# drop unexisting indexes
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
# drop unexisting tables
|
|
217
|
+
for table in other_schema.tables:
|
|
218
|
+
# get table with same name in other schema
|
|
219
|
+
other_table = None
|
|
220
|
+
for target_table in self._schema.tables:
|
|
221
|
+
if target_table.name == table.name:
|
|
222
|
+
other_table = target_table
|
|
223
|
+
break
|
|
224
|
+
# if not found, drop table
|
|
225
|
+
if other_table == None:
|
|
226
|
+
result.append(self.drop_table(table))
|
|
227
|
+
continue
|
|
228
|
+
# return
|
|
229
|
+
return "\n".join(result)
|
|
230
|
+
|
|
231
|
+
class GeneratorSqlSqllite(GeneratorSql):
|
|
232
|
+
|
|
233
|
+
def disable_foreign_keys(self):
|
|
234
|
+
return "PRAGMA foreign_keys = OFF;"
|
|
235
|
+
|
|
236
|
+
def enable_foreign_keys(self):
|
|
237
|
+
return "PRAGMA foreign_keys = ON;"
|