q2rad 0.1.212__tar.gz → 0.1.214__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.

Potentially problematic release.


This version of q2rad might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: q2rad
3
- Version: 0.1.212
3
+ Version: 0.1.214
4
4
  Summary: RAD - database, GUI, reports
5
5
  Author: Andrei Puchko
6
6
  Author-email: andrei.puchko@gmx.de
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "q2rad"
3
- version = "0.1.212"
3
+ version = "0.1.214"
4
4
  description = "RAD - database, GUI, reports"
5
5
  authors = ["Andrei Puchko <andrei.puchko@gmx.de>"]
6
6
  readme = "README.md"
@@ -96,7 +96,7 @@ class AppManager(Q2Form):
96
96
  "App URL",
97
97
  control="line",
98
98
  data=q2app.q2_app.app_url,
99
- disabled=1,
99
+ readonly=1,
100
100
  )
101
101
  if q2app.q2_app.app_version:
102
102
  self.add_control(
@@ -104,20 +104,20 @@ class AppManager(Q2Form):
104
104
  "App version",
105
105
  control="line",
106
106
  data=q2app.q2_app.app_version,
107
- disabled=1,
107
+ readonly=1,
108
108
  )
109
109
  self.add_control(
110
110
  "drl",
111
111
  "Database type",
112
112
  data=app_data["driver_logic"].lower(),
113
- disabled=1,
113
+ readonly=1,
114
114
  datalen=len(app_data["driver_logic"].strip()) + 5,
115
115
  )
116
116
  self.add_control(
117
117
  "dtl",
118
118
  "Database name ",
119
119
  data=app_data["database_logic"],
120
- disabled=1,
120
+ readonly=1,
121
121
  datalen=len(app_data["database_logic"].strip()),
122
122
  )
123
123
  if app_data.get("host_logic"):
@@ -125,7 +125,7 @@ class AppManager(Q2Form):
125
125
  "hl",
126
126
  "Host",
127
127
  data=app_data["host_logic"],
128
- disabled=1,
128
+ readonly=1,
129
129
  datalen=len(app_data["host_logic"].strip()),
130
130
  )
131
131
  if num(app_data.get("port_logic")):
@@ -133,12 +133,21 @@ class AppManager(Q2Form):
133
133
  "pl",
134
134
  "Port",
135
135
  data=app_data["port_logic"],
136
- disabled=1,
136
+ readonly=1,
137
137
  datalen=len(app_data["port_logic"]) + 5,
138
138
  )
139
139
  self.add_control("/")
140
140
 
141
141
  if self.add_control("/h", ""):
142
+ self.add_control(
143
+ "exts",
144
+ "Extensions",
145
+ control="button",
146
+ # datalen=13,
147
+ valid=q2app.q2_app.run_extensions,
148
+ )
149
+
150
+ self.add_control("/s")
142
151
  if self.add_control("/h", "Export"):
143
152
  self.add_control(
144
153
  "save_app",
@@ -188,14 +197,14 @@ class AppManager(Q2Form):
188
197
  "drd",
189
198
  "Database type",
190
199
  data=app_data["driver_data"].lower(),
191
- disabled=1,
200
+ readonly=1,
192
201
  datalen=len(app_data["driver_data"].strip()) + 5,
193
202
  )
194
203
  self.add_control(
195
204
  "dtd",
196
205
  "Database name ",
197
206
  data=app_data["database_data"],
198
- disabled=1,
207
+ readonly=1,
199
208
  datalen=len(app_data["database_data"].strip()),
200
209
  )
201
210
  if app_data.get("host_data"):
@@ -203,7 +212,7 @@ class AppManager(Q2Form):
203
212
  "hd",
204
213
  "Host",
205
214
  data=app_data["host_data"],
206
- disabled=1,
215
+ readonly=1,
207
216
  datalen=len(app_data["host_data"].strip()),
208
217
  )
209
218
  if num(app_data.get("port_data")):
@@ -211,7 +220,7 @@ class AppManager(Q2Form):
211
220
  "pd",
212
221
  "Port",
213
222
  data=app_data["port_data"],
214
- disabled=1,
223
+ readonly=1,
215
224
  datalen=len(app_data["port_data"]) + 5,
216
225
  )
217
226
  self.add_control("/")
@@ -321,16 +330,23 @@ class AppManager(Q2Form):
321
330
  json.dump(app_json, open(file, "w"), indent=1)
322
331
 
323
332
  def get_app_json(self):
333
+ return AppManager._get_app_json()
334
+
335
+ @staticmethod
336
+ def _get_app_json(prefix=""):
324
337
  db: Q2Db = q2app.q2_app.db_logic
325
338
  rez = {}
326
- for x in db.get_tables():
327
- if x not in app_tables:
339
+ for table in db.get_tables():
340
+ if table not in app_tables:
328
341
  continue
329
- if x.startswith("log_") or x == "sqlite_sequence":
342
+ if table.startswith("log_") or table == "sqlite_sequence":
330
343
  continue
331
- rez[x] = []
332
- for row in db.table(x).records():
333
- rez[x].append(row)
344
+ rez[table] = []
345
+ for row in db.table(table).records():
346
+ if prefix:
347
+ if table != "packages" and not row.get("name").startswith(prefix):
348
+ continue
349
+ rez[table].append(row)
334
350
  return rez
335
351
 
336
352
  def tables_selector(self, mode="export"):
@@ -440,19 +456,23 @@ class AppManager(Q2Form):
440
456
  self.q2_app.open_selected_app()
441
457
 
442
458
  @staticmethod
443
- def import_json_app(data, db=None):
459
+ def import_json_app(data, db=None, prefix=""):
444
460
  if db is None:
445
461
  db: Q2Db = q2app.q2_app.db_logic
446
462
  db_tables = db.get_tables()
447
463
  wait_table = Q2WaitShow(len(data))
448
464
  errors = []
465
+ db.transaction()
449
466
  for table in data:
450
467
  wait_table.step(table)
451
468
  if table not in db_tables:
452
469
  continue
453
470
  wait_row = Q2WaitShow(len(data[table]))
454
471
  if table != "packages":
455
- db.cursor(f'delete from `{table}` where name not like "\\_%"')
472
+ if prefix:
473
+ db.cursor(f'delete from `{table}` where substr(name,1,{len(prefix)}) = "{prefix}"')
474
+ else:
475
+ db.cursor(f'delete from `{table}` where substr(name,1,1) <> "_"')
456
476
  if db.last_sql_error:
457
477
  errors.append(db.last_sql_error)
458
478
  for row in data[table]:
@@ -463,12 +483,18 @@ class AppManager(Q2Form):
463
483
  == row["package_name"]
464
484
  ):
465
485
  continue
466
- if row.get("name", "").startswith("_"):
467
- continue
486
+ else:
487
+ if prefix:
488
+ if not row.get("name").startswith(prefix):
489
+ continue
490
+ else:
491
+ if row.get("name", "").startswith("_"):
492
+ continue
468
493
  if not db.insert(table, row):
469
494
  errors.append(db.last_sql_error)
470
495
  errors.append(db.last_record)
471
496
  wait_row.close()
497
+ db.commit()
472
498
  wait_table.close()
473
499
  if errors:
474
500
  q2Mess("<br>".join(errors))
@@ -491,6 +517,7 @@ class AppManager(Q2Form):
491
517
  db: Q2Db = q2app.q2_app.db_data
492
518
  db_tables = db.get_tables()
493
519
  wait_table = Q2WaitShow(len(data))
520
+ db.transaction()
494
521
  errors = []
495
522
  for table in data:
496
523
  wait_table.step(table)
@@ -506,6 +533,7 @@ class AppManager(Q2Form):
506
533
  errors.append(db.last_sql_error)
507
534
  # print(db.last_sql_error)
508
535
  wait_row.close()
536
+ db.commit()
509
537
  wait_table.close()
510
538
  if errors:
511
539
  q2Mess("<br>".join(errors))
@@ -0,0 +1,142 @@
1
+ # Copyright © 2021 Andrei Puchko
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ import os
17
+ from datetime import datetime
18
+ from q2db.cursor import Q2Cursor
19
+ from q2gui.q2model import Q2CursorModel
20
+ from q2gui.q2dialogs import q2Mess, q2AskYN, q2working
21
+
22
+ from q2rad.q2utils import Q2Form
23
+ from q2gui import q2app
24
+ from q2rad.q2appmanager import AppManager
25
+ from q2terminal.q2terminal import Q2Terminal
26
+ import gettext
27
+ import json
28
+
29
+
30
+ _ = gettext.gettext
31
+
32
+
33
+ class Q2Extensions(Q2Form):
34
+ def __init__(self, title=""):
35
+ super().__init__("Extensions")
36
+ self.no_view_action = True
37
+
38
+ def on_init(self):
39
+ self.db = q2app.q2_app.db_data
40
+ self.add_control("prefix", _("Name"), datatype="char", datalen=50, pk="*")
41
+ self.add_control("seq", _("Sequence number"), datatype="int")
42
+ self.add_control("version", _("Version"), datatype="char", datalen=16, readonly=True)
43
+ self.add_control("checkupdates", _("Check for updatea"), control="check", datatype="char", datalen=1)
44
+ self.add_control("comment", _("Comment"), datatype="text")
45
+
46
+ cursor: Q2Cursor = self.q2_app.db_data.table(table_name="extensions")
47
+ model = Q2CursorModel(cursor)
48
+ model.set_order("seq").refresh()
49
+ self.set_model(model)
50
+ self.add_action("/crud")
51
+ self.add_action("Export|as JSON file", self.export_json, eof_disabled=True)
52
+ self.add_action("Export|to q2Market", self.export_q2market, eof_disabled=True)
53
+ self.add_action("Import|from JSON file", self.import_json, eof_disabled=True)
54
+ self.add_action("Import|from q2Market", self.import_q2market, eof_disabled=True)
55
+
56
+ def info(self):
57
+ pass
58
+
59
+ def export_json(self, file=""):
60
+ prefix = self.r.prefix
61
+ filetype = "JSON(*.json)"
62
+ if not file:
63
+ desktop = os.path.expanduser("~/Desktop")
64
+ file = f"{desktop}/{prefix}.json"
65
+ file, filetype = q2app.q2_app.get_save_file_dialoq(
66
+ f"Export Extension ({prefix})", filter=filetype, path=file
67
+ )
68
+ if not file:
69
+ return
70
+ file = self.validate_impexp_file_name(file, filetype)
71
+ if file:
72
+
73
+ def get_ext_json(prefix=prefix):
74
+ return AppManager._get_app_json(prefix)
75
+
76
+ ext_json = q2working(get_ext_json, "Prepare data...")
77
+ if ext_json:
78
+ json.dump(ext_json, open(file, "w"), indent=1)
79
+
80
+ def import_json(self, file=""):
81
+ prefix = self.r.prefix
82
+ filetype = "JSON(*.json)"
83
+ if not file:
84
+ file, filetype = q2app.q2_app.get_open_file_dialoq(
85
+ f"Import Extension ({prefix})", filter=filetype
86
+ )
87
+
88
+ if not file or not os.path.isfile(file):
89
+ return
90
+
91
+ data = json.load(open(file))
92
+ if data:
93
+ AppManager.import_json_app(data, prefix=prefix)
94
+ self.q2_app.open_selected_app()
95
+
96
+ def export_q2market(self):
97
+ prefix = self.r.prefix
98
+ if not self.q2_app.app_url:
99
+ q2Mess("No App URL!")
100
+ return
101
+ if (
102
+ q2AskYN(
103
+ f"<p>You are about to export Extension ({prefix}) "
104
+ f"<p>into folder {os.path.abspath(self.q2_app.q2market_path)}"
105
+ "<p>Are you sure?"
106
+ )
107
+ != 2
108
+ ):
109
+ return
110
+
111
+ q2market_file = f"{self.q2_app.q2market_path}/q2market.json"
112
+ if os.path.isfile(q2market_file):
113
+ q2market = json.load(open(q2market_file))
114
+ else:
115
+ q2market = {}
116
+
117
+ version = datetime.now().strftime(r"%Y-%m-%d %H:%M:%S")
118
+ ext_url = f"{os.path.dirname(self.q2_app.app_url)}/{prefix}"
119
+ q2market[ext_url] = {
120
+ "ext_title": prefix,
121
+ "ext_version": version,
122
+ "ext_description": self.r.comment,
123
+ }
124
+ json.dump(q2market, open(q2market_file, "w"), indent=2)
125
+ open(f"{self.q2_app.q2market_path}/{prefix}.version", "w").write(version)
126
+ self.db.update("extensions", {"prefix": prefix, "version": version})
127
+
128
+ self.export_json(f"{self.q2_app.q2market_path}/{prefix}.json")
129
+
130
+ trm = Q2Terminal(callback=print)
131
+
132
+ def worker():
133
+ trm.run(f"cd {self.q2_app.q2market_path}")
134
+ trm.run("git add -A")
135
+ trm.run(f"""git commit -a -m"{version}" """)
136
+
137
+ q2working(worker, "Commiting")
138
+ q2Mess(trm.run("""git push"""))
139
+ trm.close()
140
+
141
+ def import_q2market(self):
142
+ q2app.q2_app.check_ext_update(self.r.prefix, force_update=True)
@@ -41,9 +41,10 @@ class Q2Market(Q2Form):
41
41
  data = json.loads(read_url(q2market_catalogue_url).decode("utf-8"))
42
42
  rez = []
43
43
  for x in data:
44
- rec = data[x]
45
- rec["app_url"] = x
46
- rez.append(rec)
44
+ if "app_title" in data[x]:
45
+ rec = data[x]
46
+ rec["app_url"] = x
47
+ rez.append(rec)
47
48
  model = Q2Model()
48
49
  model.set_records(rez)
49
50
  self.set_model(model)
@@ -57,6 +57,7 @@ from q2rad.q2forms import Q2Forms
57
57
  from q2rad.q2lines import Q2Lines
58
58
  from q2rad.q2market import Q2Market
59
59
  from q2rad.q2packages import Q2Packages
60
+ from q2rad.q2extensions import Q2Extensions
60
61
  from q2rad.q2constants import Q2Constants, q2const
61
62
  from q2rad.q2queries import Q2Queries
62
63
  from q2rad.q2reports import Q2Reports, Q2RadReport
@@ -111,44 +112,57 @@ def run_form(form_name, order="", where=""):
111
112
 
112
113
 
113
114
  def run_module(module_name=None, _globals={}, _locals=locals(), script="", import_only=False):
115
+ ext_modules = []
114
116
  if module_name is not None:
115
117
  script = q2app.q2_app.db_logic.get("modules", f"name = '{module_name}'", "script")
116
- if not script:
117
- return
118
- code = q2app.q2_app.code_compiler(script)
119
- if code["code"] is False:
120
- msg = code["error"]
121
- if threading.current_thread() is threading.main_thread():
122
- q2Mess(f"{msg}".replace("\n", "<br>").replace(" ", "&nbsp;"))
123
- print(f"{msg}")
124
- print("-" * 25)
125
- _logger.error(msg)
126
- return
127
- else:
128
- if import_only:
129
- __name__ = ""
118
+ for row in q2cursor("select prefix from extensions order by seq").records():
119
+ ext_module_name = row["prefix"] + module_name
120
+ if get("modules", f"name='{ext_module_name}'", "name", q2app.q2_app.db_logic) == ext_module_name:
121
+ ext_modules.append(ext_module_name)
122
+ if script:
123
+ code = q2app.q2_app.code_compiler(script)
124
+ if code["code"] is False:
125
+ msg = code["error"]
126
+ if threading.current_thread() is threading.main_thread():
127
+ q2Mess(f"{msg}".replace("\n", "<br>").replace(" ", "&nbsp;"))
128
+ print(f"{msg}")
129
+ print("-" * 25)
130
+ _logger.error(msg)
131
+ return
130
132
  else:
131
- __name__ = "__main__"
133
+ if import_only:
134
+ __name__ = ""
135
+ else:
136
+ __name__ = "__main__"
137
+
138
+ _locals.update(
139
+ {
140
+ "RETURN": None,
141
+ "ReturnEvent": ReturnEvent,
142
+ "self": q2app.q2_app,
143
+ "q2_app": q2app.q2_app,
144
+ "myapp": q2app.q2_app,
145
+ "__name__": __name__,
146
+ }
147
+ )
148
+ _globals.update(globals())
149
+ _globals.update(_locals)
150
+ try:
151
+ exec(code["code"], _globals)
152
+ except ReturnEvent as error:
153
+ pass
154
+ except Exception as error:
155
+ explain_error()
132
156
 
133
- _locals.update(
134
- {
135
- "RETURN": None,
136
- "ReturnEvent": ReturnEvent,
137
- "self": q2app.q2_app,
138
- "q2_app": q2app.q2_app,
139
- "myapp": q2app.q2_app,
140
- "__name__": __name__,
141
- }
142
- )
143
- _globals.update(globals())
144
- _globals.update(_locals)
145
- try:
146
- exec(code["code"], _globals)
147
- except ReturnEvent as error:
148
- pass
149
- except Exception as error:
150
- explain_error()
151
- return _globals["RETURN"]
157
+ if ext_modules:
158
+ res = None
159
+ for mdl in ext_modules:
160
+ res = run_module(mdl, _globals=_globals, _locals=_locals, import_only=import_only)
161
+
162
+ if res is not None:
163
+ return res
164
+
165
+ return _globals.get("RETURN")
152
166
 
153
167
 
154
168
  def explain_error(tb=None, errtype=None):
@@ -300,6 +314,7 @@ class Q2RadApp(Q2App):
300
314
  if self.selected_application != {}:
301
315
  self.open_selected_app(True)
302
316
  self.check_app_update()
317
+ self.check_ext_update()
303
318
  self.on_new_tab()
304
319
  else:
305
320
  self.close()
@@ -326,6 +341,7 @@ class Q2RadApp(Q2App):
326
341
  else:
327
342
  self.set_title(f"{self.selected_application.get('name', '')}")
328
343
  self.run_module("autorun")
344
+ self.run_module("_autorun")
329
345
 
330
346
  if go_to_q2market and (
331
347
  max(
@@ -368,7 +384,10 @@ class Q2RadApp(Q2App):
368
384
  )
369
385
  for column in cu.records():
370
386
  data_schema.add(**column)
371
- for form in (Q2Constants(),):
387
+ for form in (
388
+ Q2Constants(),
389
+ Q2Extensions(),
390
+ ):
372
391
  for x in form.get_table_schema():
373
392
  data_schema.add(**x)
374
393
 
@@ -894,6 +913,45 @@ class Q2RadApp(Q2App):
894
913
  AppManager.import_json_app(data)
895
914
  self.open_selected_app()
896
915
 
916
+ def check_ext_update(self, prefix="", force_update=False):
917
+ if self.frozen:
918
+ return
919
+ if prefix:
920
+ cu = q2cursor(f"select * from extensions where prefix='{prefix}'")
921
+ else:
922
+ cu = q2cursor(f"select * from extensions order by seq")
923
+ for row in cu.records():
924
+ _prefix = row["prefix"]
925
+ ext_url = f"{os.path.dirname(self.app_url)}/{_prefix}"
926
+ ext_version = row["version"]
927
+ if not os.path.isdir(self.q2market_path) or force_update:
928
+ try:
929
+ market_version = read_url(ext_url + ".version").decode("utf-8") # noqa F405
930
+ except Exception as e: # noqa F841
931
+ self.show_statusbar_mess("An error occurred while checking for updates")
932
+ return
933
+ if force_update or market_version and market_version != ext_version:
934
+ if force_update:
935
+ update_detected = (
936
+ f"You are about to rewrite current Extension ({_prefix}) <b>{self.app_title}</b>!"
937
+ )
938
+ else:
939
+ update_detected = (
940
+ f"Update for Extension ({_prefix}) <b>{self.app_title}</b> detected!"
941
+ )
942
+ if (
943
+ q2AskYN(
944
+ f"{update_detected}"
945
+ f"<p>Current version <b>{ext_version}</b>"
946
+ f"<p>New version <b>{market_version}</b>"
947
+ "<p>Download and install?"
948
+ )
949
+ == 2
950
+ ):
951
+ data = json.load(open_url(ext_url + ".json")) # noqa F405
952
+ AppManager.import_json_app(data, prefix=_prefix)
953
+ self.open_selected_app()
954
+
897
955
  def update_app_packages(self):
898
956
  if self.frozen:
899
957
  return
@@ -1029,6 +1087,9 @@ class Q2RadApp(Q2App):
1029
1087
  def run_packages(self):
1030
1088
  Q2Packages().run()
1031
1089
 
1090
+ def run_extensions(self):
1091
+ Q2Extensions().run()
1092
+
1032
1093
  def make_binary(self):
1033
1094
  make_binary(self)
1034
1095
 
@@ -1144,6 +1205,7 @@ class Q2RadApp(Q2App):
1144
1205
  return self.get_form(child_form_name)
1145
1206
 
1146
1207
  return worker
1208
+
1147
1209
  form.add_action(
1148
1210
  x["action_text"],
1149
1211
  self.code_runner(x["action_worker"]) if x["action_worker"] else None,
@@ -1214,8 +1276,8 @@ class Q2RadApp(Q2App):
1214
1276
  # import
1215
1277
  if re.findall(r"^\s*import\W*.*", x):
1216
1278
  module = x.split("import")[1].strip()
1217
- if self.db_logic.get("modules", f"name='{module}'", "name"):
1218
- x = x.split("import")[0] + f"run_module('{module}', import_only=True)"
1279
+ # if self.db_logic.get("modules", f"name='{module}'", "name"):
1280
+ x = x.split("import")[0] + f"run_module('{module}', import_only=True)"
1219
1281
 
1220
1282
  new_script_lines.append(x)
1221
1283
  script = "\n".join(new_script_lines)
@@ -0,0 +1 @@
1
+ __version__ = "0.1.214"
@@ -1 +0,0 @@
1
- __version__ = "0.1.212"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes