kongalib 2.0.4__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.
Potentially problematic release.
This version of kongalib might be problematic. Click here for more details.
- _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.4.dist-info/METADATA +150 -0
- kongalib-2.0.4.dist-info/RECORD +21 -0
- kongalib-2.0.4.dist-info/WHEEL +6 -0
- kongalib-2.0.4.dist-info/licenses/LICENSE +165 -0
- kongalib-2.0.4.dist-info/top_level.txt +4 -0
- kongalib-2.0.4.dist-info/zip-safe +1 -0
- kongaui.py +507 -0
- kongautil.py +581 -0
kongalib/client.py
ADDED
|
@@ -0,0 +1,1045 @@
|
|
|
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 sys
|
|
19
|
+
import threading
|
|
20
|
+
|
|
21
|
+
from kongalib import Error, ErrorList
|
|
22
|
+
from .constants import *
|
|
23
|
+
from .expression import *
|
|
24
|
+
from .data_dictionary import *
|
|
25
|
+
|
|
26
|
+
from _kongalib import Client as ClientImpl
|
|
27
|
+
from _kongalib import start_timer
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
DEFAULT_DISCOVER_TIMEOUT = 5000
|
|
31
|
+
DEFAULT_CONNECT_TIMEOUT = 30000
|
|
32
|
+
DEFAULT_EXECUTE_TIMEOUT = 180000
|
|
33
|
+
|
|
34
|
+
OUT_ERROR = 'ERROR'
|
|
35
|
+
OUT_ERRNO = 'ERRNO'
|
|
36
|
+
|
|
37
|
+
GET_FLAG_GET_MASK = 0x000000FF
|
|
38
|
+
GET_FLAG_GET_IMAGES = 0x00000001
|
|
39
|
+
GET_FLAG_GET_ATTACHMENTS = 0x00000002
|
|
40
|
+
GET_FLAG_GET_NOTES = 0x00000004
|
|
41
|
+
GET_FLAG_GET_EVENTS = 0x00000008
|
|
42
|
+
GET_FLAG_GET_TRANSLATIONS = 0x00000010
|
|
43
|
+
GET_FLAG_ACTION_MASK = 0x00000F00
|
|
44
|
+
GET_FLAG_ACTION_COUNT = 0x00000100
|
|
45
|
+
GET_FLAG_ACTION_LIST = 0x00000200
|
|
46
|
+
GET_FLAG_ACTION_CONTENTS = 0x00000400
|
|
47
|
+
GET_FLAG_SKIP_MASK = 0x000FF000
|
|
48
|
+
GET_FLAG_SKIP_SPECIAL_FIELDS = 0x00001000
|
|
49
|
+
GET_FLAG_SKIP_ROWS = 0x00002000
|
|
50
|
+
GET_FLAG_SKIP_NORMAL_FIELDS = 0x00004000
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
GET_FLAG_DEFAULT = GET_FLAG_GET_IMAGES | GET_FLAG_GET_ATTACHMENTS | GET_FLAG_GET_NOTES | GET_FLAG_GET_EVENTS | GET_FLAG_GET_TRANSLATIONS | GET_FLAG_ACTION_COUNT
|
|
54
|
+
|
|
55
|
+
IMAGE_NORMAL = 1
|
|
56
|
+
IMAGE_WEB = 2
|
|
57
|
+
IMAGE_THUMBNAIL = 3
|
|
58
|
+
IMAGE_EXTRA = 4
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def make_callbacks(success, error, log=None):
|
|
62
|
+
def callback(output, dummy):
|
|
63
|
+
answer = output[OUT_LOG] or []
|
|
64
|
+
error_list = ErrorList(answer)
|
|
65
|
+
if output[OUT_ERRNO] == OK:
|
|
66
|
+
if len(answer) > 0:
|
|
67
|
+
if log is None:
|
|
68
|
+
if error is not None:
|
|
69
|
+
error(error_list)
|
|
70
|
+
else:
|
|
71
|
+
error_list.prepare_log(log)
|
|
72
|
+
if log.has_errors():
|
|
73
|
+
if error is not None:
|
|
74
|
+
error(error_list)
|
|
75
|
+
else:
|
|
76
|
+
success(output)
|
|
77
|
+
else:
|
|
78
|
+
success(output)
|
|
79
|
+
elif error is not None:
|
|
80
|
+
if error_list.errno == OK:
|
|
81
|
+
error(ErrorList.from_error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
82
|
+
else:
|
|
83
|
+
error(error_list)
|
|
84
|
+
def errback(errno, errstr, dummy):
|
|
85
|
+
if error is not None:
|
|
86
|
+
error(ErrorList.from_error(errno, errstr))
|
|
87
|
+
return callback, errback
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _check_result(output, result_callback=None):
|
|
92
|
+
answer = output[OUT_LOG] or []
|
|
93
|
+
e = ErrorList(answer)
|
|
94
|
+
if e.errno != OK:
|
|
95
|
+
raise e
|
|
96
|
+
elif output[OUT_ERRNO] == OK:
|
|
97
|
+
if result_callback is not None:
|
|
98
|
+
return result_callback()
|
|
99
|
+
return None
|
|
100
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class Client(object):
|
|
105
|
+
"""La classe Client permette di connettersi ad un server Konga e di eseguire comandi sullo stesso.
|
|
106
|
+
|
|
107
|
+
Molti dei metodi di questa classe possono eseguire operazioni sia in maniera sincrona (bloccante) che asincrona tramite
|
|
108
|
+
l'uso di una callback. Nel caso un metodo sia eseguito in modo asincrono, viene sempre restituito immediatamente un oggetto
|
|
109
|
+
di classe :class:`~kongalib.Deferred`, e la callback viene eseguita a tempo debito in un thread separato.
|
|
110
|
+
|
|
111
|
+
.. note:: Nelle chiamate asincrone spesso è possibile specificare una callback di *progress* e una di *error*. La *progress* deve
|
|
112
|
+
essere nella forma ``progress(type, completeness, state, partial, userdata)``; i parametri interessanti di questa callback sono
|
|
113
|
+
*completeness* (percentuale di completamento, ossia un numero che varia da 0.0 a 100.0; se -1.0 indica una percentuale di completamento
|
|
114
|
+
indefinita) e *state* (stringa che specifica l'eventuale stato corrente dell'operazione). *userdata* è un parametro aggiuntivo che
|
|
115
|
+
viene normalmente passato alla chiamata asincrona dall'utente per tenere traccia di un eventuale stato. La *error* deve essere nella
|
|
116
|
+
forma ``error(errno, errstr, userdata)``; fare riferimento ai :ref:`codici di errore <error_codes>` per il significato dei parametri
|
|
117
|
+
*errno* e del corrispettivo *errstr*.
|
|
118
|
+
|
|
119
|
+
Oggetti di classe Client possono essere usati come contesti per il costrutto ``with``: all'ingresso del blocco verrà iniziata una
|
|
120
|
+
transazione, mentre in uscita verrà eseguita una commit o una rollback della stessa a seconda che ci sia stata o meno un'eccezione
|
|
121
|
+
all'interno del blocco di istruzioni.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
DATA_DICTIONARY_CACHE = {}
|
|
125
|
+
DATA_DICTIONARY_LOCK = threading.RLock()
|
|
126
|
+
|
|
127
|
+
def __init__(self, impl=None):
|
|
128
|
+
if impl is None:
|
|
129
|
+
impl = ClientImpl()
|
|
130
|
+
self._impl = impl
|
|
131
|
+
|
|
132
|
+
def __enter__(self):
|
|
133
|
+
self.begin_transaction()
|
|
134
|
+
return self
|
|
135
|
+
|
|
136
|
+
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
137
|
+
if exc_type is None:
|
|
138
|
+
self.commit_transaction()
|
|
139
|
+
else:
|
|
140
|
+
self.rollback_transaction()
|
|
141
|
+
|
|
142
|
+
def as_async(self):
|
|
143
|
+
"""Ritorna un oggetto :class:`~kongalib.AsyncClient` equivalente a questo client, preservando le connessioni già presenti.
|
|
144
|
+
"""
|
|
145
|
+
import kongalib
|
|
146
|
+
return kongalib.AsyncClient(self._impl)
|
|
147
|
+
|
|
148
|
+
def list_servers(self, timeout=DEFAULT_DISCOVER_TIMEOUT, port=0, success=None, progress=None, userdata=None):
|
|
149
|
+
"""Esegue una scansione della rete locale alla ricerca dei server Konga disponibili, attendendo al massimo *timeout* millisecondi
|
|
150
|
+
per una risposta. *port* specifica la porta da cui far partire la scansione (default = 51967); sono controllate le successive 10
|
|
151
|
+
porte UDP con intervallo di 20 porte (quindi di default vengono scansionate le porte 51967, 51987, 52007, ... 52147). Se *success*
|
|
152
|
+
è ``None``, la funzione è bloccante e scaduto il *timeout* restituisce una lista di ``dict``, le cui chiavi principali sono *host*,
|
|
153
|
+
*port*, *name* e *description*. Al contrario, se *success* è una funzione nella forma ``success(servers, userdata)``, ``list_servers``
|
|
154
|
+
restituisce immediatamente un oggetto :class:`~kongalib.Deferred` e la chiamata viene eseguita in modo asincrono; la callback *success*
|
|
155
|
+
verrà invocata a tempo debito con la lista dei risultati (come nel risultato del caso sincrono) ed il parametro *userdata*.
|
|
156
|
+
"""
|
|
157
|
+
return self._impl.list_servers(timeout, port, success, progress, userdata)
|
|
158
|
+
|
|
159
|
+
def connect(self, server=None, host=None, port=0, options=None, timeout=DEFAULT_CONNECT_TIMEOUT, success=None, error=None, progress=None, userdata=None):
|
|
160
|
+
"""Tenta una connessione ad un server Konga. Il server a cui ci si vuole connettere può essere specificato in due modi: tramite i
|
|
161
|
+
parametri *host* e *port*, oppure tramite un ``dict`` *server* che deve contenere almeno le chiavi *host* e *port*. Alternativamente,
|
|
162
|
+
se *server* è una stringa e *host* non è specificato, viene assunta come *host*. Se *host* include una specifica di porta e *port* è ``0``,
|
|
163
|
+
*port* viene ottenuta dalla specifica contenuta nella stringa di *host*. Se *success* è ``None``, la funzione è bloccante; su errore o
|
|
164
|
+
scaduto il *timeout* una eccezione di tipo :class:`~kongalib.Error` è lanciata, altrimenti viene restituito un ``dict`` con informazioni
|
|
165
|
+
sulla connessione appena stabilita. Se *success* è una funzione nella forma ``success(info, userdata)`` allora ``connect`` restituisce
|
|
166
|
+
immediatamente un oggetto :class:`~kongalib.Deferred` e la chiamata viene eseguita in modo asincrono; la callback *success* verrà
|
|
167
|
+
invocata con le informazioni sulla connessione e *userdata* se e quando la connessione viene stabilita.
|
|
168
|
+
Il parametro *options* può essere un ``dict`` contenente opzioni aggiuntive per la connessione; al momento le opzioni supportate sono:
|
|
169
|
+
|
|
170
|
+
- ``tenant_key`` (*str*): chiave del tenant per stabilire la connessione con un server multitenant.
|
|
171
|
+
"""
|
|
172
|
+
if (server is None) and (host is None):
|
|
173
|
+
raise ValueError("either 'host' or 'server' parameter must be specified")
|
|
174
|
+
if isinstance(server, str) and (host is None):
|
|
175
|
+
host = server
|
|
176
|
+
server = None
|
|
177
|
+
if isinstance(host, str) and (port is None) and (':' in host):
|
|
178
|
+
pos = host.rfind(':')
|
|
179
|
+
host = host[:pos]
|
|
180
|
+
try:
|
|
181
|
+
port = int(host[pos+1:])
|
|
182
|
+
except:
|
|
183
|
+
raise ValueError("Invalid port value embedded in host string")
|
|
184
|
+
return self._impl.connect(server, host or '', port, options, timeout, success, error, progress, userdata)
|
|
185
|
+
|
|
186
|
+
def disconnect(self):
|
|
187
|
+
"""Disconnette il server attualmente connesso, oppure non fa nulla se non si è al momento connessi."""
|
|
188
|
+
self._impl.disconnect()
|
|
189
|
+
|
|
190
|
+
def get_id(self):
|
|
191
|
+
"""Restituisce un ID numerico univoco assegnato dal server alla connessione con questo client, o 0 se non si è connessi."""
|
|
192
|
+
return self._impl.get_id()
|
|
193
|
+
|
|
194
|
+
def get_connection_info(self):
|
|
195
|
+
"""Restituisce un ``dict`` con informazioni sulla connessione corrente, o ``None`` se non si è connessi."""
|
|
196
|
+
return self._impl.get_connection_info()
|
|
197
|
+
|
|
198
|
+
def execute(self, command, data=None, timeout=DEFAULT_EXECUTE_TIMEOUT, success=None, error=None, progress=None, idle=None, userdata=None):
|
|
199
|
+
return self._impl.execute(command, data or {}, timeout, success, error, progress, idle, userdata)
|
|
200
|
+
|
|
201
|
+
def interrupt(self):
|
|
202
|
+
"""Interrompe tutte le operazioni al momento in esecuzione da parte di questo client."""
|
|
203
|
+
self._impl.interrupt()
|
|
204
|
+
|
|
205
|
+
def get_data_dictionary(self, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
206
|
+
"""Restituisce il dizionario dei dati disponibile sul server attualmente connesso, sotto forma di oggetto di classe
|
|
207
|
+
:class:`kongalib.DataDictionary`.
|
|
208
|
+
"""
|
|
209
|
+
uuid = self.get_connection_info().get('uuid', None)
|
|
210
|
+
with Client.DATA_DICTIONARY_LOCK:
|
|
211
|
+
if uuid is None:
|
|
212
|
+
data = None
|
|
213
|
+
else:
|
|
214
|
+
data = Client.DATA_DICTIONARY_CACHE.get(uuid, None)
|
|
215
|
+
if data is None:
|
|
216
|
+
def cache(d):
|
|
217
|
+
Client.DATA_DICTIONARY_CACHE[uuid] = d
|
|
218
|
+
return d
|
|
219
|
+
if success is not None:
|
|
220
|
+
def callback(d, dummy):
|
|
221
|
+
with Client.DATA_DICTIONARY_LOCK:
|
|
222
|
+
success(cache(DataDictionary(d)))
|
|
223
|
+
self._impl.get_data_dictionary(callback, error, progress, userdata, timeout)
|
|
224
|
+
else:
|
|
225
|
+
return cache(DataDictionary(self._impl.get_data_dictionary(success, error, progress, userdata, timeout)))
|
|
226
|
+
else:
|
|
227
|
+
if success is not None:
|
|
228
|
+
def callback(dummy):
|
|
229
|
+
success(data)
|
|
230
|
+
start_timer(0, callback)
|
|
231
|
+
else:
|
|
232
|
+
return data
|
|
233
|
+
|
|
234
|
+
def list_drivers(self, configured=True, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
235
|
+
"""Restituisce la lista dei driver di database presenti sul server attualmente connesso, oppure lancia un'eccezione :class:`~kongalib.Error`
|
|
236
|
+
su errore. Ogni elemento della lista restituita è un ``dict`` che comprende la chiavi *name*, *version* e *description*.
|
|
237
|
+
Se *success* è ``None`` la funzione è bloccante, altrimenti se è una funzione nella forma ``success(drivers, userdata)``, restituisce
|
|
238
|
+
immediatamente un oggetto :class:`~kongalib.Deferred` e la chiamata viene eseguita in modo asincrono; la callback *success* verrà
|
|
239
|
+
invocata a tempo debito con la lista dei driver ed il parametro *userdata*. Se *configured* è False, tutti i driver installati sul
|
|
240
|
+
server sono restituiti, altrimenti verranno restituite solo le informazioni sui driver configurati correttamente ed in esecuzione
|
|
241
|
+
sul server.
|
|
242
|
+
"""
|
|
243
|
+
return self._impl.list_drivers(configured, success, error, progress, userdata, timeout)
|
|
244
|
+
|
|
245
|
+
def list_databases(self, driver=None, quick=False, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
246
|
+
"""Restituisce la lista dei database disponibili sul server corrente, appartenenti a tutti o ad uno specifico *driver*. La lista viene tornata
|
|
247
|
+
sotto forma di ``dict``, le cui chiavi sono i nomi dei driver e i valori le liste dei database appartenenti allo specifico driver. Ogni
|
|
248
|
+
database nelle liste è un ``dict`` che contiene almeno le chiavi *name*, *desc*, *uuid*, *created_ts* e *modified_ts*. L'eccezione
|
|
249
|
+
:class:`~kongalib.Error` viene lanciata se si verifica un errore. Se *success* è una funzione nella forma ``success(databases, userdata)``,
|
|
250
|
+
``list_databases`` restituisce immediatamente un oggetto :class:`~kongalib.Deferred` e la chiamata viene eseguita in modo asincrono; la
|
|
251
|
+
callback *success* verrà invocata a tempo debito con la lista dei database ed il parametro *userdata*. Se *quick* è ``True``, la funzione
|
|
252
|
+
ritorna il più velocemente possibile ma la scansione dei database disponibili potrebbe risultare ancora incompleta.
|
|
253
|
+
"""
|
|
254
|
+
return self._impl.list_databases(driver, quick, success, error, progress, userdata, timeout)
|
|
255
|
+
|
|
256
|
+
def create_database(self, password, driver, name, desc='', success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
257
|
+
"""Crea un nuovo database sul server attualmente connesso; il database avrà nome *name* e descrizione *desc*.
|
|
258
|
+
Se *success* è ``None`` la chiamata è bloccante e se il database viene creato con successo viene restituito l'UUID del nuovo database;
|
|
259
|
+
se si verifica un errore viene lanciata l'eccezione :class:`~kongalib.Error`.
|
|
260
|
+
Se *success* è una funzione nella forma ``success(databases, userdata)``, ``create_database`` restituisce immediatamente un oggetto
|
|
261
|
+
:class:`~kongalib.Deferred` e la chiamata viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con la
|
|
262
|
+
lista dei database ed il parametro *userdata*.
|
|
263
|
+
|
|
264
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
265
|
+
"""
|
|
266
|
+
return self._impl.create_database(password, driver, name, desc, success, error, progress, userdata, timeout)
|
|
267
|
+
|
|
268
|
+
def open_database(self, driver, name, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
269
|
+
"""Apre un database rendendolo il database attivo per la connessione corrente. Se *success* è ``None``, la chiamata è bloccante e
|
|
270
|
+
viene tornato un ``dict`` con le informazioni sul database connesso, oppure viene lanciata l'eccezione :class:`~kongalib.Error` in caso
|
|
271
|
+
di errore. Se *success* è una funzione nella forma ``success(info, userdata)``, ``open_database`` restituisce immediatamente un oggetto
|
|
272
|
+
:class:`~kongalib.Deferred` e la chiamata viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con le
|
|
273
|
+
informazioni sul database ed il parametro *userdata*.
|
|
274
|
+
"""
|
|
275
|
+
return self._impl.open_database(driver, name, success, error, progress, userdata, timeout)
|
|
276
|
+
|
|
277
|
+
def close_database(self, backup=False, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
278
|
+
"""Chiude il database attivo sulla connessione corrente.
|
|
279
|
+
Se *success* è ``None``, la chiamata è bloccante e viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
280
|
+
Se *success* è una funzione nella forma ``success(userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
281
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con il parametro *userdata*.
|
|
282
|
+
|
|
283
|
+
.. note:: Se *backup* è ``True``, il server esegue un backup automatico del database prima di chiuderlo.
|
|
284
|
+
"""
|
|
285
|
+
return self._impl.close_database(backup, success, error, progress, userdata, timeout)
|
|
286
|
+
|
|
287
|
+
def upgrade_database(self, password, driver, name, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
288
|
+
"""Aggiorna il database specificato all'ultima versione disponibile.
|
|
289
|
+
Se *success* è ``None``, la chiamata è bloccante e viene restituita una tupla (log, old_version, new_version), dove il log dell'operazione
|
|
290
|
+
è sotto forma di una lista di stringhe, oppure viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore. Se *success* è una
|
|
291
|
+
funzione nella forma ``success(log, old_version, new_version, userdata)``, ``upgrade_database`` restituisce immediatamente un oggetto
|
|
292
|
+
:class:`~kongalib.Deferred` e la chiamata viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con il
|
|
293
|
+
log dell'operazione, la vecchia versione dati, la nuova versione dati ed il parametro *userdata*.
|
|
294
|
+
|
|
295
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
296
|
+
"""
|
|
297
|
+
return self._impl.upgrade_database(password, driver, name, success, error, progress, userdata, timeout)
|
|
298
|
+
|
|
299
|
+
def delete_database(self, password, driver, name, delete_cloud_data=None, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
300
|
+
"""Cancella il database specificato. Se *delete_cloud_data* è ``None`` (valore predefinito) la cancellazione sarà negata nel caso ci siano
|
|
301
|
+
dati binari legati al database al momento presenti nel cloud; in caso contrario i dati binari saranno o meno cancellati dal cloud in base
|
|
302
|
+
al valore del parametro.
|
|
303
|
+
Se *success* è ``None``, la chiamata è bloccante e viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
304
|
+
Se *success* è una funzione nella forma ``success(userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
305
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con il parametro *userdata*.
|
|
306
|
+
|
|
307
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
308
|
+
"""
|
|
309
|
+
return self._impl.delete_database(password, driver, name, delete_cloud_data, success, error, progress, userdata, timeout)
|
|
310
|
+
|
|
311
|
+
def query(self, query, native=False, full_column_names=False, collapse_blobs=False, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
312
|
+
"""Esegue una query SQL sul database attivo nella connessione corrente. Se *native* è ``True``, la query viene passata al driver
|
|
313
|
+
del database senza essere interpretata, permettendo l'esecuzione di query native per l'RDBMS sottostante. Se *success* è ``None``, la
|
|
314
|
+
chiamata è bloccante e viene restituita una tupla ``(affected_rows, column_names, result_set)``; *affected_rows* è il numero di righe
|
|
315
|
+
coinvolte nella query di UPDATE/DELETE, *column_names* è una lista di nomi di colonne per il result set, mentre *result_set* è una lista
|
|
316
|
+
di righe risultati della query, dove ogni riga è una lista di valori corrispondenti alle colonne restituite in *column_names*. In caso di
|
|
317
|
+
errore viene lanciata l'eccezione :class:`~kongalib.Error`. Se *success* è una funzione nella forma
|
|
318
|
+
``success(affected_rows, column_names, result_set, userdata)``, ``query`` restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
319
|
+
e la chiamata viene eseguita in modo asincrono.
|
|
320
|
+
|
|
321
|
+
.. note:: Se *full_column_names* è ``False``, *column_names* includerà i nomi delle colonne senza nome tabella, altrimenti saranno
|
|
322
|
+
inclusi i nomi completi delle colonne. Se *collapse_blobs* è ``True``, i dati di tipo BLOB binari verranno restituiti come ``[...]``.
|
|
323
|
+
"""
|
|
324
|
+
return self._impl.query_database(query, native, full_column_names, collapse_blobs, success, error, progress, userdata, timeout)
|
|
325
|
+
|
|
326
|
+
def backup_database(self, password, backup_name, driver, name, auto=True, overwrite=False, position=0, store_index=False, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
327
|
+
"""Esegue un backup del database specificato sul server attualmente connesso. Se *auto* è ``False``, è necessario specificare un nome
|
|
328
|
+
per il backup tramite *backup_name*, altrimenti il backup viene considerato automatico ed un nome univoco è assegnato dal server. Se
|
|
329
|
+
*overwrite* è ``False`` ed un backup con lo stesso nome esiste già sul server, non sarà possibile eseguire il backup. *position*
|
|
330
|
+
permette di specificare dove eseguire il backup, ed è una combinazione delle costanti :const:`kongalib.BACKUP_ON_COMPUTER` e
|
|
331
|
+
:const:`kongalib.BACKUP_ON_CLOUD`, mentre *store_index* specifica se includere l'indice di ricerca full-text nel backup.
|
|
332
|
+
Se *success* è ``None``, la chiamata è bloccante e viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
333
|
+
Se *success* è una funzione nella forma ``success(userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
334
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con il parametro *userdata*.
|
|
335
|
+
|
|
336
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
337
|
+
"""
|
|
338
|
+
return self._impl.backup_database(password, backup_name, driver, name, auto, overwrite, position, store_index, success, error, progress, userdata, timeout)
|
|
339
|
+
|
|
340
|
+
def restore_database(self, password, backup_name, driver, name, change_uuid=True, overwrite=False, position=0, restore_index=True, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
341
|
+
"""Ripristina un database a partire da un backup effettuato in precedenza sul server connesso. Se *overwrite* è False ed esiste un
|
|
342
|
+
database gestito da *driver* con lo stesso nome, la funzione riporterà errore. *change_uuid* specifica se cambiare l'UUID associato al
|
|
343
|
+
database oppure se ripristinare quello originale; se si hanno database con lo stesso nome gestiti da driver diversi è opportuno che
|
|
344
|
+
almeno l'UUID per essi sia diverso, altrimenti si può incorrere in problemi di aliasing. *position* specifica da dove prendere il
|
|
345
|
+
backup da rispristinare, e deve essere una delle costanti :const:`kongalib.BACKUP_ON_COMPUTER` o :const:`kongalib.BACKUP_ON_CLOUD`;
|
|
346
|
+
*restore_index* invece permette di specificare se ripristinare o meno l'indice di ricerca qualora fosse contenuto all'interno del backup.
|
|
347
|
+
|
|
348
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
349
|
+
"""
|
|
350
|
+
return self._impl.restore_database(password, backup_name, driver, name, change_uuid, overwrite, position, restore_index, success, error, progress, userdata, timeout)
|
|
351
|
+
|
|
352
|
+
def list_backups(self, position=0, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
353
|
+
"""Ottiene la lista dei backup disponibili sul server connesso.
|
|
354
|
+
Se *success* è ``None``, la chiamata è bloccante e restituisce una lista di backup; ogni backup è un ``dict`` che contiene almeno le chiavi
|
|
355
|
+
*backup_name*, *uuid*, *date* e *size*; se si verifica un errore viene lanciata l'eccezione :class:`~kongalib.Error`.
|
|
356
|
+
Se *success* è una funzione nella forma ``success(backups, userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
357
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con la lista dei backup ed il
|
|
358
|
+
parametro *userdata*.
|
|
359
|
+
"""
|
|
360
|
+
return self._impl.list_backups(position, success, error, progress, userdata, timeout)
|
|
361
|
+
|
|
362
|
+
def delete_backup(self, password, backup_name, position, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
363
|
+
"""Cancella il backup identificato da *backup_name* dal server connesso.
|
|
364
|
+
Se *success* è ``None``, la chiamata è bloccante e viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
365
|
+
Se *success* è una funzione nella forma ``success(userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
366
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con il parametro *userdata*.
|
|
367
|
+
|
|
368
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
369
|
+
"""
|
|
370
|
+
return self._impl.delete_backup(password, backup_name, position, success, error, progress, userdata, timeout)
|
|
371
|
+
|
|
372
|
+
def optimize_database(self, password, driver, name, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
373
|
+
"""Esegue una ottimizzazione del database specificato sul server attualmente connesso.
|
|
374
|
+
Se *success* è ``None``, la chiamata è bloccante e viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
375
|
+
Se *success* è una funzione nella forma ``success(userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
376
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con il parametro *userdata*.
|
|
377
|
+
|
|
378
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
379
|
+
"""
|
|
380
|
+
return self._impl.optimize_database(password, driver, name, success, error, progress, userdata, timeout)
|
|
381
|
+
|
|
382
|
+
def repair_database(self, password, driver, name, output, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
383
|
+
"""Prova a riparare il database danneggiato specificato, salvando il database recuperato in *output*.
|
|
384
|
+
Se *success* è ``None``, la chiamata è bloccante e viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
385
|
+
Se *success* è una funzione nella forma ``success(userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
386
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con il parametro *userdata*.
|
|
387
|
+
|
|
388
|
+
.. note:: Non tutti i driver di database supportano questa operazione, e il recupero del database potrebbe fallire in ogni caso.
|
|
389
|
+
|
|
390
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
391
|
+
"""
|
|
392
|
+
return self._impl.repair_database(password, driver, name, output, success, error, progress, userdata, timeout)
|
|
393
|
+
|
|
394
|
+
def index_database(self, password, driver, name, reset=False, run=True, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
395
|
+
"""Esegue una indicizzazione del database specificato sul server attualmente connesso. Se *reset* è ``False``, l'indicizzazione è
|
|
396
|
+
incrementale, ovvero l'indice viene modificato per tenere conto solo dei record inseriti, modificati o cancellati dall'ultima
|
|
397
|
+
indicizzazione; se invece *reset* è ``True`` l'indice viene prima cancellato e poi, se *run* è anch'esso ``True``, viene ricreato completamente.
|
|
398
|
+
Se *success* è ``None``, la chiamata è bloccante e viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
399
|
+
Se *success* è una funzione nella forma ``success(userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
400
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con il parametro *userdata*.
|
|
401
|
+
|
|
402
|
+
.. warning:: E' necessaria la *password* del server per poter eseguire questa operazione.
|
|
403
|
+
"""
|
|
404
|
+
return self._impl.index_database(password, driver, name, reset, run, success, error, progress, userdata, timeout)
|
|
405
|
+
|
|
406
|
+
def list_clients(self, full=True, any=False, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
407
|
+
return self._impl.list_clients(full, any, success, error, progress, userdata, timeout)
|
|
408
|
+
|
|
409
|
+
def get_client_info(self, id, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
410
|
+
return self._impl.get_client_info(id, success, error, progress, userdata, timeout)
|
|
411
|
+
|
|
412
|
+
def kill_client(self, id, password, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
413
|
+
return self._impl.kill_client(id, password, success, error, progress, userdata, timeout)
|
|
414
|
+
|
|
415
|
+
def authenticate(self, username, password, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT, new_password=None):
|
|
416
|
+
"""Effettua un accesso al database attivo sulla connessione corrente, identificando l'utente tramite i parametri *username* e *password*.
|
|
417
|
+
Se *success* è ``None``, la chiamata è bloccante e restituisce un ``dict`` con informazioni dettagliate sull'utente autenticato, oppure
|
|
418
|
+
viene lanciata l'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
419
|
+
Se *success* è una funzione nella forma ``success(info, userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
420
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con le informazioni sull'utente ed
|
|
421
|
+
il parametro *userdata*.
|
|
422
|
+
"""
|
|
423
|
+
return self._impl.authenticate(username, password, success, error, progress, userdata, timeout, new_password)
|
|
424
|
+
|
|
425
|
+
def full_text_search(self, text, limit, success=None, error=None, progress=None, userdata=None, timeout=DEFAULT_EXECUTE_TIMEOUT):
|
|
426
|
+
"""Esegue una ricerca full-text sul database attivo sulla connessione corrente, limitando la ricerca di *text* a *limit* risultati.
|
|
427
|
+
Se *success* è ``None``, la chiamata è bloccante e restituisce una lista di risultati, dove ogni risultato è ``dict`` con almeno le chiavi
|
|
428
|
+
*score*, *tablename*, *id* e *display*; in caso di errore viene lanciata l'eccezione :class:`~kongalib.Error`.
|
|
429
|
+
Se *success* è una funzione nella format ``success(hits, userdata)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
430
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con i risultati della ricerca ed
|
|
431
|
+
il parametro *userdata*.
|
|
432
|
+
"""
|
|
433
|
+
return self._impl.full_text_search(text, limit, success, error, progress, userdata, timeout)
|
|
434
|
+
|
|
435
|
+
def get_permissions(self, user_id):
|
|
436
|
+
output = self.execute(CMD_GET_PERMISSIONS, {
|
|
437
|
+
IN_USER_ID: user_id
|
|
438
|
+
})
|
|
439
|
+
if output[OUT_ERRNO] != OK:
|
|
440
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
441
|
+
return output[OUT_PERMISSIONS]
|
|
442
|
+
|
|
443
|
+
def set_permissions(self, user_id, permissions):
|
|
444
|
+
output = self.execute(CMD_SET_PERMISSIONS, {
|
|
445
|
+
IN_USER_ID: user_id,
|
|
446
|
+
IN_PERMISSIONS: permissions
|
|
447
|
+
})
|
|
448
|
+
if output[OUT_ERRNO] != OK:
|
|
449
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
450
|
+
|
|
451
|
+
def begin_transaction(self, pause_indexing=False, deferred=False):
|
|
452
|
+
"""Inizia una transazione sul database attivo nella connessione corrente. Se *pause_indexing* è ``True``, l'indicizzazione del
|
|
453
|
+
database è disabilitata sul server. In caso di errore viene lanciata l'eccezione :class:`~kongalib.Error`.
|
|
454
|
+
"""
|
|
455
|
+
flags = 0
|
|
456
|
+
if pause_indexing:
|
|
457
|
+
flags |= 0x1
|
|
458
|
+
if deferred:
|
|
459
|
+
flags |= 0x2
|
|
460
|
+
output = self.execute(CMD_BEGIN_TRANSACTION, {
|
|
461
|
+
IN_FLAGS: flags
|
|
462
|
+
})
|
|
463
|
+
if output[OUT_ERRNO] != OK:
|
|
464
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
465
|
+
|
|
466
|
+
def commit_transaction(self, resume_indexing=False):
|
|
467
|
+
"""Esegue una COMMIT della transazione sul database attivo nella connessione corrente. Se *resume_indexing* è ``True``, l'indicizzazione
|
|
468
|
+
del database è abilitata sul server. In caso di errore viene lanciata l'eccezione :class:`~kongalib.Error`.
|
|
469
|
+
"""
|
|
470
|
+
flags = 0
|
|
471
|
+
if resume_indexing:
|
|
472
|
+
flags |= 0x1
|
|
473
|
+
output = self.execute(CMD_COMMIT_TRANSACTION, {
|
|
474
|
+
IN_FLAGS: flags
|
|
475
|
+
})
|
|
476
|
+
if output[OUT_ERRNO] != OK:
|
|
477
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
478
|
+
|
|
479
|
+
def rollback_transaction(self, resume_indexing=False):
|
|
480
|
+
"""Esegue un ROLLBACK della transazione sul database attivo nella connessione corrente. Se *resume_indexing* è ``True``, l'indicizzazione
|
|
481
|
+
del database è abilitata sul server. In caso di errore viene lanciata l'eccezione :class:`~kongalib.Error`.
|
|
482
|
+
"""
|
|
483
|
+
flags = 0
|
|
484
|
+
if resume_indexing:
|
|
485
|
+
flags |= 0x1
|
|
486
|
+
output = self.execute(CMD_ROLLBACK_TRANSACTION, {
|
|
487
|
+
IN_FLAGS: flags
|
|
488
|
+
})
|
|
489
|
+
if output[OUT_ERRNO] != OK:
|
|
490
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
491
|
+
|
|
492
|
+
def lock_resource(self, command, row_id=None):
|
|
493
|
+
"""Tenta di eseguire il blocco della risorsa identificata da *command*. Se *row_id* è diverso da ``None``, è possibile eseguire il
|
|
494
|
+
blocco di una singola riga di una tabella del database.
|
|
495
|
+
Restituisce una tupla ``(result, owner_data)``, dove *owner_data* è un ``dict`` contenente informazioni sull'utente che detiene già il blocco
|
|
496
|
+
della risorsa in caso fosse già bloccata, oppure lancia un'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
497
|
+
"""
|
|
498
|
+
output = self.execute(CMD_LOCK, {
|
|
499
|
+
IN_COMMAND_NAME: command,
|
|
500
|
+
IN_ROW_ID: row_id
|
|
501
|
+
})
|
|
502
|
+
return output['ANSWER'], output['OWNER_DATA']
|
|
503
|
+
|
|
504
|
+
def unlock_resource(self, command, row_id=None):
|
|
505
|
+
"""Rilascia il blocco della risorsa identificata da *tablename* e *row_id*.
|
|
506
|
+
"""
|
|
507
|
+
output = self.execute(CMD_UNLOCK, {
|
|
508
|
+
IN_COMMAND_NAME: command,
|
|
509
|
+
IN_ROW_ID: row_id
|
|
510
|
+
})
|
|
511
|
+
return output['ANSWER']
|
|
512
|
+
|
|
513
|
+
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, success=None, error=None, progress=None):
|
|
514
|
+
"""Genera ed esegue una SELECT sul server per ottenere una lista di risultati, a partire dalla tabella *tablename*.
|
|
515
|
+
*fieldnamelist* è una lista di nomi dei campi da ottenere; se un campo fk_X di *tablename* è una foreign key, si può accedere ai
|
|
516
|
+
campi della tabella collegata Y specificando "fk_X.Campo_di_Y"; la JOIN corrispondente verrà generata e gestita automaticamente dal
|
|
517
|
+
server. Analogamente, si possono creare catene di JOIN implicite facendo riferimenti multipli di campi foreign key, per esempio
|
|
518
|
+
"fk_X.fk_Y.fk_Z.Campo_di_Z".
|
|
519
|
+
|
|
520
|
+
Se *where_expr* non è ``None``, può essere il corpo di una espressione WHERE SQL, e può contenere riferimenti nella stessa forma di
|
|
521
|
+
*fieldnamelist*, per esempio "(Campo_di_X = 1) AND (fk_X.Campo_di_Y > 5)".
|
|
522
|
+
|
|
523
|
+
*order_by* può essere un nome di campo per cui ordinare i risultati, dove *order_desc* specifica se ordinare in modo ascendente o discendente.
|
|
524
|
+
*offset* e *count* permettono di restituire risultati solo a partire dal numero *offset*, e limitandosi a *count* risultati.
|
|
525
|
+
|
|
526
|
+
Se *get_total* è ``True``, il valore di ritorno (o gli argomenti della callback *success*) sarà una tupla nella forma ``(result_set, total_rows, exist_results)``;
|
|
527
|
+
*total_rows* sarà il numero totale di righe come se *offset* e *limit* non fossero stati specificati, mentre *exist_results* sarà un
|
|
528
|
+
``dict`` le cui chiavi saranno gli ID specificati nel parametro *exist*, e i valori saranno ``True`` o ``False`` a seconda che il
|
|
529
|
+
corrispettivo ID sia presente nel database per la tabella *tablename* oppure no.
|
|
530
|
+
|
|
531
|
+
Se *success* è ``None`` la chiamata è bloccante e verrà restituito un risultato come descritto sopra, altrimenti verrà lanciata un'eccezione
|
|
532
|
+
:class:`~kongalib.Error` in caso di errore.
|
|
533
|
+
Se *success* è una funzione nella forma ``success(result_set)`` oppure ``success(result_set, total_rows, exist_results)`` (a seconda del
|
|
534
|
+
parametro *get_total* come descritto sopra), la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred` e l'operazione
|
|
535
|
+
viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito.
|
|
536
|
+
"""
|
|
537
|
+
if isinstance(fieldnamelist, str):
|
|
538
|
+
fieldnamelist = [ fieldnamelist ]
|
|
539
|
+
elif fieldnamelist:
|
|
540
|
+
fieldnamelist = list(fieldnamelist)
|
|
541
|
+
if success is not None:
|
|
542
|
+
def callback(output, dummy):
|
|
543
|
+
if output[OUT_ERRNO] == OK:
|
|
544
|
+
if get_total:
|
|
545
|
+
success(output[OUT_RESULT_SET], output[OUT_TOTAL_ROWS], output[OUT_EXIST])
|
|
546
|
+
else:
|
|
547
|
+
success(output[OUT_RESULT_SET])
|
|
548
|
+
elif error is not None:
|
|
549
|
+
error(ErrorList.from_error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
550
|
+
def errback(errno, errstr, dummy):
|
|
551
|
+
if error is not None:
|
|
552
|
+
error(ErrorList.from_error(errno, errstr))
|
|
553
|
+
return self.execute(CMD_SELECT, {
|
|
554
|
+
IN_TABLE_NAME: tablename,
|
|
555
|
+
IN_COLUMN_NAMES: fieldnamelist,
|
|
556
|
+
IN_WHERE_CLAUSE: where(where_expr),
|
|
557
|
+
IN_ORDER_BY: order_by,
|
|
558
|
+
IN_ORDER_DESC: order_desc,
|
|
559
|
+
IN_OFFSET: offset,
|
|
560
|
+
IN_ROW_COUNT: count,
|
|
561
|
+
IN_GET_TOTAL_ROWS: get_total,
|
|
562
|
+
IN_GET_ROWS_EXIST: exist,
|
|
563
|
+
}, success=callback, error=errback, progress=progress)
|
|
564
|
+
else:
|
|
565
|
+
output = self.execute(CMD_SELECT, {
|
|
566
|
+
IN_TABLE_NAME: tablename,
|
|
567
|
+
IN_COLUMN_NAMES: fieldnamelist,
|
|
568
|
+
IN_WHERE_CLAUSE: where(where_expr),
|
|
569
|
+
IN_ORDER_BY: order_by,
|
|
570
|
+
IN_ORDER_DESC: order_desc,
|
|
571
|
+
IN_OFFSET: offset,
|
|
572
|
+
IN_ROW_COUNT: count,
|
|
573
|
+
IN_GET_TOTAL_ROWS: get_total,
|
|
574
|
+
IN_GET_ROWS_EXIST: exist,
|
|
575
|
+
})
|
|
576
|
+
if output[OUT_ERRNO] == OK:
|
|
577
|
+
if get_total:
|
|
578
|
+
return output[OUT_RESULT_SET], output[OUT_TOTAL_ROWS], output[OUT_EXIST]
|
|
579
|
+
else:
|
|
580
|
+
return output[OUT_RESULT_SET]
|
|
581
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
582
|
+
|
|
583
|
+
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, success=None, error=None, progress=None):
|
|
584
|
+
"""Esattamente come :meth:`.select_data`, ma restituisce il *result_set* come una lista di ``dict``, anzichè una lista di liste."""
|
|
585
|
+
if isinstance(fieldnamelist, str):
|
|
586
|
+
fieldnamelist = [ fieldnamelist ]
|
|
587
|
+
elif fieldnamelist:
|
|
588
|
+
fieldnamelist = list(fieldnamelist)
|
|
589
|
+
def get_result_set(output):
|
|
590
|
+
names = output.get(OUT_COLUMN_NAMES, None) or fieldnamelist
|
|
591
|
+
return [dict(list(zip(names, row))) for row in output[OUT_RESULT_SET] ]
|
|
592
|
+
if success is not None:
|
|
593
|
+
def callback(output, dummy):
|
|
594
|
+
if output[OUT_ERRNO] == OK:
|
|
595
|
+
if get_total:
|
|
596
|
+
success(get_result_set(output), output[OUT_TOTAL_ROWS], output[OUT_EXIST])
|
|
597
|
+
else:
|
|
598
|
+
success(get_result_set(output))
|
|
599
|
+
elif error is not None:
|
|
600
|
+
error(ErrorList.from_error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
601
|
+
def errback(errno, errstr, dummy):
|
|
602
|
+
if error is not None:
|
|
603
|
+
error(ErrorList.from_error(errno, errstr))
|
|
604
|
+
return self.execute(CMD_SELECT, {
|
|
605
|
+
IN_TABLE_NAME: tablename,
|
|
606
|
+
IN_COLUMN_NAMES: fieldnamelist,
|
|
607
|
+
IN_WHERE_CLAUSE: where(where_expr),
|
|
608
|
+
IN_ORDER_BY: order_by,
|
|
609
|
+
IN_ORDER_DESC: order_desc,
|
|
610
|
+
IN_OFFSET: offset,
|
|
611
|
+
IN_ROW_COUNT: count,
|
|
612
|
+
IN_GET_TOTAL_ROWS: get_total,
|
|
613
|
+
}, success=callback, error=errback, progress=progress)
|
|
614
|
+
else:
|
|
615
|
+
output = self.execute(CMD_SELECT, {
|
|
616
|
+
IN_TABLE_NAME: tablename,
|
|
617
|
+
IN_COLUMN_NAMES: fieldnamelist,
|
|
618
|
+
IN_WHERE_CLAUSE: where(where_expr),
|
|
619
|
+
IN_ORDER_BY: order_by,
|
|
620
|
+
IN_ORDER_DESC: order_desc,
|
|
621
|
+
IN_OFFSET: offset,
|
|
622
|
+
IN_ROW_COUNT: count,
|
|
623
|
+
IN_GET_TOTAL_ROWS: get_total,
|
|
624
|
+
})
|
|
625
|
+
if output[OUT_ERRNO] == OK:
|
|
626
|
+
if get_total:
|
|
627
|
+
return get_result_set(output), output[OUT_TOTAL_ROWS], output[OUT_EXIST]
|
|
628
|
+
else:
|
|
629
|
+
return get_result_set(output)
|
|
630
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
631
|
+
|
|
632
|
+
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, success=None, error=None, progress=None):
|
|
633
|
+
"""Ottiene il record completo della tabella *tablename*, sotto forma di ``dict``. Il record può essere identificato in due modi: o
|
|
634
|
+
tramite il solo *id*, oppure tramite la specifica dei parametri *code*, *code_azienda* e *num_esercizio*.
|
|
635
|
+
Se *success* è ``None`` la chiamata è bloccante, altrimenti verrà lanciata un'eccezione :class:`~kongalib.Error` in caso di errore.
|
|
636
|
+
Se *success* è una funzione nella forma ``success(data)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred` e
|
|
637
|
+
l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con i dati del record.
|
|
638
|
+
"""
|
|
639
|
+
if (id is None) and (code is None):
|
|
640
|
+
raise ValueError('Either code or id must be specified')
|
|
641
|
+
if success is not None:
|
|
642
|
+
def callback(output):
|
|
643
|
+
data = output[OUT_DICT_DATA]
|
|
644
|
+
data['@checksum'] = output[OUT_CHECKSUM]
|
|
645
|
+
success(data)
|
|
646
|
+
callback, errback = make_callbacks(callback, error, None)
|
|
647
|
+
return self.execute(CMD_GET, {
|
|
648
|
+
IN_TABLE_NAME: tablename,
|
|
649
|
+
IN_ROW_ID: id,
|
|
650
|
+
IN_CODE: code,
|
|
651
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
652
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
653
|
+
IN_FLAGS: flags,
|
|
654
|
+
IN_COLUMN_NAMES: field_names,
|
|
655
|
+
IN_ROW_EXTRA_FIELDS: row_extra_field_names,
|
|
656
|
+
}, success=callback, error=errback, progress=progress)
|
|
657
|
+
else:
|
|
658
|
+
output = self.execute(CMD_GET, {
|
|
659
|
+
IN_TABLE_NAME: tablename,
|
|
660
|
+
IN_ROW_ID: id,
|
|
661
|
+
IN_CODE: code,
|
|
662
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
663
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
664
|
+
IN_FLAGS: flags,
|
|
665
|
+
IN_COLUMN_NAMES: field_names,
|
|
666
|
+
IN_ROW_EXTRA_FIELDS: row_extra_field_names,
|
|
667
|
+
})
|
|
668
|
+
def callback():
|
|
669
|
+
data = output[OUT_DICT_DATA]
|
|
670
|
+
data['@checksum'] = output[OUT_CHECKSUM]
|
|
671
|
+
return data
|
|
672
|
+
return _check_result(output, callback)
|
|
673
|
+
|
|
674
|
+
def insert_record(self, tablename, data, code_azienda=None, num_esercizio=None, log=None, success=None, error=None, progress=None):
|
|
675
|
+
"""Inserisce un nuovo record nella tabella *tablename*. Il nuovo record, i cui dati sono passati nel ``dict`` *data*, sarà un record
|
|
676
|
+
condiviso con tutte le aziende del database se *code_azienda* e *num_esercizio* sono ``None``, altrimenti apparterrà solo all'azienda e
|
|
677
|
+
all'esercizio specificati.
|
|
678
|
+
Se *success* è ``None`` la chiamata è bloccante e ritornerà una tupla nella forma ``(id, code)``, dove *id* è l'ID univoco assegnato
|
|
679
|
+
al record dal server, mentre *code* è il codice del record (che può essere diverso da quello passato in *data* se sono attivi i codici
|
|
680
|
+
automatici per *tablename*); in caso di errore verrà lanciata un'eccezione di classe :class:`~kongalib.Error` o :class:`~kongalib.ErrorList`.
|
|
681
|
+
Se *success* è una funzione nella forma ``success(id, code)``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred`
|
|
682
|
+
e l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito con l'ID ed il codice del nuovo
|
|
683
|
+
record. In modalità asincrona se *log* è un oggetto di classe :class:`OperationLog`, esso riceverà ogni eventuale messaggio di log
|
|
684
|
+
prodotto dal server durante l'inserimento.
|
|
685
|
+
"""
|
|
686
|
+
if success is not None:
|
|
687
|
+
callback, errback = make_callbacks(lambda output: success(output[OUT_ID], output[OUT_CODE]), error, log)
|
|
688
|
+
return self.execute(CMD_INSERT_FROM_DICT, {
|
|
689
|
+
IN_TABLE_NAME: tablename,
|
|
690
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
691
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
692
|
+
IN_DICT_DATA: data
|
|
693
|
+
}, success=callback, error=errback, progress=progress)
|
|
694
|
+
else:
|
|
695
|
+
output = self.execute(CMD_INSERT_FROM_DICT, {
|
|
696
|
+
IN_TABLE_NAME: tablename,
|
|
697
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
698
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
699
|
+
IN_DICT_DATA: data
|
|
700
|
+
})
|
|
701
|
+
return _check_result(output, lambda: (output[OUT_ID], output[OUT_CODE]))
|
|
702
|
+
|
|
703
|
+
def update_record(self, tablename, data, code=None, id=None, code_azienda=None, num_esercizio=None, log=None, success=None, error=None, progress=None):
|
|
704
|
+
"""Aggiorna un record esistente nella tabella *tablename*. Il record, i cui dati da aggiornare sono passati nel ``dict`` *data*, può
|
|
705
|
+
essere identificato in due modi: o tramite il solo *id*, oppure tramite la specifica dei parametri *code*, *code_azienda* e *num_esercizio*.
|
|
706
|
+
Se *success* è ``None`` la chiamata è bloccante, e in caso di errore verrà lanciata un'eccezione di classe :class:`~kongalib.Error` o
|
|
707
|
+
:class:`~kongalib.ErrorList`.
|
|
708
|
+
Se *success* è una funzione nella forma ``success()``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred` e
|
|
709
|
+
l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito. In modalità asincrona se *log* è un
|
|
710
|
+
oggetto di classe :class:`OperationLog`, esso riceverà ogni eventuale messaggio di log prodotto dal server durante l'aggiornamento.
|
|
711
|
+
"""
|
|
712
|
+
if success is not None:
|
|
713
|
+
callback, errback = make_callbacks(lambda output: success(), error, log)
|
|
714
|
+
return self.execute(CMD_UPDATE_FROM_DICT, {
|
|
715
|
+
IN_TABLE_NAME: tablename,
|
|
716
|
+
IN_ROW_ID: id,
|
|
717
|
+
IN_CODE: code,
|
|
718
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
719
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
720
|
+
IN_DICT_DATA: data
|
|
721
|
+
}, success=callback, error=errback, progress=progress)
|
|
722
|
+
else:
|
|
723
|
+
output = self.execute(CMD_UPDATE_FROM_DICT, {
|
|
724
|
+
IN_TABLE_NAME: tablename,
|
|
725
|
+
IN_ROW_ID: id,
|
|
726
|
+
IN_CODE: code,
|
|
727
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
728
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
729
|
+
IN_DICT_DATA: data
|
|
730
|
+
})
|
|
731
|
+
return _check_result(output)
|
|
732
|
+
|
|
733
|
+
def delete_record(self, tablename, code=None, id=None, code_azienda=None, num_esercizio=None, log=None, success=None, error=None, progress=None):
|
|
734
|
+
"""Cancella un record dalla tabella *tablename*. Il record può essere identificato in due modi: o tramite il solo *id*, oppure tramite
|
|
735
|
+
la specifica dei parametri *code*, *code_azienda* e *num_esercizio*.
|
|
736
|
+
Se *success* è ``None`` la chiamata è bloccante, e in caso di errore verrà lanciata un'eccezione di classe :class:`~kongalib.Error` o
|
|
737
|
+
:class:`~kongalib.ErrorList`.
|
|
738
|
+
Se *success* è una funzione nella forma ``success()``, la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred` e
|
|
739
|
+
l'operazione viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito. In modalità asincrona se *log* è un
|
|
740
|
+
oggetto di classe :class:`OperationLog`, esso riceverà ogni eventuale messaggio di log prodotto dal server durante la cancellazione.
|
|
741
|
+
"""
|
|
742
|
+
if success is not None:
|
|
743
|
+
callback, errback = make_callbacks(lambda output: success(), error, log)
|
|
744
|
+
return self.execute(CMD_DELETE_FROM_CODE, {
|
|
745
|
+
IN_TABLE_NAME: tablename,
|
|
746
|
+
IN_ROW_ID: id,
|
|
747
|
+
IN_CODE: code,
|
|
748
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
749
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
750
|
+
}, success=callback, error=errback, progress=progress)
|
|
751
|
+
else:
|
|
752
|
+
output = self.execute(CMD_DELETE_FROM_CODE, {
|
|
753
|
+
IN_TABLE_NAME: tablename,
|
|
754
|
+
IN_ROW_ID: id,
|
|
755
|
+
IN_CODE: code,
|
|
756
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
757
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
758
|
+
})
|
|
759
|
+
return _check_result(output)
|
|
760
|
+
|
|
761
|
+
def code_exists(self, tablename, code, code_azienda, num_esercizio, extra_where=None):
|
|
762
|
+
"""Controlla l'esistenza del codice *code* nella tabella *tablename* per l'azienda e l'esercizio specificati in *code_azienda* e *num_esercizio*."""
|
|
763
|
+
output = self.execute(CMD_CODE_EXISTS, {
|
|
764
|
+
IN_TABLE_NAME: tablename,
|
|
765
|
+
IN_CODE: code,
|
|
766
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
767
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
768
|
+
IN_EXTRA_WHERE: where(extra_where),
|
|
769
|
+
})
|
|
770
|
+
return output['EXISTS']
|
|
771
|
+
|
|
772
|
+
def get_next_available_code(self, tablename, code_azienda, num_esercizio, dry_run=False, force=False):
|
|
773
|
+
return self.execute(CMD_GET_NEXT_CODE, {
|
|
774
|
+
IN_TABLE_NAME: tablename,
|
|
775
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
776
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
777
|
+
IN_DRY_RUN: dry_run,
|
|
778
|
+
IN_FORCE: force,
|
|
779
|
+
})[OUT_CODE]
|
|
780
|
+
|
|
781
|
+
def get_last_npfe(self, code_azienda, num_esercizio):
|
|
782
|
+
return self.execute(CMD_GET_LAST_NPFE, {
|
|
783
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
784
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
785
|
+
})[OUT_NPFE]
|
|
786
|
+
|
|
787
|
+
def start_elab(self, command, params, code_azienda, num_esercizio, log=None, success=None, error=None, progress=None, tx=True):
|
|
788
|
+
if success is not None:
|
|
789
|
+
def callback(output, dummy):
|
|
790
|
+
if output[OUT_ERRNO] == OK:
|
|
791
|
+
answer = output[OUT_LOG]
|
|
792
|
+
if len(answer) > 0:
|
|
793
|
+
error_list = ErrorList(answer)
|
|
794
|
+
if log is None:
|
|
795
|
+
if error is not None:
|
|
796
|
+
error(error_list)
|
|
797
|
+
else:
|
|
798
|
+
error_list.prepare_log(log)
|
|
799
|
+
success(output[OUT_DATA])
|
|
800
|
+
else:
|
|
801
|
+
success(output[OUT_DATA])
|
|
802
|
+
elif error is not None:
|
|
803
|
+
error(ErrorList.from_error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
804
|
+
def errback(errno, errstr, dummy):
|
|
805
|
+
if error is not None:
|
|
806
|
+
error(ErrorList.from_error(errno, errstr))
|
|
807
|
+
|
|
808
|
+
return self.execute(CMD_START_ELAB, {
|
|
809
|
+
IN_COMMAND: command,
|
|
810
|
+
IN_PARAMS: params,
|
|
811
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
812
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
813
|
+
IN_TX: tx,
|
|
814
|
+
}, success=callback, error=errback, progress=progress)
|
|
815
|
+
else:
|
|
816
|
+
output = self.execute(CMD_START_ELAB, {
|
|
817
|
+
IN_COMMAND: command,
|
|
818
|
+
IN_PARAMS: params,
|
|
819
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
820
|
+
IN_NUM_ESERCIZIO: num_esercizio,
|
|
821
|
+
IN_TX: tx,
|
|
822
|
+
})
|
|
823
|
+
if output[OUT_ERRNO] == OK:
|
|
824
|
+
answer = output[OUT_LOG]
|
|
825
|
+
e = ErrorList(answer)
|
|
826
|
+
if (log is not None) and (len(answer) > 0):
|
|
827
|
+
e.prepare_log(log)
|
|
828
|
+
if e.errno != OK:
|
|
829
|
+
raise e
|
|
830
|
+
return output[OUT_DATA]
|
|
831
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
832
|
+
|
|
833
|
+
def list_binaries(self, field_or_tablename, id, type=None, success=None, error=None, progress=None, full=False):
|
|
834
|
+
"""Ottiene la lista dei dati binari associati ad una scheda del database, identificata da *field_or_tablename* (che può essere un nome
|
|
835
|
+
tabella o un campo da cui risolvere il nome tabella) e *id*. La funzione ritorna una lista di tuple, in cui la n-esima tupla ha la
|
|
836
|
+
forma ``( Tipo, NomeAllegato, NomeOriginale )``; *Tipo* è un intero ed è uno dei valori della *Choice* ``Resources``, *NomeAllegato* è
|
|
837
|
+
il nome assegnato internamente a Konga per identificare univocamente il contenuto binario, mentre *NomeOriginale* è il nome del file
|
|
838
|
+
originale da cui è stato caricato il contenuto. Se *type* è specificato, la funzione filtrerà i risultati in base ad esso, ritornando
|
|
839
|
+
solo le tuple con il *Tipo* corretto. Se *full* è ``True`` la n-esima tupla ritornata avrà tre valori in più corrispondenti all'etichetta
|
|
840
|
+
dell'immagine aggiuntiva (se specificata), al codice della tipologia dell'allegato e ai metadati associati (se presenti), e la tupla avrà
|
|
841
|
+
quindi la forma ``( Tipo, NomeAllegato, NomeOriginale, Etichetta, CodiceTipologia, Metadati )``.
|
|
842
|
+
Se *success* è diverso da ``None``, la callback verrà invocata in caso di successo con la lista di tuple di cui sopra.
|
|
843
|
+
"""
|
|
844
|
+
if success is not None:
|
|
845
|
+
def callback(output, dummy):
|
|
846
|
+
if output[OUT_ERRNO] == OK:
|
|
847
|
+
success([ tuple(row) for row in output[OUT_LIST] if ((type is None) or (row[0] == type)) ])
|
|
848
|
+
elif error is not None:
|
|
849
|
+
error(Error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
850
|
+
def errback(errno, errstr, dummy):
|
|
851
|
+
if error is not None:
|
|
852
|
+
error(Error(errno, errstr))
|
|
853
|
+
|
|
854
|
+
return self.execute(CMD_LIST_BINARIES, {
|
|
855
|
+
IN_FIELD_NAME: field_or_tablename,
|
|
856
|
+
IN_ROW_ID: id,
|
|
857
|
+
IN_FULL: full,
|
|
858
|
+
}, success=callback, error=errback, progress=progress)
|
|
859
|
+
else:
|
|
860
|
+
output = self.execute(CMD_LIST_BINARIES, {
|
|
861
|
+
IN_FIELD_NAME: field_or_tablename,
|
|
862
|
+
IN_ROW_ID: id,
|
|
863
|
+
IN_FULL: full,
|
|
864
|
+
})
|
|
865
|
+
if output[OUT_ERRNO] == OK:
|
|
866
|
+
return [ tuple(row) for row in output[OUT_LIST] if ((type is None) or (row[0] == type)) ]
|
|
867
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
868
|
+
|
|
869
|
+
def fetch_image(self, fieldname, id, type, success=None, error=None, progress=None, label=None):
|
|
870
|
+
"""Piccolo wrapper alla funzione :meth:`.fetch_binary`, dedicato alle immagini, con l'unica differenza che il valore di ritorno sarà
|
|
871
|
+
direttamente il contenuto binario dell'immagine in caso di successo (e questo sarà anche l'unico parametro passato alla callback
|
|
872
|
+
*success*)"""
|
|
873
|
+
if success is not None:
|
|
874
|
+
def callback(output, dummy):
|
|
875
|
+
if output[OUT_ERRNO] == OK:
|
|
876
|
+
success(output[OUT_DATA])
|
|
877
|
+
elif error is not None:
|
|
878
|
+
error(Error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
879
|
+
def errback(errno, errstr, dummy):
|
|
880
|
+
if error is not None:
|
|
881
|
+
error(Error(errno, errstr))
|
|
882
|
+
|
|
883
|
+
return self.execute(CMD_FETCH_BINARY, {
|
|
884
|
+
IN_FIELD_NAME: fieldname,
|
|
885
|
+
IN_ROW_ID: id,
|
|
886
|
+
IN_TYPE: type,
|
|
887
|
+
IN_LABEL: label,
|
|
888
|
+
}, success=callback, error=errback, progress=progress)
|
|
889
|
+
else:
|
|
890
|
+
output = self.execute(CMD_FETCH_BINARY, {
|
|
891
|
+
IN_FIELD_NAME: fieldname,
|
|
892
|
+
IN_ROW_ID: id,
|
|
893
|
+
IN_TYPE: type,
|
|
894
|
+
IN_LABEL: label,
|
|
895
|
+
})
|
|
896
|
+
if output[OUT_ERRNO] == OK:
|
|
897
|
+
return output[OUT_DATA]
|
|
898
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
899
|
+
|
|
900
|
+
def fetch_binary(self, field_or_tablename, id, type, filename=None, check_only=False, success=None, error=None, progress=None, label=None, with_extra=False):
|
|
901
|
+
"""Carica un contenuto binario dal server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
902
|
+
questa tabella unita a *id* identificano la scheda del database da cui caricare la risorsa; *type* è uno dei valori della *Choice*
|
|
903
|
+
``Resources``, mentre *filename* e *label* hanno senso solo per identificare rispettivamente le risorse di tipo documento ed immagine
|
|
904
|
+
aggiuntiva.
|
|
905
|
+
La funzione ritorna una tupla di quattro elementi: ( *dati*, *filename*, *original_filename*, *checksum* ). Questi quattro elementi
|
|
906
|
+
sono anche i parametri passati alla callback *success* in caso di successo. *dati* sono i dati binari che sono stati caricati dal
|
|
907
|
+
server; *filename* è il nome file interno con cui è identificata la risorsa, *original_filename* è il nome del file originale che è
|
|
908
|
+
stato specificato all'atto del salvataggio della risorsa sul server, mentre *checksum* è un checksum dei dati. Se *with_extra* è
|
|
909
|
+
``True``, la funzione ritorna sei elementi, e gli elementi aggiuntivi sono un ``dict`` con i metadata associati alla risorsa, o ``None``
|
|
910
|
+
se non ci sono metadati associati, e il codice della tipologia allegato se presente.
|
|
911
|
+
Se *check_only* è ``True``, i dati binari della risorsa non verranno effettivamente caricati dal dispositivo di archiviazione in cui
|
|
912
|
+
sono depositati, e *dati* sarà ``None``; questa modalità è utile per verificare l'esistenza di una risorsa e il suo checksum senza
|
|
913
|
+
effettivamente caricarla da remoto (nel caso di archiviazione su cloud il caricamento potrebbe essere lento)."""
|
|
914
|
+
if (type == 0) and (not filename):
|
|
915
|
+
raise ValueError('filename must be specified for document type resources')
|
|
916
|
+
if success is not None:
|
|
917
|
+
def callback(output, dummy):
|
|
918
|
+
if output[OUT_ERRNO] == OK:
|
|
919
|
+
if with_extra:
|
|
920
|
+
success(output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM], output[OUT_METADATA], output[OUT_CODE_TIPOLOGIA])
|
|
921
|
+
else:
|
|
922
|
+
success(output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM])
|
|
923
|
+
elif error is not None:
|
|
924
|
+
error(Error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
925
|
+
def errback(errno, errstr, dummy):
|
|
926
|
+
if error is not None:
|
|
927
|
+
error(Error(errno, errstr))
|
|
928
|
+
|
|
929
|
+
return self.execute(CMD_FETCH_BINARY, {
|
|
930
|
+
IN_FIELD_NAME: field_or_tablename,
|
|
931
|
+
IN_ROW_ID: id,
|
|
932
|
+
IN_TYPE: type,
|
|
933
|
+
IN_FILENAME: filename,
|
|
934
|
+
IN_LABEL: label,
|
|
935
|
+
IN_CHECK: check_only,
|
|
936
|
+
}, success=callback, error=errback, progress=progress)
|
|
937
|
+
else:
|
|
938
|
+
output = self.execute(CMD_FETCH_BINARY, {
|
|
939
|
+
IN_FIELD_NAME: field_or_tablename,
|
|
940
|
+
IN_ROW_ID: id,
|
|
941
|
+
IN_TYPE: type,
|
|
942
|
+
IN_FILENAME: filename,
|
|
943
|
+
IN_LABEL: label,
|
|
944
|
+
IN_CHECK: check_only,
|
|
945
|
+
})
|
|
946
|
+
if output[OUT_ERRNO] == OK:
|
|
947
|
+
if with_extra:
|
|
948
|
+
return output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM], output[OUT_METADATA], output[OUT_CODE_TIPOLOGIA]
|
|
949
|
+
else:
|
|
950
|
+
return output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM]
|
|
951
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
952
|
+
|
|
953
|
+
def store_binary(self, field_or_tablename, id, type, filename=None, original_filename=None, data=None, desc=None, force_delete=False, code_azienda=None, success=None, error=None, progress=None, label=None, metadata=None, code_tipologia=None, log=None):
|
|
954
|
+
"""Salva un contenuto binario sul server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
955
|
+
questa tabella unita a *id* identificano la scheda a cui abbinare la risorsa; *type* è uno dei valori della *Choice*``Resources``;
|
|
956
|
+
*filename* permette di specificare un nome file interno con cui identificare la risorsa (se ``None`` il server genererà un nome univoco
|
|
957
|
+
automaticamente); *original_filename* è il nome file originale i cui dati si stanno salvando sul server; *data* sono i dati binari
|
|
958
|
+
effettivi; *desc* è la descrizione da abbinare alla risorsa; *code_azienda* infine identifica l'azienda su cui si sta operando, mentre
|
|
959
|
+
*code_tipologia* permette di specificare una tipologia da abbinare al dati. Per le risorse di tipo immagine aggiuntiva è necessario
|
|
960
|
+
specificare una *label* da abbinare all'immagine per identificarla univocamente. *metadata* può essere un ``dict`` in cui sia chiavi che
|
|
961
|
+
valori siano delle semplici stringhe, e permette di specificare dei metadati aggiuntivi associati alla risorsa binaria che si sta inserendo.
|
|
962
|
+
La funzione ritorna il nome del file interno usato dal server per identificare la risorsa, che come detto sopra è uguale a *filename* se
|
|
963
|
+
quest'ultimo è diverso da ``None``, altrimenti verrà ritornato il nome file generato dal server. La callback *success* se specificata
|
|
964
|
+
riceverà *filename* come unico parametro.
|
|
965
|
+
Se *data* è ``None``, la funzione cancella i dati binari associati alla scheda; *force_delete* in questo caso può essere ``True`` se
|
|
966
|
+
si desidera cancellare il riferimento ai dati anche se i dati non sono raggiungibili dal server."""
|
|
967
|
+
if success is not None:
|
|
968
|
+
def callback(output, dummy):
|
|
969
|
+
if output[OUT_ERRNO] == OK:
|
|
970
|
+
if log is not None:
|
|
971
|
+
ErrorList(output[OUT_LOG] or []).prepare_log(log)
|
|
972
|
+
success(output[OUT_FILENAME])
|
|
973
|
+
elif error is not None:
|
|
974
|
+
error(Error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
975
|
+
def errback(errno, errstr, dummy):
|
|
976
|
+
if error is not None:
|
|
977
|
+
error(Error(errno, errstr))
|
|
978
|
+
|
|
979
|
+
return self.execute(CMD_STORE_BINARY, {
|
|
980
|
+
IN_FIELD_NAME: field_or_tablename,
|
|
981
|
+
IN_ROW_ID: id,
|
|
982
|
+
IN_TYPE: type,
|
|
983
|
+
IN_FILENAME: filename,
|
|
984
|
+
IN_ORIGINAL_FILENAME: original_filename,
|
|
985
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
986
|
+
IN_DATA: data,
|
|
987
|
+
IN_DESC: desc,
|
|
988
|
+
IN_FORCE_DELETE: force_delete,
|
|
989
|
+
IN_LABEL: label,
|
|
990
|
+
IN_METADATA: metadata,
|
|
991
|
+
IN_CODE_TIPOLOGIA: code_tipologia,
|
|
992
|
+
}, success=callback, error=errback, progress=progress)
|
|
993
|
+
else:
|
|
994
|
+
output = self.execute(CMD_STORE_BINARY, {
|
|
995
|
+
IN_FIELD_NAME: field_or_tablename,
|
|
996
|
+
IN_ROW_ID: id,
|
|
997
|
+
IN_TYPE: type,
|
|
998
|
+
IN_FILENAME: filename,
|
|
999
|
+
IN_ORIGINAL_FILENAME: original_filename,
|
|
1000
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
1001
|
+
IN_DATA: data,
|
|
1002
|
+
IN_DESC: desc,
|
|
1003
|
+
IN_FORCE_DELETE: force_delete,
|
|
1004
|
+
IN_LABEL: label,
|
|
1005
|
+
IN_METADATA: metadata,
|
|
1006
|
+
IN_CODE_TIPOLOGIA: code_tipologia,
|
|
1007
|
+
})
|
|
1008
|
+
if output[OUT_ERRNO] == OK:
|
|
1009
|
+
if log is not None:
|
|
1010
|
+
ErrorList(output[OUT_LOG] or []).prepare_log(log)
|
|
1011
|
+
return output[OUT_FILENAME]
|
|
1012
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
1013
|
+
|
|
1014
|
+
def translate(self, field, value, language, code_azienda=None):
|
|
1015
|
+
output = self.execute(CMD_TRANSLATE, {
|
|
1016
|
+
IN_FIELD: field,
|
|
1017
|
+
IN_VALUE: value,
|
|
1018
|
+
IN_LANGUAGE: language,
|
|
1019
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
1020
|
+
})
|
|
1021
|
+
if output[OUT_ERRNO] != OK:
|
|
1022
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
1023
|
+
return output[OUT_TEXT]
|
|
1024
|
+
|
|
1025
|
+
def set_database_language(self, language, success=None, error=None, progress=None):
|
|
1026
|
+
if success is not None:
|
|
1027
|
+
def callback(output, dummy):
|
|
1028
|
+
if output[OUT_ERRNO] == OK:
|
|
1029
|
+
success()
|
|
1030
|
+
elif error is not None:
|
|
1031
|
+
error(Error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
1032
|
+
def errback(errno, errstr, dummy):
|
|
1033
|
+
if error is not None:
|
|
1034
|
+
error(Error(errno, errstr))
|
|
1035
|
+
self.execute(CMD_SET_DATABASE_LANGUAGE, {
|
|
1036
|
+
IN_LANGUAGE: language,
|
|
1037
|
+
}, success=callback, error=errback, progress=progress)
|
|
1038
|
+
else:
|
|
1039
|
+
output = self.execute(CMD_SET_DATABASE_LANGUAGE, {
|
|
1040
|
+
IN_LANGUAGE: language,
|
|
1041
|
+
})
|
|
1042
|
+
if output[OUT_ERRNO] != OK:
|
|
1043
|
+
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
1044
|
+
|
|
1045
|
+
|