kongalib 2.0.4__cp314-cp314t-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/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
+