dsw-database 4.12.0__tar.gz → 4.14.0__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.
- {dsw_database-4.12.0/dsw_database.egg-info → dsw_database-4.14.0}/PKG-INFO +4 -4
- {dsw_database-4.12.0 → dsw_database-4.14.0}/dsw/database/build_info.py +4 -4
- {dsw_database-4.12.0 → dsw_database-4.14.0}/dsw/database/database.py +85 -65
- {dsw_database-4.12.0 → dsw_database-4.14.0}/dsw/database/model.py +34 -35
- {dsw_database-4.12.0 → dsw_database-4.14.0/dsw_database.egg-info}/PKG-INFO +4 -4
- {dsw_database-4.12.0 → dsw_database-4.14.0}/dsw_database.egg-info/requires.txt +1 -1
- {dsw_database-4.12.0 → dsw_database-4.14.0}/pyproject.toml +4 -4
- {dsw_database-4.12.0 → dsw_database-4.14.0}/LICENSE +0 -0
- {dsw_database-4.12.0 → dsw_database-4.14.0}/README.md +0 -0
- {dsw_database-4.12.0 → dsw_database-4.14.0}/dsw/database/__init__.py +0 -0
- {dsw_database-4.12.0 → dsw_database-4.14.0}/dsw_database.egg-info/SOURCES.txt +0 -0
- {dsw_database-4.12.0 → dsw_database-4.14.0}/dsw_database.egg-info/dependency_links.txt +0 -0
- {dsw_database-4.12.0 → dsw_database-4.14.0}/dsw_database.egg-info/not-zip-safe +0 -0
- {dsw_database-4.12.0 → dsw_database-4.14.0}/dsw_database.egg-info/top_level.txt +0 -0
- {dsw_database-4.12.0 → dsw_database-4.14.0}/setup.cfg +0 -0
- {dsw_database-4.12.0 → dsw_database-4.14.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dsw-database
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.14.0
|
|
4
4
|
Summary: Library for managing DSW database
|
|
5
5
|
Author-email: Marek Suchánek <marek.suchanek@ds-wizard.org>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -11,16 +11,16 @@ Keywords: dsw,database
|
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
13
|
Classifier: Programming Language :: Python
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Topic :: Database
|
|
17
17
|
Classifier: Topic :: Utilities
|
|
18
|
-
Requires-Python: <4,>=3.
|
|
18
|
+
Requires-Python: <4,>=3.11
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
21
|
Requires-Dist: psycopg[binary]
|
|
22
22
|
Requires-Dist: tenacity
|
|
23
|
-
Requires-Dist: dsw-config==4.
|
|
23
|
+
Requires-Dist: dsw-config==4.14.0
|
|
24
24
|
|
|
25
25
|
# Data Stewardship Wizard: Database
|
|
26
26
|
|
|
@@ -9,9 +9,9 @@ BuildInfo = namedtuple(
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
BUILD_INFO = BuildInfo(
|
|
12
|
-
version='v4.
|
|
13
|
-
built_at='
|
|
14
|
-
sha='
|
|
12
|
+
version='v4.14.0~213910f',
|
|
13
|
+
built_at='2025-01-07 08:17:38Z',
|
|
14
|
+
sha='213910ffb32a7cea98942ccd0f6c52cb6cf79128',
|
|
15
15
|
branch='HEAD',
|
|
16
|
-
tag='v4.
|
|
16
|
+
tag='v4.14.0',
|
|
17
17
|
)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import logging
|
|
3
|
+
import typing
|
|
4
|
+
|
|
3
5
|
import psycopg
|
|
4
6
|
import psycopg.conninfo
|
|
5
7
|
import psycopg.rows
|
|
6
8
|
import psycopg.types.json
|
|
7
9
|
import tenacity
|
|
8
10
|
|
|
9
|
-
from typing import List, Iterable, Optional
|
|
10
|
-
|
|
11
11
|
from dsw.config.model import DatabaseConfig
|
|
12
12
|
|
|
13
13
|
from .model import DBDocumentTemplate, DBDocumentTemplateFile, \
|
|
@@ -28,44 +28,56 @@ def wrap_json_data(data: dict):
|
|
|
28
28
|
return psycopg.types.json.Json(data)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
# pylint: disable-next=too-many-public-methods
|
|
31
32
|
class Database:
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
SELECT_QTN_DOCUMENTS = 'SELECT * FROM document
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
SELECT_DOCUMENT = ('SELECT * FROM document '
|
|
35
|
+
'WHERE uuid = %s AND tenant_uuid = %s LIMIT 1;')
|
|
36
|
+
SELECT_QTN_DOCUMENTS = ('SELECT * FROM document '
|
|
37
|
+
'WHERE questionnaire_uuid = %s AND tenant_uuid = %s;')
|
|
38
|
+
SELECT_DOCUMENT_SUBMISSIONS = ('SELECT * FROM submission '
|
|
39
|
+
'WHERE document_uuid = %s AND tenant_uuid = %s;')
|
|
40
|
+
SELECT_QTN_SUBMISSIONS = ('SELECT s.* '
|
|
41
|
+
'FROM document d JOIN submission s ON d.uuid = s.document_uuid '
|
|
42
|
+
'WHERE d.questionnaire_uuid = %s AND d.tenant_uuid = %s;')
|
|
43
|
+
SELECT_QTN_SIMPLE = ('SELECT qtn.* FROM questionnaire qtn '
|
|
44
|
+
'WHERE qtn.uuid = %s AND qtn.tenant_uuid = %s;')
|
|
45
|
+
SELECT_TENANT_CONFIG = ('SELECT * FROM tenant_config '
|
|
46
|
+
'WHERE uuid = %(tenant_uuid)s LIMIT 1;')
|
|
47
|
+
SELECT_TENANT_LIMIT = ('SELECT uuid, storage FROM tenant_limit_bundle '
|
|
48
|
+
'WHERE uuid = %(tenant_uuid)s LIMIT 1;')
|
|
43
49
|
UPDATE_DOCUMENT_STATE = 'UPDATE document SET state = %s, worker_log = %s WHERE uuid = %s;'
|
|
44
50
|
UPDATE_DOCUMENT_RETRIEVED = 'UPDATE document SET retrieved_at = %s, state = %s WHERE uuid = %s;'
|
|
45
|
-
UPDATE_DOCUMENT_FINISHED = 'UPDATE document SET finished_at = %s, state = %s, '
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
SELECT_TEMPLATE = 'SELECT * FROM document_template
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
UPDATE_DOCUMENT_FINISHED = ('UPDATE document SET finished_at = %s, state = %s, '
|
|
52
|
+
'file_name = %s, content_type = %s, worker_log = %s, '
|
|
53
|
+
'file_size = %s WHERE uuid = %s;')
|
|
54
|
+
SELECT_TEMPLATE = ('SELECT * FROM document_template '
|
|
55
|
+
'WHERE id = %s AND tenant_uuid = %s LIMIT 1;')
|
|
56
|
+
SELECT_TEMPLATE_FILES = ('SELECT * FROM document_template_file '
|
|
57
|
+
'WHERE document_template_id = %s AND tenant_uuid = %s;')
|
|
58
|
+
SELECT_TEMPLATE_ASSETS = ('SELECT * FROM document_template_asset '
|
|
59
|
+
'WHERE document_template_id = %s AND tenant_uuid = %s;')
|
|
60
|
+
CHECK_TABLE_EXISTS = ('SELECT EXISTS(SELECT * FROM information_schema.tables'
|
|
61
|
+
' WHERE table_name = %(table_name)s)')
|
|
62
|
+
SELECT_MAIL_CONFIG = ('SELECT icm.* '
|
|
63
|
+
'FROM tenant_config tc JOIN instance_config_mail icm '
|
|
64
|
+
'ON tc.mail_config_uuid = icm.uuid '
|
|
65
|
+
'WHERE tc.uuid = %(tenant_uuid)s;')
|
|
66
|
+
UPDATE_COMPONENT_INFO = ('INSERT INTO component '
|
|
67
|
+
'(name, version, built_at, created_at, updated_at) '
|
|
68
|
+
'VALUES (%(name)s, %(version)s, %(built_at)s, '
|
|
69
|
+
'%(created_at)s, %(updated_at)s)'
|
|
70
|
+
'ON CONFLICT (name) DO '
|
|
71
|
+
'UPDATE SET version = %(version)s, built_at = %(built_at)s, '
|
|
72
|
+
'updated_at = %(updated_at)s;')
|
|
63
73
|
SELECT_COMPONENT_INFO = 'SELECT * FROM component WHERE name = %(name)s;'
|
|
64
|
-
SUM_FILE_SIZES = 'SELECT (SELECT COALESCE(SUM(file_size)::bigint, 0) '
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
SUM_FILE_SIZES = ('SELECT (SELECT COALESCE(SUM(file_size)::bigint, 0) '
|
|
75
|
+
'FROM document WHERE tenant_uuid = %(tenant_uuid)s) '
|
|
76
|
+
'+ (SELECT COALESCE(SUM(file_size)::bigint, 0) '
|
|
77
|
+
'FROM document_template_asset WHERE tenant_uuid = %(tenant_uuid)s) '
|
|
78
|
+
'+ (SELECT COALESCE(SUM(file_size)::bigint, 0) '
|
|
79
|
+
'FROM questionnaire_file WHERE tenant_uuid = %(tenant_uuid)s) '
|
|
80
|
+
'AS result;')
|
|
69
81
|
|
|
70
82
|
def __init__(self, cfg: DatabaseConfig, connect: bool = True,
|
|
71
83
|
with_queue: bool = True):
|
|
@@ -121,7 +133,7 @@ class Database:
|
|
|
121
133
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
122
134
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
123
135
|
)
|
|
124
|
-
def fetch_document(self, document_uuid: str, tenant_uuid: str) ->
|
|
136
|
+
def fetch_document(self, document_uuid: str, tenant_uuid: str) -> DBDocument | None:
|
|
125
137
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
126
138
|
cursor.execute(
|
|
127
139
|
query=self.SELECT_DOCUMENT,
|
|
@@ -139,7 +151,7 @@ class Database:
|
|
|
139
151
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
140
152
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
141
153
|
)
|
|
142
|
-
def fetch_tenant_config(self, tenant_uuid: str) ->
|
|
154
|
+
def fetch_tenant_config(self, tenant_uuid: str) -> DBTenantConfig | None:
|
|
143
155
|
return self.get_tenant_config(tenant_uuid)
|
|
144
156
|
|
|
145
157
|
@tenacity.retry(
|
|
@@ -149,7 +161,7 @@ class Database:
|
|
|
149
161
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
150
162
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
151
163
|
)
|
|
152
|
-
def fetch_tenant_limits(self, tenant_uuid: str) ->
|
|
164
|
+
def fetch_tenant_limits(self, tenant_uuid: str) -> DBTenantLimits | None:
|
|
153
165
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
154
166
|
cursor.execute(
|
|
155
167
|
query=self.SELECT_TENANT_LIMIT,
|
|
@@ -169,7 +181,7 @@ class Database:
|
|
|
169
181
|
)
|
|
170
182
|
def fetch_template(
|
|
171
183
|
self, template_id: str, tenant_uuid: str
|
|
172
|
-
) ->
|
|
184
|
+
) -> DBDocumentTemplate | None:
|
|
173
185
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
174
186
|
cursor.execute(
|
|
175
187
|
query=self.SELECT_TEMPLATE,
|
|
@@ -189,7 +201,7 @@ class Database:
|
|
|
189
201
|
)
|
|
190
202
|
def fetch_template_files(
|
|
191
203
|
self, template_id: str, tenant_uuid: str
|
|
192
|
-
) ->
|
|
204
|
+
) -> list[DBDocumentTemplateFile]:
|
|
193
205
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
194
206
|
cursor.execute(
|
|
195
207
|
query=self.SELECT_TEMPLATE_FILES,
|
|
@@ -206,7 +218,7 @@ class Database:
|
|
|
206
218
|
)
|
|
207
219
|
def fetch_template_assets(
|
|
208
220
|
self, template_id: str, tenant_uuid: str
|
|
209
|
-
) ->
|
|
221
|
+
) -> list[DBDocumentTemplateAsset]:
|
|
210
222
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
211
223
|
cursor.execute(
|
|
212
224
|
query=self.SELECT_TEMPLATE_ASSETS,
|
|
@@ -221,7 +233,8 @@ class Database:
|
|
|
221
233
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
222
234
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
223
235
|
)
|
|
224
|
-
def fetch_qtn_documents(self, questionnaire_uuid: str,
|
|
236
|
+
def fetch_qtn_documents(self, questionnaire_uuid: str,
|
|
237
|
+
tenant_uuid: str) -> list[DBDocument]:
|
|
225
238
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
226
239
|
cursor.execute(
|
|
227
240
|
query=self.SELECT_QTN_DOCUMENTS,
|
|
@@ -236,7 +249,8 @@ class Database:
|
|
|
236
249
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
237
250
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
238
251
|
)
|
|
239
|
-
def fetch_document_submissions(self, document_uuid: str,
|
|
252
|
+
def fetch_document_submissions(self, document_uuid: str,
|
|
253
|
+
tenant_uuid: str) -> list[DBSubmission]:
|
|
240
254
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
241
255
|
cursor.execute(
|
|
242
256
|
query=self.SELECT_DOCUMENT_SUBMISSIONS,
|
|
@@ -251,7 +265,8 @@ class Database:
|
|
|
251
265
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
252
266
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
253
267
|
)
|
|
254
|
-
def fetch_questionnaire_submissions(self, questionnaire_uuid: str,
|
|
268
|
+
def fetch_questionnaire_submissions(self, questionnaire_uuid: str,
|
|
269
|
+
tenant_uuid: str) -> list[DBSubmission]:
|
|
255
270
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
256
271
|
cursor.execute(
|
|
257
272
|
query=self.SELECT_QTN_SUBMISSIONS,
|
|
@@ -266,7 +281,8 @@ class Database:
|
|
|
266
281
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
267
282
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
268
283
|
)
|
|
269
|
-
def fetch_questionnaire_simple(self, questionnaire_uuid: str,
|
|
284
|
+
def fetch_questionnaire_simple(self, questionnaire_uuid: str,
|
|
285
|
+
tenant_uuid: str) -> DBQuestionnaireSimple:
|
|
270
286
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
271
287
|
cursor.execute(
|
|
272
288
|
query=self.SELECT_QTN_SIMPLE,
|
|
@@ -303,7 +319,7 @@ class Database:
|
|
|
303
319
|
query=self.UPDATE_DOCUMENT_RETRIEVED,
|
|
304
320
|
params=(
|
|
305
321
|
retrieved_at,
|
|
306
|
-
DocumentState.PROCESSING,
|
|
322
|
+
DocumentState.PROCESSING.value,
|
|
307
323
|
document_uuid,
|
|
308
324
|
),
|
|
309
325
|
)
|
|
@@ -317,7 +333,7 @@ class Database:
|
|
|
317
333
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
318
334
|
)
|
|
319
335
|
def update_document_finished(
|
|
320
|
-
self, finished_at: datetime.datetime, file_name: str, file_size: int,
|
|
336
|
+
self, *, finished_at: datetime.datetime, file_name: str, file_size: int,
|
|
321
337
|
content_type: str, worker_log: str, document_uuid: str
|
|
322
338
|
) -> bool:
|
|
323
339
|
with self.conn_query.new_cursor() as cursor:
|
|
@@ -325,7 +341,7 @@ class Database:
|
|
|
325
341
|
query=self.UPDATE_DOCUMENT_FINISHED,
|
|
326
342
|
params=(
|
|
327
343
|
finished_at,
|
|
328
|
-
DocumentState.FINISHED,
|
|
344
|
+
DocumentState.FINISHED.value,
|
|
329
345
|
file_name,
|
|
330
346
|
content_type,
|
|
331
347
|
worker_log,
|
|
@@ -358,7 +374,7 @@ class Database:
|
|
|
358
374
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
359
375
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
360
376
|
)
|
|
361
|
-
def get_tenant_config(self, tenant_uuid: str) ->
|
|
377
|
+
def get_tenant_config(self, tenant_uuid: str) -> DBTenantConfig | None:
|
|
362
378
|
if not self._check_table_exists(table_name='tenant_config'):
|
|
363
379
|
return None
|
|
364
380
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
@@ -370,8 +386,8 @@ class Database:
|
|
|
370
386
|
result = cursor.fetchone()
|
|
371
387
|
return DBTenantConfig.from_dict_row(data=result)
|
|
372
388
|
except Exception as e:
|
|
373
|
-
LOG.warning(
|
|
374
|
-
|
|
389
|
+
LOG.warning('Could not retrieve tenant_config for tenant "%s": %s',
|
|
390
|
+
tenant_uuid, str(e))
|
|
375
391
|
return None
|
|
376
392
|
|
|
377
393
|
@tenacity.retry(
|
|
@@ -381,7 +397,7 @@ class Database:
|
|
|
381
397
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
382
398
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
383
399
|
)
|
|
384
|
-
def get_mail_config(self, tenant_uuid: str) ->
|
|
400
|
+
def get_mail_config(self, tenant_uuid: str) -> DBInstanceConfigMail | None:
|
|
385
401
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
386
402
|
if not self._check_table_exists(table_name='instance_config_mail'):
|
|
387
403
|
return None
|
|
@@ -395,8 +411,8 @@ class Database:
|
|
|
395
411
|
return None
|
|
396
412
|
return DBInstanceConfigMail.from_dict_row(data=result)
|
|
397
413
|
except Exception as e:
|
|
398
|
-
LOG.warning(
|
|
399
|
-
|
|
414
|
+
LOG.warning('Could not retrieve instance_config_mail for tenant "%s": %s',
|
|
415
|
+
tenant_uuid, str(e))
|
|
400
416
|
return None
|
|
401
417
|
|
|
402
418
|
@tenacity.retry(
|
|
@@ -409,7 +425,7 @@ class Database:
|
|
|
409
425
|
def update_component_info(self, name: str, version: str, built_at: datetime.datetime):
|
|
410
426
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
411
427
|
if not self._check_table_exists(table_name='component'):
|
|
412
|
-
return
|
|
428
|
+
return
|
|
413
429
|
ts_now = datetime.datetime.now(tz=datetime.UTC)
|
|
414
430
|
try:
|
|
415
431
|
cursor.execute(
|
|
@@ -424,7 +440,7 @@ class Database:
|
|
|
424
440
|
)
|
|
425
441
|
self.conn_query.connection.commit()
|
|
426
442
|
except Exception as e:
|
|
427
|
-
LOG.warning(
|
|
443
|
+
LOG.warning('Could not update component info: %s', str(e))
|
|
428
444
|
|
|
429
445
|
@tenacity.retry(
|
|
430
446
|
reraise=True,
|
|
@@ -433,7 +449,7 @@ class Database:
|
|
|
433
449
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
434
450
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
435
451
|
)
|
|
436
|
-
def get_component_info(self, name: str) ->
|
|
452
|
+
def get_component_info(self, name: str) -> DBComponent | None:
|
|
437
453
|
if not self._check_table_exists(table_name='component'):
|
|
438
454
|
return None
|
|
439
455
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
@@ -447,7 +463,7 @@ class Database:
|
|
|
447
463
|
return None
|
|
448
464
|
return DBComponent.from_dict_row(data=result)
|
|
449
465
|
except Exception as e:
|
|
450
|
-
LOG.warning(
|
|
466
|
+
LOG.warning('Could not get component info: %s', str(e))
|
|
451
467
|
return None
|
|
452
468
|
|
|
453
469
|
@tenacity.retry(
|
|
@@ -457,7 +473,7 @@ class Database:
|
|
|
457
473
|
before=tenacity.before_log(LOG, logging.DEBUG),
|
|
458
474
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
459
475
|
)
|
|
460
|
-
def execute_queries(self, queries: Iterable[str]):
|
|
476
|
+
def execute_queries(self, queries: typing.Iterable[str]):
|
|
461
477
|
with self.conn_query.new_cursor(use_dict=True) as cursor:
|
|
462
478
|
for query in queries:
|
|
463
479
|
cursor.execute(query=query)
|
|
@@ -484,7 +500,7 @@ class PostgresConnection:
|
|
|
484
500
|
connect_timeout=timeout,
|
|
485
501
|
)
|
|
486
502
|
self.autocommit = autocommit
|
|
487
|
-
self._connection
|
|
503
|
+
self._connection: psycopg.Connection | None = None
|
|
488
504
|
|
|
489
505
|
@tenacity.retry(
|
|
490
506
|
reraise=True,
|
|
@@ -494,11 +510,15 @@ class PostgresConnection:
|
|
|
494
510
|
after=tenacity.after_log(LOG, logging.DEBUG),
|
|
495
511
|
)
|
|
496
512
|
def _connect_db(self):
|
|
497
|
-
LOG.info(
|
|
513
|
+
LOG.info('Creating connection to PostgreSQL database "%s"', self.name)
|
|
498
514
|
try:
|
|
499
|
-
connection
|
|
515
|
+
connection: psycopg.Connection = psycopg.connect(
|
|
516
|
+
conninfo=self.dsn,
|
|
517
|
+
autocommit=self.autocommit,
|
|
518
|
+
)
|
|
500
519
|
except Exception as e:
|
|
501
|
-
LOG.error(
|
|
520
|
+
LOG.error('Failed to connect to PostgreSQL database "%s": %s',
|
|
521
|
+
self.name, str(e))
|
|
502
522
|
raise e
|
|
503
523
|
# test connection
|
|
504
524
|
cursor = connection.cursor()
|
|
@@ -506,7 +526,7 @@ class PostgresConnection:
|
|
|
506
526
|
result = cursor.fetchone()
|
|
507
527
|
if result is None:
|
|
508
528
|
raise RuntimeError('Failed to verify DB connection')
|
|
509
|
-
LOG.debug(
|
|
529
|
+
LOG.debug('DB connection verified (result=%s)', result[0])
|
|
510
530
|
cursor.close()
|
|
511
531
|
connection.commit()
|
|
512
532
|
self._connection = connection
|
|
@@ -532,6 +552,6 @@ class PostgresConnection:
|
|
|
532
552
|
|
|
533
553
|
def close(self):
|
|
534
554
|
if self._connection:
|
|
535
|
-
LOG.info(
|
|
555
|
+
LOG.info('Closing connection to PostgreSQL database "%s"', self.name)
|
|
536
556
|
self._connection.close()
|
|
537
557
|
self._connection = None
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import datetime
|
|
3
|
+
import enum
|
|
3
4
|
import json
|
|
4
5
|
|
|
5
|
-
from typing import Optional
|
|
6
|
-
|
|
7
6
|
|
|
8
7
|
NULL_UUID = '00000000-0000-0000-0000-000000000000'
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
class DocumentState:
|
|
10
|
+
class DocumentState(enum.Enum):
|
|
12
11
|
QUEUED = 'QueuedDocumentState'
|
|
13
12
|
PROCESSING = 'InProgressDocumentState'
|
|
14
13
|
FAILED = 'ErrorDocumentState'
|
|
15
14
|
FINISHED = 'DoneDocumentState'
|
|
16
15
|
|
|
17
16
|
|
|
18
|
-
class DocumentTemplatePhase:
|
|
17
|
+
class DocumentTemplatePhase(enum.Enum):
|
|
19
18
|
RELEASED = 'ReleasedTemplatePhase'
|
|
20
19
|
DEPRECATED = 'DeprecatedTemplatePhase'
|
|
21
20
|
DRAFT = 'DraftTemplatePhase'
|
|
@@ -55,8 +54,8 @@ class DBDocument:
|
|
|
55
54
|
content_type: str
|
|
56
55
|
worker_log: str
|
|
57
56
|
created_by: str
|
|
58
|
-
retrieved_at:
|
|
59
|
-
finished_at:
|
|
57
|
+
retrieved_at: datetime.datetime | None
|
|
58
|
+
finished_at: datetime.datetime | None
|
|
60
59
|
created_at: datetime.datetime
|
|
61
60
|
tenant_uuid: str
|
|
62
61
|
file_size: int
|
|
@@ -191,11 +190,11 @@ class PersistentCommand:
|
|
|
191
190
|
component: str
|
|
192
191
|
function: str
|
|
193
192
|
body: dict
|
|
194
|
-
last_error_message:
|
|
193
|
+
last_error_message: str | None
|
|
195
194
|
attempts: int
|
|
196
195
|
max_attempts: int
|
|
197
196
|
tenant_uuid: str
|
|
198
|
-
created_by:
|
|
197
|
+
created_by: str | None
|
|
199
198
|
created_at: datetime.datetime
|
|
200
199
|
updated_at: datetime.datetime
|
|
201
200
|
|
|
@@ -220,28 +219,28 @@ class PersistentCommand:
|
|
|
220
219
|
@dataclasses.dataclass
|
|
221
220
|
class DBTenantConfig:
|
|
222
221
|
uuid: str
|
|
223
|
-
organization:
|
|
224
|
-
authentication:
|
|
225
|
-
privacy_and_support:
|
|
226
|
-
dashboard:
|
|
227
|
-
look_and_feel:
|
|
228
|
-
registry:
|
|
229
|
-
knowledge_model:
|
|
230
|
-
questionnaire:
|
|
231
|
-
submission:
|
|
232
|
-
owl:
|
|
233
|
-
mail_config_uuid:
|
|
222
|
+
organization: dict | None
|
|
223
|
+
authentication: dict | None
|
|
224
|
+
privacy_and_support: dict | None
|
|
225
|
+
dashboard: dict | None
|
|
226
|
+
look_and_feel: dict | None
|
|
227
|
+
registry: dict | None
|
|
228
|
+
knowledge_model: dict | None
|
|
229
|
+
questionnaire: dict | None
|
|
230
|
+
submission: dict | None
|
|
231
|
+
owl: dict | None
|
|
232
|
+
mail_config_uuid: str | None
|
|
234
233
|
created_at: datetime.datetime
|
|
235
234
|
updated_at: datetime.datetime
|
|
236
235
|
|
|
237
236
|
@property
|
|
238
|
-
def app_title(self) ->
|
|
237
|
+
def app_title(self) -> str | None:
|
|
239
238
|
if self.look_and_feel is None:
|
|
240
239
|
return None
|
|
241
240
|
return self.look_and_feel.get('appTitle', None)
|
|
242
241
|
|
|
243
242
|
@property
|
|
244
|
-
def support_email(self) ->
|
|
243
|
+
def support_email(self) -> str | None:
|
|
245
244
|
if self.privacy_and_support is None:
|
|
246
245
|
return None
|
|
247
246
|
return self.privacy_and_support.get('supportEmail', None)
|
|
@@ -269,7 +268,7 @@ class DBTenantConfig:
|
|
|
269
268
|
@dataclasses.dataclass
|
|
270
269
|
class DBTenantLimits:
|
|
271
270
|
tenant_uuid: str
|
|
272
|
-
storage:
|
|
271
|
+
storage: int | None
|
|
273
272
|
|
|
274
273
|
@staticmethod
|
|
275
274
|
def from_dict_row(data: dict):
|
|
@@ -390,19 +389,19 @@ class DBInstanceConfigMail:
|
|
|
390
389
|
uuid: str
|
|
391
390
|
enabled: bool
|
|
392
391
|
provider: str
|
|
393
|
-
sender_name:
|
|
394
|
-
sender_email:
|
|
395
|
-
smtp_host:
|
|
396
|
-
smtp_port:
|
|
397
|
-
smtp_security:
|
|
398
|
-
smtp_username:
|
|
399
|
-
smtp_password:
|
|
400
|
-
aws_access_key_id:
|
|
401
|
-
aws_secret_access_key:
|
|
402
|
-
aws_region:
|
|
403
|
-
rate_limit_window:
|
|
404
|
-
rate_limit_count:
|
|
405
|
-
timeout:
|
|
392
|
+
sender_name: str | None
|
|
393
|
+
sender_email: str | None
|
|
394
|
+
smtp_host: str | None
|
|
395
|
+
smtp_port: int | None
|
|
396
|
+
smtp_security: str | None
|
|
397
|
+
smtp_username: str | None
|
|
398
|
+
smtp_password: str | None
|
|
399
|
+
aws_access_key_id: str | None
|
|
400
|
+
aws_secret_access_key: str | None
|
|
401
|
+
aws_region: str | None
|
|
402
|
+
rate_limit_window: int | None
|
|
403
|
+
rate_limit_count: int | None
|
|
404
|
+
timeout: int | None
|
|
406
405
|
|
|
407
406
|
@staticmethod
|
|
408
407
|
def from_dict_row(data: dict):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dsw-database
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.14.0
|
|
4
4
|
Summary: Library for managing DSW database
|
|
5
5
|
Author-email: Marek Suchánek <marek.suchanek@ds-wizard.org>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -11,16 +11,16 @@ Keywords: dsw,database
|
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
13
|
Classifier: Programming Language :: Python
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Topic :: Database
|
|
17
17
|
Classifier: Topic :: Utilities
|
|
18
|
-
Requires-Python: <4,>=3.
|
|
18
|
+
Requires-Python: <4,>=3.11
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE
|
|
21
21
|
Requires-Dist: psycopg[binary]
|
|
22
22
|
Requires-Dist: tenacity
|
|
23
|
-
Requires-Dist: dsw-config==4.
|
|
23
|
+
Requires-Dist: dsw-config==4.14.0
|
|
24
24
|
|
|
25
25
|
# Data Stewardship Wizard: Database
|
|
26
26
|
|
|
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = 'dsw-database'
|
|
7
|
-
version = "4.
|
|
7
|
+
version = "4.14.0"
|
|
8
8
|
description = 'Library for managing DSW database'
|
|
9
9
|
readme = 'README.md'
|
|
10
10
|
keywords = ['dsw', 'database']
|
|
@@ -16,17 +16,17 @@ classifiers = [
|
|
|
16
16
|
'Development Status :: 5 - Production/Stable',
|
|
17
17
|
'License :: OSI Approved :: Apache Software License',
|
|
18
18
|
'Programming Language :: Python',
|
|
19
|
-
'Programming Language :: Python :: 3.10',
|
|
20
19
|
'Programming Language :: Python :: 3.11',
|
|
20
|
+
'Programming Language :: Python :: 3.12',
|
|
21
21
|
'Topic :: Database',
|
|
22
22
|
'Topic :: Utilities',
|
|
23
23
|
]
|
|
24
|
-
requires-python = '>=3.
|
|
24
|
+
requires-python = '>=3.11, <4'
|
|
25
25
|
dependencies = [
|
|
26
26
|
'psycopg[binary]',
|
|
27
27
|
'tenacity',
|
|
28
28
|
# DSW
|
|
29
|
-
"dsw-config==4.
|
|
29
|
+
"dsw-config==4.14.0",
|
|
30
30
|
]
|
|
31
31
|
|
|
32
32
|
[project.urls]
|
|
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
|