kongalib 2.0.5__cp314-cp314-macosx_10_15_universal2.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.
- _kongalib.cpython-314-darwin.so +0 -0
- kongalib/__init__.py +394 -0
- kongalib/async_client.py +813 -0
- kongalib/client.py +1045 -0
- kongalib/constants.json +1 -0
- kongalib/constants.py +187 -0
- kongalib/data_dictionary.py +203 -0
- kongalib/db.py +267 -0
- kongalib/expression.py +841 -0
- kongalib/json.py +114 -0
- kongalib/lex.py +1058 -0
- kongalib/scripting.py +766 -0
- kongalib/yacc.py +3276 -0
- kongalib-2.0.5.dist-info/METADATA +150 -0
- kongalib-2.0.5.dist-info/RECORD +21 -0
- kongalib-2.0.5.dist-info/WHEEL +6 -0
- kongalib-2.0.5.dist-info/licenses/LICENSE +165 -0
- kongalib-2.0.5.dist-info/top_level.txt +4 -0
- kongalib-2.0.5.dist-info/zip-safe +1 -0
- kongaui.py +507 -0
- kongautil.py +581 -0
kongalib/async_client.py
ADDED
|
@@ -0,0 +1,813 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# _ _ _ _
|
|
3
|
+
# | | | (_) |
|
|
4
|
+
# | | _____ _ __ __ _ __ _| |_| |__
|
|
5
|
+
# | |/ / _ \| '_ \ / _` |/ _` | | | '_ \
|
|
6
|
+
# | < (_) | | | | (_| | (_| | | | |_) |
|
|
7
|
+
# |_|\_\___/|_| |_|\__, |\__,_|_|_|_.__/
|
|
8
|
+
# __/ |
|
|
9
|
+
# |___/
|
|
10
|
+
#
|
|
11
|
+
# Konga client library, by EasyByte Software
|
|
12
|
+
#
|
|
13
|
+
# https://github.com/easybyte-software/kongalib
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from __future__ import absolute_import
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import nest_asyncio
|
|
20
|
+
import inspect
|
|
21
|
+
|
|
22
|
+
from kongalib import *
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
nest_asyncio.apply()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AsyncClient(Client):
|
|
29
|
+
"""La classe AsyncClient, analogamente alla classe :class:`~kongalib.Client`, permette di connettersi ad un server Konga e di eseguire
|
|
30
|
+
comandi sullo stesso; la differenza è nel fatto che questa classe è costruita esplicitamente per lavorare con *asyncio* di Python, ed
|
|
31
|
+
è disponibile solo se si usa Python >= 3.6.
|
|
32
|
+
|
|
33
|
+
Tutti i metodi che esistono nella classe Client e che possono essere invocati sia in maniera asincrona (specificando le callback di
|
|
34
|
+
*success*, *error* e *progress*) che sincrona (generalmente omettendo di specificare la callback di *success*), nella classe AsyncClient
|
|
35
|
+
accettano eventualmente la sola callback di *progress*, in quanto vengono sempre eseguiti in maniera asincrona tramite l'event loop di
|
|
36
|
+
asyncio, che si assume sia in esecuzione. La *progress* viene eseguita in un thread separato, ed ha la forma ``progress(type, completeness, state, userdata)``;
|
|
37
|
+
i parametri interessanti di questa callback sono *completeness* (percentuale di completamento, ossia un numero che varia da 0.0 a 100.0;
|
|
38
|
+
se -1.0 indica una percentuale di completamento indefinita), *state* (stringa che specifica l'eventuale stato corrente dell'operazione)
|
|
39
|
+
e *userdata*, che è un parametro aggiuntivo che viene normalmente passato alla chiamata asincrona dall'utente per tenere traccia di un
|
|
40
|
+
eventuale stato.
|
|
41
|
+
|
|
42
|
+
Come per la classe Client, oggetti di classe AsyncClient possono essere usati come contesti per il costrutto ``with``: all'ingresso del
|
|
43
|
+
blocco verrà iniziata una transazione, mentre in uscita verrà eseguita una commit o una rollback della stessa a seconda che ci sia stata
|
|
44
|
+
o meno un'eccezione all'interno del blocco di istruzioni. Da notare che dal momento che siamo in ambito asincrono, andrà usato il
|
|
45
|
+
costrutto ``async with`` al posto del semplice ``with``.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def _safe_set_result(self, future, result):
|
|
49
|
+
if not future.done():
|
|
50
|
+
future.set_result(result)
|
|
51
|
+
|
|
52
|
+
def _safe_set_exception(self, future, e):
|
|
53
|
+
if not future.done():
|
|
54
|
+
future.set_exception(e)
|
|
55
|
+
|
|
56
|
+
def _make_progress(self, future, progress, userdata):
|
|
57
|
+
def callback(ptype, completeness, state, dummy):
|
|
58
|
+
loop = future.get_loop()
|
|
59
|
+
try:
|
|
60
|
+
if future.cancelled() or (progress is None):
|
|
61
|
+
result = not future.cancelled()
|
|
62
|
+
else:
|
|
63
|
+
if asyncio.iscoroutinefunction(progress):
|
|
64
|
+
coro = progress
|
|
65
|
+
else:
|
|
66
|
+
async def coro(*args):
|
|
67
|
+
return progress(*args)
|
|
68
|
+
result = asyncio.run_coroutine_threadsafe(coro(ptype, completeness, state, userdata), loop).result()
|
|
69
|
+
if result is False:
|
|
70
|
+
return False
|
|
71
|
+
except Exception as e:
|
|
72
|
+
result = False
|
|
73
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, e)
|
|
74
|
+
return result
|
|
75
|
+
return callback
|
|
76
|
+
|
|
77
|
+
def _make_error(self, future):
|
|
78
|
+
def error(errno, *args):
|
|
79
|
+
loop = future.get_loop()
|
|
80
|
+
if isinstance(errno, int) and (errno in (ABORTED, EXECUTE_ABORTED)):
|
|
81
|
+
errno = Error(errno, '')
|
|
82
|
+
if isinstance(errno, Error):
|
|
83
|
+
if errno.errno in (ABORTED, EXECUTE_ABORTED):
|
|
84
|
+
loop.call_soon_threadsafe(future.cancel)
|
|
85
|
+
else:
|
|
86
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, errno)
|
|
87
|
+
elif isinstance(errno, Exception):
|
|
88
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, errno)
|
|
89
|
+
else:
|
|
90
|
+
if len(args) > 0:
|
|
91
|
+
errstr = ensure_text(args[0])
|
|
92
|
+
else:
|
|
93
|
+
errstr = '<unknown>'
|
|
94
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, Error(errno, errstr))
|
|
95
|
+
return error
|
|
96
|
+
|
|
97
|
+
def _make_success_tuple(self, future, count):
|
|
98
|
+
def success(*args):
|
|
99
|
+
loop = future.get_loop()
|
|
100
|
+
if count == 0:
|
|
101
|
+
loop.call_soon_threadsafe(self._safe_set_result, future, None)
|
|
102
|
+
elif count == 1:
|
|
103
|
+
loop.call_soon_threadsafe(self._safe_set_result, future, args[0])
|
|
104
|
+
else:
|
|
105
|
+
loop.call_soon_threadsafe(self._safe_set_result, future, args[:count])
|
|
106
|
+
return success
|
|
107
|
+
|
|
108
|
+
def _make_success(self, future, log=None, finalize_output=None):
|
|
109
|
+
def success(output, *args):
|
|
110
|
+
loop = future.get_loop()
|
|
111
|
+
answer = output[OUT_LOG] or []
|
|
112
|
+
error_list = ErrorList(answer)
|
|
113
|
+
if output[OUT_ERRNO] == OK:
|
|
114
|
+
if error_list.errno != OK:
|
|
115
|
+
if log is None:
|
|
116
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, error_list)
|
|
117
|
+
else:
|
|
118
|
+
error_list.prepare_log(log)
|
|
119
|
+
if log.has_errors():
|
|
120
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, error_list)
|
|
121
|
+
else:
|
|
122
|
+
try:
|
|
123
|
+
if finalize_output is not None:
|
|
124
|
+
output = finalize_output(output)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, e)
|
|
127
|
+
else:
|
|
128
|
+
loop.call_soon_threadsafe(self._safe_set_result, future, output)
|
|
129
|
+
else:
|
|
130
|
+
try:
|
|
131
|
+
if finalize_output is not None:
|
|
132
|
+
output = finalize_output(output)
|
|
133
|
+
except Exception as e:
|
|
134
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, e)
|
|
135
|
+
else:
|
|
136
|
+
loop.call_soon_threadsafe(self._safe_set_result, future, output)
|
|
137
|
+
else:
|
|
138
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, ErrorList.from_error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
139
|
+
return success
|
|
140
|
+
|
|
141
|
+
def _execute(self, cmd, in_params, out_params=None, progress=None, log=None):
|
|
142
|
+
def finalize(output):
|
|
143
|
+
if out_params:
|
|
144
|
+
if callable(out_params):
|
|
145
|
+
return out_params(output)
|
|
146
|
+
elif isinstance(out_params, (tuple, list)):
|
|
147
|
+
return tuple([ output[param] for param in out_params ])
|
|
148
|
+
else:
|
|
149
|
+
return output[out_params]
|
|
150
|
+
else:
|
|
151
|
+
return None
|
|
152
|
+
fut = asyncio.get_running_loop().create_future()
|
|
153
|
+
self._impl.execute(cmd, in_params or {}, DEFAULT_EXECUTE_TIMEOUT, self._make_success(fut, log, finalize), self._make_error(fut), self._make_progress(fut, progress, None))
|
|
154
|
+
return fut
|
|
155
|
+
|
|
156
|
+
async def __aenter__(self):
|
|
157
|
+
await self.begin_transaction()
|
|
158
|
+
return self
|
|
159
|
+
|
|
160
|
+
async def __aexit__(self, exc_type, exc_value, exc_traceback):
|
|
161
|
+
if exc_type is None:
|
|
162
|
+
await self.commit_transaction()
|
|
163
|
+
else:
|
|
164
|
+
await self.rollback_transaction()
|
|
165
|
+
|
|
166
|
+
def as_sync(self):
|
|
167
|
+
"""Ritorna un oggetto :class:`~kongalib.Client` equivalente a questo client, preservando le connessioni già presenti.
|
|
168
|
+
"""
|
|
169
|
+
return Client(self._impl)
|
|
170
|
+
|
|
171
|
+
def list_servers(self, timeout=DEFAULT_DISCOVER_TIMEOUT, port=0, progress=None, userdata=None):
|
|
172
|
+
"""Esegue una scansione della rete locale alla ricerca dei server Konga disponibili, attendendo al massimo *timeout* millisecondi
|
|
173
|
+
per una risposta. *port* specifica la porta da cui far partire la scansione (default = 51967); sono controllate le successive 10
|
|
174
|
+
porte UDP con intervallo di 20 porte (quindi di default vengono scansionate le porte 51967, 51987, 52007, ... 52147). La funzione
|
|
175
|
+
restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà una lista di ``dict``, le cui chiavi principali
|
|
176
|
+
sono *host*, *port*, *name* e *description*.
|
|
177
|
+
"""
|
|
178
|
+
fut = asyncio.get_running_loop().create_future()
|
|
179
|
+
self._impl.list_servers(timeout, port, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata))
|
|
180
|
+
return fut
|
|
181
|
+
|
|
182
|
+
def connect(self, server=None, host=None, port=0, options=None, timeout=DEFAULT_CONNECT_TIMEOUT, progress=None, userdata=None):
|
|
183
|
+
"""Tenta una connessione ad un server Konga. Il server a cui ci si vuole connettere può essere specificato in due modi: tramite i
|
|
184
|
+
parametri *host* e *port*, oppure tramite un ``dict`` *server* che deve contenere almeno le chiavi *host* e *port*. Alternativamente,
|
|
185
|
+
se *server* è una stringa e *host* non è specificato, viene assunta come *host*. Se *host* include una specifica di porta e *port* è ``0``,
|
|
186
|
+
*port* viene ottenuta dalla specifica contenuta nella stringa di *host*. Il parametro *options* può essere un ``dict`` contenente opzioni
|
|
187
|
+
aggiuntive per la connessione; al momento le opzioni supportate sono:
|
|
188
|
+
|
|
189
|
+
- ``tenant_key`` (*str*): chiave del tenant per stabilire la connessione con un server multitenant.
|
|
190
|
+
|
|
191
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà un ``dict`` contenente le informazioni
|
|
192
|
+
sulla connessione stabilita.
|
|
193
|
+
"""
|
|
194
|
+
if (server is None) and (host is None):
|
|
195
|
+
raise ValueError("either 'host' or 'server' parameter must be specified")
|
|
196
|
+
if isinstance(server, str) and (host is None):
|
|
197
|
+
host = server
|
|
198
|
+
server = None
|
|
199
|
+
if isinstance(host, str) and (port is None) and (':' in host):
|
|
200
|
+
pos = host.rfind(':')
|
|
201
|
+
host = host[:pos]
|
|
202
|
+
try:
|
|
203
|
+
port = int(host[pos+1:])
|
|
204
|
+
except:
|
|
205
|
+
raise ValueError("Invalid port value embedded in host string")
|
|
206
|
+
fut = asyncio.get_running_loop().create_future()
|
|
207
|
+
self._impl.connect(server, host or '', port, options, timeout, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata))
|
|
208
|
+
return fut
|
|
209
|
+
|
|
210
|
+
def disconnect(self):
|
|
211
|
+
"""Disconnette il server attualmente connesso, oppure non fa nulla se non si è al momento connessi."""
|
|
212
|
+
self._impl.disconnect()
|
|
213
|
+
|
|
214
|
+
def get_id(self):
|
|
215
|
+
"""Restituisce un ID numerico univoco assegnato dal server alla connessione con questo client, o 0 se non si è connessi."""
|
|
216
|
+
return self._impl.get_id()
|
|
217
|
+
|
|
218
|
+
def get_connection_info(self):
|
|
219
|
+
"""Restituisce un ``dict`` con informazioni sulla connessione corrente, o ``None`` se non si è connessi."""
|
|
220
|
+
return self._impl.get_connection_info()
|
|
221
|
+
|
|
222
|
+
def execute(self, command, data=None, timeout=DEFAULT_EXECUTE_TIMEOUT, progress=None, userdata=None, log=None):
|
|
223
|
+
fut = asyncio.get_running_loop().create_future()
|
|
224
|
+
self._impl.execute(command, data or {}, timeout, self._make_success(fut, log), self._make_error(fut), self._make_progress(fut, progress, userdata))
|
|
225
|
+
return fut
|
|
226
|
+
|
|
227
|
+
def interrupt(self):
|
|
228
|
+
"""Interrompe tutte le operazioni al momento in esecuzione da parte di questo client."""
|
|
229
|
+
self._impl.interrupt()
|
|
230
|
+
|
|
231
|
+
def get_data_dictionary(self, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
232
|
+
"""Restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà il dizionario dei dati disponibile sul server
|
|
233
|
+
attualmente connesso, sotto forma di oggetto di classe :class:`kongalib.DataDictionary`.
|
|
234
|
+
"""
|
|
235
|
+
fut = asyncio.get_running_loop().create_future()
|
|
236
|
+
uuid = self.get_connection_info().get('uuid', None)
|
|
237
|
+
with Client.DATA_DICTIONARY_LOCK:
|
|
238
|
+
if uuid is None:
|
|
239
|
+
data = None
|
|
240
|
+
else:
|
|
241
|
+
data = Client.DATA_DICTIONARY_CACHE.get(uuid, None)
|
|
242
|
+
if data is None:
|
|
243
|
+
def success(d, userdata):
|
|
244
|
+
with Client.DATA_DICTIONARY_LOCK:
|
|
245
|
+
d = DataDictionary(d)
|
|
246
|
+
Client.DATA_DICTIONARY_CACHE[uuid] = d
|
|
247
|
+
fut.get_loop().call_soon_threadsafe(self._safe_set_result, fut, d)
|
|
248
|
+
self._impl.get_data_dictionary(success, self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
249
|
+
else:
|
|
250
|
+
fut.set_result(data)
|
|
251
|
+
return fut
|
|
252
|
+
|
|
253
|
+
def list_drivers(self, configured=True, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
254
|
+
"""Restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà la lista dei driver di database presenti
|
|
255
|
+
sul server attualmente connesso, oppure lancia un'eccezione :class:`~kongalib.Error` su errore. Ogni elemento della lista restituita
|
|
256
|
+
è un ``dict`` che comprende la chiavi *name*, *version* e *description*. Se *configured* è False, tutti i driver installati sul
|
|
257
|
+
server sono restituiti, altrimenti verranno restituite solo le informazioni sui driver configurati correttamente ed in esecuzione
|
|
258
|
+
sul server.
|
|
259
|
+
"""
|
|
260
|
+
fut = asyncio.get_running_loop().create_future()
|
|
261
|
+
self._impl.list_drivers(configured, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
262
|
+
return fut
|
|
263
|
+
|
|
264
|
+
def list_databases(self, driver=None, quick=False, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
265
|
+
"""Restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà la lista dei database disponibili sul server
|
|
266
|
+
corrente, appartenenti a tutti o ad uno specifico *driver*. La lista viene tornata sotto forma di ``dict``, le cui chiavi sono i nomi
|
|
267
|
+
dei driver e i valori le liste dei database appartenenti allo specifico driver. Ogni database nelle liste è un ``dict`` che contiene
|
|
268
|
+
almeno le chiavi *name*, *desc*, *uuid*, *created_ts* e *modified_ts*. L'eccezione :class:`~kongalib.Error` viene lanciata se si
|
|
269
|
+
verifica un errore. Se *quick* è ``True``, la funzione ritorna il più velocemente possibile ma la scansione dei database disponibili
|
|
270
|
+
potrebbe risultare ancora incompleta.
|
|
271
|
+
"""
|
|
272
|
+
fut = asyncio.get_running_loop().create_future()
|
|
273
|
+
self._impl.list_databases(driver, quick, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
274
|
+
return fut
|
|
275
|
+
|
|
276
|
+
def create_database(self, password, driver, name, desc='', progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
277
|
+
"""Crea un nuovo database sul server attualmente connesso; il database avrà nome *name* e descrizione *desc*.
|
|
278
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà l'UUID del nuovo database;
|
|
279
|
+
se si verifica un errore viene lanciata l'eccezione :class:`~kongalib.Error`.
|
|
280
|
+
|
|
281
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
282
|
+
"""
|
|
283
|
+
fut = asyncio.get_running_loop().create_future()
|
|
284
|
+
self._impl.create_database(password, driver, name, desc, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
285
|
+
return fut
|
|
286
|
+
|
|
287
|
+
def open_database(self, driver, name, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
288
|
+
"""Apre un database rendendolo il database attivo per la connessione corrente. La funzione restituisce un oggetto ``asyncio.Future`` il
|
|
289
|
+
cui risultato una volta completato sarà un ``dict`` con le informazioni sul database connesso, oppure viene lanciata l'eccezione
|
|
290
|
+
:class:`~kongalib.Error` in caso di errore.
|
|
291
|
+
"""
|
|
292
|
+
fut = asyncio.get_running_loop().create_future()
|
|
293
|
+
self._impl.open_database(driver, name, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
294
|
+
return fut
|
|
295
|
+
|
|
296
|
+
def close_database(self, backup=False, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
297
|
+
"""Chiude il database attivo sulla connessione corrente, restituendo un oggetto ``asyncio.Future`` per l'esecuzione asincrona; in caso
|
|
298
|
+
di errore verrà lanciata l'eccezione :class:`~kongalib.Error`.
|
|
299
|
+
|
|
300
|
+
.. note:: Se *backup* è ``True``, il server esegue un backup automatico del database prima di chiuderlo.
|
|
301
|
+
"""
|
|
302
|
+
fut = asyncio.get_running_loop().create_future()
|
|
303
|
+
self._impl.close_database(backup, self._make_success_tuple(fut, 0), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
304
|
+
return fut
|
|
305
|
+
|
|
306
|
+
def upgrade_database(self, password, driver, name, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
307
|
+
"""Aggiorna il database specificato all'ultima versione disponibile.
|
|
308
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà una tupla (log, old_version, new_version),
|
|
309
|
+
dove il log dell'operazione è sotto forma di una lista di stringhe, oppure viene lanciata l'eccezione :class:`~kongalib.Error` in caso
|
|
310
|
+
di errore.
|
|
311
|
+
|
|
312
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
313
|
+
"""
|
|
314
|
+
fut = asyncio.get_running_loop().create_future()
|
|
315
|
+
self._impl.upgrade_database(password, driver, name, self._make_success_tuple(fut, 3), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
316
|
+
return fut
|
|
317
|
+
|
|
318
|
+
def delete_database(self, password, driver, name, delete_cloud_data=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
319
|
+
"""Cancella il database specificato. Se *delete_cloud_data* è ``None`` (valore predefinito) la cancellazione sarà negata nel caso ci siano
|
|
320
|
+
dati binari legati al database al momento presenti nel cloud; in caso contrario i dati binari saranno o meno cancellati dal cloud in base
|
|
321
|
+
al valore del parametro.
|
|
322
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
323
|
+
caso di errore.
|
|
324
|
+
|
|
325
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
326
|
+
"""
|
|
327
|
+
fut = asyncio.get_running_loop().create_future()
|
|
328
|
+
self._impl.delete_database(password, driver, name, delete_cloud_data, self._make_success_tuple(fut, 0), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
329
|
+
return fut
|
|
330
|
+
|
|
331
|
+
def query(self, query, native=False, full_column_names=False, collapse_blobs=False, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
332
|
+
"""Esegue una query SQL sul database attivo nella connessione corrente. Se *native* è ``True``, la query viene passata al driver
|
|
333
|
+
del database senza essere interpretata, permettendo l'esecuzione di query native per l'RDBMS sottostante. La funzione restituisce un oggetto
|
|
334
|
+
``asyncio.Future`` il cui risultato una volta completato sarà una tupla nella forma ``(affected_rows, column_names, result_set)``;
|
|
335
|
+
*affected_rows* è il numero di righe coinvolte nella query di UPDATE/DELETE, *column_names* è una lista di nomi di colonne per il result set,
|
|
336
|
+
mentre *result_set* è una lista di righe risultati della query, dove ogni riga è una lista di valori corrispondenti alle colonne restituite in
|
|
337
|
+
*column_names*. In caso di errore viene lanciata l'eccezione :class:`~kongalib.Error`.
|
|
338
|
+
|
|
339
|
+
.. note:: Se *full_column_names* è ``False``, *column_names* includerà i nomi delle colonne senza nome tabella, altrimenti saranno
|
|
340
|
+
inclusi i nomi completi delle colonne. Se *collapse_blobs* è ``True``, i dati di tipo BLOB binari verranno restituiti come ``[...]``.
|
|
341
|
+
"""
|
|
342
|
+
fut = asyncio.get_running_loop().create_future()
|
|
343
|
+
self._impl.query_database(query, native, full_column_names, collapse_blobs, self._make_success_tuple(fut, 3), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
344
|
+
return fut
|
|
345
|
+
|
|
346
|
+
def backup_database(self, password, backup_name, driver, name, auto=True, overwrite=False, position=0, store_index=False, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
347
|
+
"""Esegue un backup del database specificato sul server attualmente connesso. Se *auto* è ``False``, è necessario specificare un nome
|
|
348
|
+
per il backup tramite *backup_name*, altrimenti il backup viene considerato automatico ed un nome univoco è assegnato dal server. Se
|
|
349
|
+
*overwrite* è ``False`` ed un backup con lo stesso nome esiste già sul server, non sarà possibile eseguire il backup. *position*
|
|
350
|
+
permette di specificare dove eseguire il backup, ed è una combinazione delle costanti :const:`kongalib.BACKUP_ON_COMPUTER` e
|
|
351
|
+
:const:`kongalib.BACKUP_ON_CLOUD`, mentre *store_index* specifica se includere l'indice di ricerca full-text nel backup.
|
|
352
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
353
|
+
caso di errore.
|
|
354
|
+
|
|
355
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
356
|
+
"""
|
|
357
|
+
fut = asyncio.get_running_loop().create_future()
|
|
358
|
+
self._impl.backup_database(password, backup_name, driver, name, auto, overwrite, position, store_index, self._make_success_tuple(fut, 0), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
359
|
+
return fut
|
|
360
|
+
|
|
361
|
+
def restore_database(self, password, backup_name, driver, name, change_uuid=True, overwrite=False, position=0, restore_index=True, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
362
|
+
"""Ripristina un database a partire da un backup effettuato in precedenza sul server connesso. Se *overwrite* è False ed esiste un
|
|
363
|
+
database gestito da *driver* con lo stesso nome, la funzione riporterà errore. *change_uuid* specifica se cambiare l'UUID associato al
|
|
364
|
+
database oppure se ripristinare quello originale; se si hanno database con lo stesso nome gestiti da driver diversi è opportuno che
|
|
365
|
+
almeno l'UUID per essi sia diverso, altrimenti si può incorrere in problemi di aliasing. *position* specifica da dove prendere il
|
|
366
|
+
backup da rispristinare, e deve essere una delle costanti :const:`kongalib.BACKUP_ON_COMPUTER` o :const:`kongalib.BACKUP_ON_CLOUD`;
|
|
367
|
+
*restore_index* invece permette di specificare se ripristinare o meno l'indice di ricerca qualora fosse contenuto all'interno del backup.
|
|
368
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
369
|
+
caso di errore.
|
|
370
|
+
|
|
371
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
372
|
+
"""
|
|
373
|
+
fut = asyncio.get_running_loop().create_future()
|
|
374
|
+
self._impl.restore_database(password, backup_name, driver, name, change_uuid, overwrite, position, restore_index, self._make_success_tuple(fut, 0), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
375
|
+
return fut
|
|
376
|
+
|
|
377
|
+
def list_backups(self, position=0, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
378
|
+
"""Ottiene la lista dei backup disponibili sul server connesso.
|
|
379
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà una lista di backup; ogni backup è un
|
|
380
|
+
``dict`` che contiene almeno le chiavi *backup_name*, *uuid*, *date* e *size*; se si verifica un errore viene lanciata l'eccezione
|
|
381
|
+
:class:`~kongalib.Error`.
|
|
382
|
+
"""
|
|
383
|
+
fut = asyncio.get_running_loop().create_future()
|
|
384
|
+
self._impl.list_backups(position, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
385
|
+
return fut
|
|
386
|
+
|
|
387
|
+
def delete_backup(self, password, backup_name, position, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
388
|
+
"""Cancella il backup identificato da *backup_name* dal server connesso.
|
|
389
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
390
|
+
caso di errore.
|
|
391
|
+
|
|
392
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
393
|
+
"""
|
|
394
|
+
fut = asyncio.get_running_loop().create_future()
|
|
395
|
+
self._impl.delete_backup(password, backup_name, position, self._make_success_tuple(fut, 0), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
396
|
+
return fut
|
|
397
|
+
|
|
398
|
+
def optimize_database(self, password, driver, name, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
399
|
+
"""Esegue una ottimizzazione del database specificato sul server attualmente connesso.
|
|
400
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
401
|
+
caso di errore.
|
|
402
|
+
|
|
403
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
404
|
+
"""
|
|
405
|
+
fut = asyncio.get_running_loop().create_future()
|
|
406
|
+
self._impl.optimize_database(password, driver, name, self._make_success_tuple(fut, 0), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
407
|
+
return fut
|
|
408
|
+
|
|
409
|
+
def repair_database(self, password, driver, name, output, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
410
|
+
"""Prova a riparare il database danneggiato specificato, salvando il database recuperato in *output*.
|
|
411
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
412
|
+
caso di errore.
|
|
413
|
+
|
|
414
|
+
.. note:: Non tutti i driver di database supportano questa operazione, e il recupero del database potrebbe fallire in ogni caso.
|
|
415
|
+
|
|
416
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
417
|
+
"""
|
|
418
|
+
fut = asyncio.get_running_loop().create_future()
|
|
419
|
+
self._impl.repair_database(password, driver, name, output, self._make_success_tuple(fut, 0), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
420
|
+
return fut
|
|
421
|
+
|
|
422
|
+
def index_database(self, password, driver, name, reset=False, run=True, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
423
|
+
"""Esegue una indicizzazione del database specificato sul server attualmente connesso. Se *reset* è ``False``, l'indicizzazione è
|
|
424
|
+
incrementale, ovvero l'indice viene modificato per tenere conto solo dei record inseriti, modificati o cancellati dall'ultima
|
|
425
|
+
indicizzazione; se invece *reset* è ``True`` l'indice viene prima cancellato e poi, se *run* è anch'esso ``True``, viene ricreato completamente.
|
|
426
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
427
|
+
caso di errore.
|
|
428
|
+
|
|
429
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
430
|
+
"""
|
|
431
|
+
fut = asyncio.get_running_loop().create_future()
|
|
432
|
+
self._impl.index_database(password, driver, name, reset, run, self._make_success_tuple(fut, 0), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
433
|
+
return fut
|
|
434
|
+
|
|
435
|
+
def list_clients(self, full=True, any=False, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
436
|
+
fut = asyncio.get_running_loop().create_future()
|
|
437
|
+
self._impl.list_clients(full, any, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
438
|
+
return fut
|
|
439
|
+
|
|
440
|
+
def get_client_info(self, id, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
441
|
+
fut = asyncio.get_running_loop().create_future()
|
|
442
|
+
self._impl.get_client_info(id, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
443
|
+
return fut
|
|
444
|
+
|
|
445
|
+
def kill_client(self, id, password, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
446
|
+
fut = asyncio.get_running_loop().create_future()
|
|
447
|
+
self._impl.kill_client(id, password, self._make_success_tuple(fut, 0), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
448
|
+
return fut
|
|
449
|
+
|
|
450
|
+
def authenticate(self, username, password, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT, new_password=None):
|
|
451
|
+
"""Effettua un accesso al database attivo sulla connessione corrente, identificando l'utente tramite i parametri *username* e *password*.
|
|
452
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà un ``dict`` con informazioni dettagliate
|
|
453
|
+
sull'utente autenticato, oppure viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
454
|
+
"""
|
|
455
|
+
fut = asyncio.get_running_loop().create_future()
|
|
456
|
+
self._impl.authenticate(username, password, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout, new_password)
|
|
457
|
+
return fut
|
|
458
|
+
|
|
459
|
+
def full_text_search(self, text, limit, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
460
|
+
"""Esegue una ricerca full-text sul database attivo sulla connessione corrente, limitando la ricerca di *text* a *limit* risultati.
|
|
461
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà una lista di risultati, dove ogni
|
|
462
|
+
risultato è ``dict`` con almeno le chiavi *score*, *tablename*, *id* e *display*; in caso di errore viene lanciata l'eccezione
|
|
463
|
+
:class:`~kongalib.Error`.
|
|
464
|
+
"""
|
|
465
|
+
fut = asyncio.get_running_loop().create_future()
|
|
466
|
+
self._impl.full_text_search(text, limit, self._make_success_tuple(fut, 1), self._make_error(fut), self._make_progress(fut, progress, userdata), None, timeout)
|
|
467
|
+
return fut
|
|
468
|
+
|
|
469
|
+
def get_permissions(self, user_id):
|
|
470
|
+
return self._execute(CMD_GET_PERMISSIONS, {
|
|
471
|
+
IN_USER_ID: user_id
|
|
472
|
+
}, OUT_PERMISSIONS)
|
|
473
|
+
|
|
474
|
+
def set_permissions(self, user_id, permissions):
|
|
475
|
+
return self._execute(CMD_SET_PERMISSIONS, {
|
|
476
|
+
IN_USER_ID: user_id,
|
|
477
|
+
IN_PERMISSIONS: permissions
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
def begin_transaction(self, pause_indexing=False, deferred=False):
|
|
481
|
+
"""Inizia una transazione sul database attivo nella connessione corrente. Se *pause_indexing* è ``True``, l'indicizzazione del
|
|
482
|
+
database è disabilitata sul server.
|
|
483
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
484
|
+
caso di errore.
|
|
485
|
+
"""
|
|
486
|
+
flags = 0
|
|
487
|
+
if pause_indexing:
|
|
488
|
+
flags |= 0x1
|
|
489
|
+
if deferred:
|
|
490
|
+
flags |= 0x2
|
|
491
|
+
return self._execute(CMD_BEGIN_TRANSACTION, {
|
|
492
|
+
IN_FLAGS: flags
|
|
493
|
+
})
|
|
494
|
+
|
|
495
|
+
def commit_transaction(self, resume_indexing=False):
|
|
496
|
+
"""Esegue una COMMIT della transazione sul database attivo nella connessione corrente. Se *resume_indexing* è ``True``, l'indicizzazione
|
|
497
|
+
del database è abilitata sul server.
|
|
498
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
499
|
+
caso di errore.
|
|
500
|
+
"""
|
|
501
|
+
flags = 0
|
|
502
|
+
if resume_indexing:
|
|
503
|
+
flags |= 0x1
|
|
504
|
+
return self._execute(CMD_COMMIT_TRANSACTION, {
|
|
505
|
+
IN_FLAGS: flags
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
def rollback_transaction(self, resume_indexing=False):
|
|
509
|
+
"""Esegue un ROLLBACK della transazione sul database attivo nella connessione corrente. Se *resume_indexing* è ``True``, l'indicizzazione
|
|
510
|
+
del database è abilitata sul server.
|
|
511
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
512
|
+
caso di errore.
|
|
513
|
+
"""
|
|
514
|
+
flags = 0
|
|
515
|
+
if resume_indexing:
|
|
516
|
+
flags |= 0x1
|
|
517
|
+
return self._execute(CMD_ROLLBACK_TRANSACTION, {
|
|
518
|
+
IN_FLAGS: flags
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
def lock_resource(self, command, row_id=None):
|
|
522
|
+
"""Tenta di eseguire il blocco della risorsa identificata da *command*. Se *row_id* è diverso da ``None``, è possibile eseguire il
|
|
523
|
+
blocco di una singola riga di una tabella del database.
|
|
524
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà una tupla ``(result, owner_data)``,
|
|
525
|
+
dove *owner_data* è un ``dict`` contenente informazioni sull'utente che detiene già il blocco della risorsa in caso fosse già bloccata,
|
|
526
|
+
oppure lancia un'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
527
|
+
"""
|
|
528
|
+
return self._execute(CMD_LOCK, {
|
|
529
|
+
IN_COMMAND_NAME: command,
|
|
530
|
+
IN_ROW_ID: row_id
|
|
531
|
+
}, ( OUT_ANSWER, OUT_OWNER_DATA ))
|
|
532
|
+
|
|
533
|
+
def unlock_resource(self, command, row_id=None):
|
|
534
|
+
"""Rilascia il blocco della risorsa identificata da *tablename* e *row_id*.
|
|
535
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona, e verrà lanciata l'eccezione :class:`~kongalib.Error` in
|
|
536
|
+
caso di errore.
|
|
537
|
+
"""
|
|
538
|
+
return self._execute(CMD_UNLOCK, {
|
|
539
|
+
IN_COMMAND_NAME: command,
|
|
540
|
+
IN_ROW_ID: row_id
|
|
541
|
+
}, OUT_ANSWER)
|
|
542
|
+
|
|
543
|
+
def select_data(self, tablename, fieldnamelist=None, where_expr=None, order_by=None, order_desc=False, offset=0, count=None, get_total=False, exist=None, progress=None):
|
|
544
|
+
"""Genera ed esegue una SELECT sul server per ottenere una lista di risultati, a partire dalla tabella *tablename*.
|
|
545
|
+
*fieldnamelist* è una lista di nomi dei campi da ottenere; se un campo fk_X di *tablename* è una foreign key, si può accedere ai
|
|
546
|
+
campi della tabella collegata Y specificando "fk_X.Campo_di_Y"; la JOIN corrispondente verrà generata e gestita automaticamente dal
|
|
547
|
+
server. Analogamente, si possono creare catene di JOIN implicite facendo riferimenti multipli di campi foreign key, per esempio
|
|
548
|
+
"fk_X.fk_Y.fk_Z.Campo_di_Z".
|
|
549
|
+
|
|
550
|
+
Se *where_expr* non è ``None``, può essere il corpo di una espressione WHERE SQL, e può contenere riferimenti nella stessa forma di
|
|
551
|
+
*fieldnamelist*, per esempio "(Campo_di_X = 1) AND (fk_X.Campo_di_Y > 5)".
|
|
552
|
+
|
|
553
|
+
*order_by* può essere un nome di campo per cui ordinare i risultati, dove *order_desc* specifica se ordinare in modo ascendente o discendente.
|
|
554
|
+
*offset* e *count* permettono di restituire risultati solo a partire dal numero *offset*, e limitandosi a *count* risultati.
|
|
555
|
+
|
|
556
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato dipende dal valore del parametro *get_total*.
|
|
557
|
+
Se *get_total* è ``True``, il risultato sarà una tupla nella forma ``(result_set, total_rows, exist_results)``; *total_rows* sarà il numero
|
|
558
|
+
totale di righe come se *offset* e *limit* non fossero stati specificati, mentre *exist_results* sarà un ``dict`` le cui chiavi saranno gli
|
|
559
|
+
ID specificati nel parametro *exist*, e i valori saranno ``True`` o ``False`` a seconda che il corrispettivo ID sia presente nel database
|
|
560
|
+
per la tabella *tablename* oppure no.
|
|
561
|
+
Se *get_total* è ``False``, il risultato sarà il solo *result_set*, ossia una lista di righe risultato della query, dove ogni riga è una
|
|
562
|
+
lista di valori.
|
|
563
|
+
"""
|
|
564
|
+
if isinstance(fieldnamelist, str):
|
|
565
|
+
fieldnamelist = [ fieldnamelist ]
|
|
566
|
+
elif fieldnamelist:
|
|
567
|
+
fieldnamelist = list(fieldnamelist)
|
|
568
|
+
return self._execute(CMD_SELECT, {
|
|
569
|
+
IN_TABLE_NAME: tablename,
|
|
570
|
+
IN_COLUMN_NAMES: fieldnamelist,
|
|
571
|
+
IN_WHERE_CLAUSE: where(where_expr),
|
|
572
|
+
IN_ORDER_BY: order_by,
|
|
573
|
+
IN_ORDER_DESC: order_desc,
|
|
574
|
+
IN_OFFSET: offset,
|
|
575
|
+
IN_ROW_COUNT: count,
|
|
576
|
+
IN_GET_TOTAL_ROWS: get_total,
|
|
577
|
+
IN_GET_ROWS_EXIST: exist,
|
|
578
|
+
}, ( OUT_RESULT_SET, OUT_TOTAL_ROWS, OUT_EXIST ) if get_total else OUT_RESULT_SET, progress=progress)
|
|
579
|
+
|
|
580
|
+
def select_data_as_dict(self, tablename, fieldnamelist=None, where_expr=None, order_by=None, order_desc=False, offset=0, count=None, get_total=False, progress=None):
|
|
581
|
+
"""Esattamente come :meth:`.select_data`, ma l'oggetto ``asyncio.Future`` restituito una volta completato ritornerà un *result_set* sotto
|
|
582
|
+
forma di lista di ``dict``, anzichè una lista di liste."""
|
|
583
|
+
if isinstance(fieldnamelist, str):
|
|
584
|
+
fieldnamelist = [ fieldnamelist ]
|
|
585
|
+
elif fieldnamelist:
|
|
586
|
+
fieldnamelist = list(fieldnamelist)
|
|
587
|
+
def get_result(output):
|
|
588
|
+
names = output.get(OUT_COLUMN_NAMES, None) or fieldnamelist
|
|
589
|
+
result_set = [dict(list(zip(names, row))) for row in output[OUT_RESULT_SET] ]
|
|
590
|
+
if get_total:
|
|
591
|
+
return ( result_set, output[OUT_TOTAL_ROWS], output[OUT_EXIST] )
|
|
592
|
+
else:
|
|
593
|
+
return result_set
|
|
594
|
+
return self._execute(CMD_SELECT, {
|
|
595
|
+
IN_TABLE_NAME: tablename,
|
|
596
|
+
IN_COLUMN_NAMES: fieldnamelist,
|
|
597
|
+
IN_WHERE_CLAUSE: where(where_expr),
|
|
598
|
+
IN_ORDER_BY: order_by,
|
|
599
|
+
IN_ORDER_DESC: order_desc,
|
|
600
|
+
IN_OFFSET: offset,
|
|
601
|
+
IN_ROW_COUNT: count,
|
|
602
|
+
IN_GET_TOTAL_ROWS: get_total,
|
|
603
|
+
}, get_result, progress=progress)
|
|
604
|
+
|
|
605
|
+
def get_record(self, tablename, code=None, id=None, field_names=None, row_extra_field_names=None, code_azienda=None, num_esercizio=None, mode=None, mask_binary=None, flags=GET_FLAG_DEFAULT, progress=None):
|
|
606
|
+
"""Ottiene il record completo della tabella *tablename*, sotto forma di ``dict``. Il record può essere identificato in due modi: o
|
|
607
|
+
tramite il solo *id*, oppure tramite la specifica dei parametri *code*, *code_azienda* e *num_esercizio*.
|
|
608
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà un ``dict`` con il record ottenuto;
|
|
609
|
+
verrà lanciata un'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
610
|
+
"""
|
|
611
|
+
if (id is None) and (code is None):
|
|
612
|
+
raise ValueError('Either code or id must be specified')
|
|
613
|
+
def get_result(output):
|
|
614
|
+
data = output[OUT_DICT_DATA]
|
|
615
|
+
data['@checksum'] = output[OUT_CHECKSUM]
|
|
616
|
+
return data
|
|
617
|
+
return self._execute(CMD_GET, {
|
|
618
|
+
IN_TABLE_NAME: tablename,
|
|
619
|
+
IN_ROW_ID: id,
|
|
620
|
+
IN_CODE: code,
|
|
621
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
622
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
623
|
+
IN_FLAGS: flags,
|
|
624
|
+
IN_COLUMN_NAMES: field_names,
|
|
625
|
+
IN_ROW_EXTRA_FIELDS: row_extra_field_names,
|
|
626
|
+
}, get_result, progress=progress)
|
|
627
|
+
|
|
628
|
+
def insert_record(self, tablename, data, code_azienda=None, num_esercizio=None, log=None, progress=None):
|
|
629
|
+
"""Inserisce un nuovo record nella tabella *tablename*. Il nuovo record, i cui dati sono passati nel ``dict`` *data*, sarà un record
|
|
630
|
+
condiviso con tutte le aziende del database se *code_azienda* e *num_esercizio* sono ``None``, altrimenti apparterrà solo all'azienda e
|
|
631
|
+
all'esercizio specificati.
|
|
632
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà una tupla nella forma ``(id, code)``,
|
|
633
|
+
dove *id* è l'ID univoco assegnato al record dal server, mentre *code* è il codice del record (che può essere diverso da quello passato
|
|
634
|
+
in *data* se sono attivi i codici automatici per *tablename*); in caso di errore verrà lanciata un'eccezione di classe
|
|
635
|
+
:class:`~kongalib.Error` o :class:`~kongalib.ErrorList`. Al termine dell'operazione, se *log* è un oggetto di classe :class:`OperationLog`,
|
|
636
|
+
esso riceverà ogni eventuale messaggio di log prodotto dal server durante l'inserimento.
|
|
637
|
+
"""
|
|
638
|
+
return self._execute(CMD_INSERT_FROM_DICT, {
|
|
639
|
+
IN_TABLE_NAME: tablename,
|
|
640
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
641
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
642
|
+
IN_DICT_DATA: data
|
|
643
|
+
}, ( OUT_ID, OUT_CODE ), progress=progress, log=log)
|
|
644
|
+
|
|
645
|
+
def update_record(self, tablename, data, code=None, id=None, code_azienda=None, num_esercizio=None, log=None, progress=None):
|
|
646
|
+
"""Aggiorna un record esistente nella tabella *tablename*. Il record, i cui dati da aggiornare sono passati nel ``dict`` *data*, può
|
|
647
|
+
essere identificato in due modi: o tramite il solo *id*, oppure tramite la specifica dei parametri *code*, *code_azienda* e *num_esercizio*.
|
|
648
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona; in caso di errore verrà lanciata un'eccezione di classe
|
|
649
|
+
:class:`~kongalib.Error` o :class:`~kongalib.ErrorList`. Al termine dell'operazione, se *log* è un oggetto di classe :class:`OperationLog`,
|
|
650
|
+
esso riceverà ogni eventuale messaggio di log prodotto dal server durante l'aggiornamento.
|
|
651
|
+
"""
|
|
652
|
+
return self._execute(CMD_UPDATE_FROM_DICT, {
|
|
653
|
+
IN_TABLE_NAME: tablename,
|
|
654
|
+
IN_ROW_ID: id,
|
|
655
|
+
IN_CODE: code,
|
|
656
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
657
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
658
|
+
IN_DICT_DATA: data
|
|
659
|
+
}, progress=progress, log=log)
|
|
660
|
+
|
|
661
|
+
def delete_record(self, tablename, code=None, id=None, code_azienda=None, num_esercizio=None, log=None, progress=None):
|
|
662
|
+
"""Cancella un record dalla tabella *tablename*. Il record può essere identificato in due modi: o tramite il solo *id*, oppure tramite
|
|
663
|
+
la specifica dei parametri *code*, *code_azienda* e *num_esercizio*.
|
|
664
|
+
La funzione restituisce un oggetto ``asyncio.Future`` per l'esecuzione asincrona; in caso di errore verrà lanciata un'eccezione di classe
|
|
665
|
+
:class:`~kongalib.Error` o :class:`~kongalib.ErrorList`. Al termine dell'operazione, se *log* è un oggetto di classe :class:`OperationLog`,
|
|
666
|
+
esso riceverà ogni eventuale messaggio di log prodotto dal server durante la cancellazione.
|
|
667
|
+
"""
|
|
668
|
+
return self._execute(CMD_DELETE_FROM_CODE, {
|
|
669
|
+
IN_TABLE_NAME: tablename,
|
|
670
|
+
IN_ROW_ID: id,
|
|
671
|
+
IN_CODE: code,
|
|
672
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
673
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
674
|
+
}, progress=progress, log=log)
|
|
675
|
+
|
|
676
|
+
def code_exists(self, tablename, code, code_azienda, num_esercizio, extra_where=None):
|
|
677
|
+
"""Controlla l'esistenza del codice *code* nella tabella *tablename* per l'azienda e l'esercizio specificati in *code_azienda* e *num_esercizio*.
|
|
678
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà un valore booleano; in caso di errore verrà
|
|
679
|
+
lanciata un'eccezione di classe :class:`~kongalib.Error`.
|
|
680
|
+
"""
|
|
681
|
+
return self._execute(CMD_CODE_EXISTS, {
|
|
682
|
+
IN_TABLE_NAME: tablename,
|
|
683
|
+
IN_CODE: code,
|
|
684
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
685
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
686
|
+
IN_EXTRA_WHERE: where(extra_where),
|
|
687
|
+
}, OUT_EXISTS)
|
|
688
|
+
|
|
689
|
+
def get_next_available_code(self, tablename, code_azienda, num_esercizio, dry_run=False, force=False):
|
|
690
|
+
return self._execute(CMD_GET_NEXT_CODE, {
|
|
691
|
+
IN_TABLE_NAME: tablename,
|
|
692
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
693
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
694
|
+
IN_DRY_RUN: dry_run,
|
|
695
|
+
IN_FORCE: force,
|
|
696
|
+
}, OUT_CODE)
|
|
697
|
+
|
|
698
|
+
def get_last_npfe(self, code_azienda, num_esercizio):
|
|
699
|
+
return self._execute(CMD_GET_LAST_NPFE, {
|
|
700
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
701
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
702
|
+
}, OUT_NPFE)
|
|
703
|
+
|
|
704
|
+
def start_elab(self, command, params, code_azienda, num_esercizio, log=None, progress=None, tx=True):
|
|
705
|
+
return self._execute(CMD_START_ELAB, {
|
|
706
|
+
IN_COMMAND: command,
|
|
707
|
+
IN_PARAMS: params,
|
|
708
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
709
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
710
|
+
IN_TX: tx,
|
|
711
|
+
}, OUT_DATA, progress=progress, log=log)
|
|
712
|
+
|
|
713
|
+
def list_binaries(self, field_or_tablename, id, type=None, progress=None, full=False):
|
|
714
|
+
"""Ottiene la lista dei dati binari associati ad una scheda del database, identificata da *field_or_tablename* (che può essere un nome
|
|
715
|
+
tabella o un campo da cui risolvere il nome tabella) e *id*. La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato
|
|
716
|
+
una volta completato sarà una lista di tuple, in cui la n-esima tupla ha la forma ``( Tipo, NomeAllegato, NomeOriginale )``; *Tipo*
|
|
717
|
+
è un intero ed è uno dei valori della *Choice* ``Resources``, *NomeAllegato* è il nome assegnato internamente a Konga per identificare
|
|
718
|
+
univocamente il contenuto binario, mentre *NomeOriginale* è il nome del file originale da cui è stato caricato il contenuto. Se *type*
|
|
719
|
+
è specificato, la funzione filtrerà i risultati in base ad esso, ritornando solo le tuple con il *Tipo* corretto. Se *full* è ``True``
|
|
720
|
+
la n-esima tupla ritornata avrà tre valori in più corrispondenti all'etichetta dell'immagine aggiuntiva (se specificata), al codice
|
|
721
|
+
della tipologia dell'allegato e ai metadati associati (se presenti), e la tupla avrà quindi la forma
|
|
722
|
+
``( Tipo, NomeAllegato, NomeOriginale, Etichetta, CodiceTipologia, Metadati )``.
|
|
723
|
+
"""
|
|
724
|
+
def get_result(output):
|
|
725
|
+
return [ tuple(row) for row in output[OUT_LIST] if ((type is None) or (row[0] == type)) ]
|
|
726
|
+
return self._execute(CMD_LIST_BINARIES, {
|
|
727
|
+
IN_FIELD_NAME: field_or_tablename,
|
|
728
|
+
IN_ROW_ID: id,
|
|
729
|
+
IN_FULL: full,
|
|
730
|
+
}, get_result, progress=progress)
|
|
731
|
+
|
|
732
|
+
def fetch_image(self, fieldname, id, type, progress=None, label=None):
|
|
733
|
+
"""Piccolo wrapper alla funzione :meth:`.fetch_binary`, dedicato alle immagini, con l'unica differenza che l'oggetto ``asyncio.Future``
|
|
734
|
+
restituito una volta completato avrà come valore di ritorno direttamente il contenuto binario dell'immagine.
|
|
735
|
+
"""
|
|
736
|
+
return self._execute(CMD_FETCH_BINARY, {
|
|
737
|
+
IN_FIELD_NAME: fieldname,
|
|
738
|
+
IN_ROW_ID: id,
|
|
739
|
+
IN_TYPE: type,
|
|
740
|
+
IN_LABEL: label,
|
|
741
|
+
}, OUT_DATA, progress=progress)
|
|
742
|
+
|
|
743
|
+
def fetch_binary(self, field_or_tablename, id, type, filename=None, check_only=False, progress=None, label=None, with_extra=False):
|
|
744
|
+
"""Carica un contenuto binario dal server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
745
|
+
questa tabella unita a *id* identificano la scheda del database da cui caricare la risorsa; *type* è uno dei valori della *Choice*
|
|
746
|
+
``Resources``, mentre *filename* e *label* hanno senso solo per identificare rispettivamente le risorse di tipo documento ed immagine
|
|
747
|
+
aggiuntiva.
|
|
748
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà una tupla di quattro elementi:
|
|
749
|
+
( *dati*, *filename*, *original_filename*, *checksum* ). *dati* sono i dati binari che sono stati caricati dal server; *filename* è
|
|
750
|
+
il nome file interno con cui è identificata la risorsa, *original_filename* è il nome del file originale che è stato specificato
|
|
751
|
+
all'atto del salvataggio della risorsa sul server, mentre *checksum* è un checksum dei dati. Se *with_extra* è ``True``, la funzione
|
|
752
|
+
ritorna sei elementi, e gli elementi aggiuntivi sono un ``dict`` con i metadata associati alla risorsa, o ``None`` se non ci sono
|
|
753
|
+
metadati associati, e il codice della tipologia allegato se presente. Se *check_only* è ``True``, i dati binari della risorsa non
|
|
754
|
+
verranno effettivamente caricati dal dispositivo di archiviazione in cui sono depositati, e *dati* sarà ``None``; questa modalità è
|
|
755
|
+
utile per verificare l'esistenza di una risorsa e il suo checksum senza effettivamente caricarla da remoto (nel caso di archiviazione
|
|
756
|
+
su cloud il caricamento potrebbe essere lento)."""
|
|
757
|
+
if (type == 0) and (not filename):
|
|
758
|
+
raise ValueError('filename must be specified for document type resources')
|
|
759
|
+
if with_extra:
|
|
760
|
+
out_params = ( OUT_DATA, OUT_FILENAME, OUT_ORIGINAL_FILENAME, OUT_DATA_CHECKSUM, OUT_METADATA, OUT_CODE_TIPOLOGIA )
|
|
761
|
+
else:
|
|
762
|
+
out_params = ( OUT_DATA, OUT_FILENAME, OUT_ORIGINAL_FILENAME, OUT_DATA_CHECKSUM )
|
|
763
|
+
return self._execute(CMD_FETCH_BINARY, {
|
|
764
|
+
IN_FIELD_NAME: field_or_tablename,
|
|
765
|
+
IN_ROW_ID: id,
|
|
766
|
+
IN_TYPE: type,
|
|
767
|
+
IN_FILENAME: filename,
|
|
768
|
+
IN_LABEL: label,
|
|
769
|
+
IN_CHECK: check_only,
|
|
770
|
+
}, out_params, progress=progress)
|
|
771
|
+
|
|
772
|
+
def store_binary(self, field_or_tablename, id, type, filename=None, original_filename=None, data=None, desc=None, force_delete=False, code_azienda=None, progress=None, label=None, metadata=None, code_tipologia=None, log=None):
|
|
773
|
+
"""Salva un contenuto binario sul server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
774
|
+
questa tabella unita a *id* identificano la scheda a cui abbinare la risorsa; *type* è uno dei valori della *Choice*``Resources``;
|
|
775
|
+
*filename* permette di specificare un nome file interno con cui identificare la risorsa (se ``None`` il server genererà un nome univoco
|
|
776
|
+
automaticamente); *original_filename* è il nome file originale i cui dati si stanno salvando sul server; *data* sono i dati binari
|
|
777
|
+
effettivi; *desc* è la descrizione da abbinare alla risorsa; *code_azienda* infine identifica l'azienda su cui si sta operando, mentre
|
|
778
|
+
*code_tipologia* permette di specificare una tipologia da abbinare al dati. Per le risorse di tipo immagine aggiuntiva è necessario
|
|
779
|
+
specificare una *label* da abbinare all'immagine per identificarla univocamente. *metadata* può essere un ``dict`` in cui sia chiavi che
|
|
780
|
+
valori siano delle semplici stringhe, e permette di specificare dei metadati aggiuntivi associati alla risorsa binaria che si sta inserendo.
|
|
781
|
+
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà il nome del file interno usato dal
|
|
782
|
+
server per identificare la risorsa, che come detto sopra è uguale a *filename* se quest'ultimo è diverso da ``None``, altrimenti sarà
|
|
783
|
+
il nome file generato dal server.
|
|
784
|
+
Se *data* è ``None``, la funzione cancella i dati binari associati alla scheda; *force_delete* in questo caso può essere ``True`` se
|
|
785
|
+
si desidera cancellare il riferimento ai dati anche se i dati non sono raggiungibili dal server."""
|
|
786
|
+
return self._execute(CMD_STORE_BINARY, {
|
|
787
|
+
IN_FIELD_NAME: field_or_tablename,
|
|
788
|
+
IN_ROW_ID: id,
|
|
789
|
+
IN_TYPE: type,
|
|
790
|
+
IN_FILENAME: filename,
|
|
791
|
+
IN_ORIGINAL_FILENAME: original_filename,
|
|
792
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
793
|
+
IN_DATA: data,
|
|
794
|
+
IN_DESC: desc,
|
|
795
|
+
IN_FORCE_DELETE: force_delete,
|
|
796
|
+
IN_LABEL: label,
|
|
797
|
+
IN_METADATA: metadata,
|
|
798
|
+
IN_CODE_TIPOLOGIA: code_tipologia,
|
|
799
|
+
}, OUT_FILENAME, progress=progress, log=log)
|
|
800
|
+
|
|
801
|
+
def translate(self, field, value, language, code_azienda=None):
|
|
802
|
+
return self._execute(CMD_TRANSLATE, {
|
|
803
|
+
IN_FIELD: field,
|
|
804
|
+
IN_VALUE: value,
|
|
805
|
+
IN_LANGUAGE: language,
|
|
806
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
807
|
+
}, OUT_TEXT)
|
|
808
|
+
|
|
809
|
+
def set_database_language(self, language, progress=None):
|
|
810
|
+
return self._execute(CMD_SET_DATABASE_LANGUAGE, {
|
|
811
|
+
IN_LANGUAGE: language,
|
|
812
|
+
}, progress=progress)
|
|
813
|
+
|