kongalib 1.12.0__cp310-cp310-win_amd64.whl → 2.0.0.post1__cp310-cp310-win_amd64.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.cp310-win_amd64.pdb +0 -0
- _kongalib.cp310-win_amd64.pyd +0 -0
- kongalib/__init__.py +22 -36
- kongalib/async_client.py +63 -65
- kongalib/client.py +51 -20
- kongalib/constants.py +110 -1842
- kongalib/data_dictionary.py +3 -12
- kongalib/db.py +7 -8
- kongalib/expression.py +29 -24
- kongalib/json.py +1 -2
- kongalib/scripting.py +54 -31
- {kongalib-1.12.0.dist-info → kongalib-2.0.0.post1.dist-info}/METADATA +29 -26
- kongalib-2.0.0.post1.dist-info/RECORD +21 -0
- {kongalib-1.12.0.dist-info → kongalib-2.0.0.post1.dist-info}/WHEEL +1 -1
- {kongalib-1.12.0.dist-info → kongalib-2.0.0.post1.dist-info}/top_level.txt +1 -0
- kongaui.py +4 -55
- kongautil.py +17 -13
- kongalib/_kongalib.cp310-win_amd64.pdb +0 -0
- kongalib/_kongalib.cp310-win_amd64.pyd +0 -0
- kongalib/compat.py +0 -492
- kongalib/parsetab.py +0 -41
- kongalib-1.12.0.dist-info/RECORD +0 -23
- {kongalib-1.12.0.dist-info → kongalib-2.0.0.post1.dist-info/licenses}/LICENSE +0 -0
- {kongalib-1.12.0.dist-info → kongalib-2.0.0.post1.dist-info}/zip-safe +0 -0
|
Binary file
|
|
Binary file
|
kongalib/__init__.py
CHANGED
|
@@ -20,8 +20,6 @@ import sys
|
|
|
20
20
|
import traceback
|
|
21
21
|
import atexit
|
|
22
22
|
|
|
23
|
-
from .compat import *
|
|
24
|
-
|
|
25
23
|
DEFAULT_DISCOVER_TIMEOUT = 5000
|
|
26
24
|
DEFAULT_CONNECT_TIMEOUT = 30000
|
|
27
25
|
DEFAULT_EXECUTE_TIMEOUT = 10000
|
|
@@ -128,6 +126,10 @@ class Log(object):
|
|
|
128
126
|
return self.messages
|
|
129
127
|
return [ msg for msg in self.messages if msg[0] == type ]
|
|
130
128
|
|
|
129
|
+
def add_messages(self, messages):
|
|
130
|
+
for msg in messages:
|
|
131
|
+
self.add_message(*msg)
|
|
132
|
+
|
|
131
133
|
def get_exception(self, klass=RuntimeError):
|
|
132
134
|
error = self.get_messages(Log.ERROR)[0][1]
|
|
133
135
|
return klass(error)
|
|
@@ -138,10 +140,7 @@ class Log(object):
|
|
|
138
140
|
|
|
139
141
|
def strip_html(self, html):
|
|
140
142
|
"""Elimina tutto il codice HTML dalla stringa in *html*, lasciando solo le parti testuali."""
|
|
141
|
-
|
|
142
|
-
from HTMLParser import HTMLParser
|
|
143
|
-
except:
|
|
144
|
-
from html.parser import HTMLParser
|
|
143
|
+
from html.parser import HTMLParser
|
|
145
144
|
|
|
146
145
|
class Stripper(HTMLParser):
|
|
147
146
|
def __init__(self):
|
|
@@ -203,14 +202,9 @@ class Error(Exception):
|
|
|
203
202
|
def __init__(self, errno, msg):
|
|
204
203
|
self.errno = errno
|
|
205
204
|
self.error = self.msg = msg
|
|
206
|
-
def
|
|
205
|
+
def __str__(self):
|
|
207
206
|
msg = self.msg and ('%s' % self.msg) or '(internal error)'
|
|
208
207
|
return ensure_text(msg)
|
|
209
|
-
def __str__(self):
|
|
210
|
-
if PY3:
|
|
211
|
-
return self.__unicode__()
|
|
212
|
-
else:
|
|
213
|
-
return self.__unicode__().encode('utf-8')
|
|
214
208
|
def __repr__(self):
|
|
215
209
|
return '<Error %d: %s>' % (self.errno, str(self))
|
|
216
210
|
|
|
@@ -246,14 +240,8 @@ class ErrorList(Error):
|
|
|
246
240
|
self.error = error
|
|
247
241
|
break
|
|
248
242
|
|
|
249
|
-
def __unicode__(self):
|
|
250
|
-
return u'\n'.join([ ensure_text(e) for e in self.get_errors() ])
|
|
251
|
-
|
|
252
243
|
def __str__(self):
|
|
253
|
-
|
|
254
|
-
return self.__unicode__()
|
|
255
|
-
else:
|
|
256
|
-
return self.__unicode__().encode('utf-8')
|
|
244
|
+
return u'\n'.join([ ensure_text(e) for e in self.get_errors() ])
|
|
257
245
|
|
|
258
246
|
def __repr__(self):
|
|
259
247
|
return '<ErrorList: %s>' % repr(self.get_errors())
|
|
@@ -319,26 +307,30 @@ class ErrorList(Error):
|
|
|
319
307
|
cls.PREPARE_CALLBACK = callback
|
|
320
308
|
|
|
321
309
|
|
|
310
|
+
|
|
322
311
|
class JSONError(Exception):
|
|
323
312
|
def __init__(self, msg):
|
|
324
313
|
self.msg = msg
|
|
325
|
-
def __unicode__(self):
|
|
326
|
-
return ensure_text(self.msg)
|
|
327
314
|
def __str__(self):
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
315
|
+
return ensure_text(self.msg)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def ensure_text(text, error='replace'):
|
|
320
|
+
if isinstance(text, bytes):
|
|
321
|
+
text = str(text, 'utf-8', 'replace')
|
|
322
|
+
elif not isinstance(text, str):
|
|
323
|
+
text = str(text)
|
|
324
|
+
return text
|
|
325
|
+
|
|
332
326
|
|
|
333
327
|
|
|
334
|
-
from
|
|
328
|
+
from _kongalib import Decimal, Deferred, JSONEncoder, JSONDecoder, NamedSemaphore, start_timer, hash_password, host_lookup, get_network_interfaces, get_machine_uuid, get_system_info, _cleanup, lock, unlock, set_default_idle_callback, set_power_callbacks, checksum, _apply_stylesheet, regexp_find_all, _check_all
|
|
335
329
|
from .constants import *
|
|
336
330
|
from .client import *
|
|
337
331
|
from .expression import *
|
|
338
332
|
from .data_dictionary import *
|
|
339
|
-
|
|
340
|
-
if sys.version_info >= (3, 6):
|
|
341
|
-
from .async_client import AsyncClient
|
|
333
|
+
from .async_client import AsyncClient
|
|
342
334
|
|
|
343
335
|
|
|
344
336
|
class ErrorMessage(object):
|
|
@@ -351,14 +343,8 @@ class ErrorMessage(object):
|
|
|
351
343
|
else:
|
|
352
344
|
self.traceback = ''.join(traceback.format_exception(*exc))
|
|
353
345
|
|
|
354
|
-
def __unicode__(self):
|
|
355
|
-
return ensure_text(self.error if self.traceback is None else self.traceback)
|
|
356
|
-
|
|
357
346
|
def __str__(self):
|
|
358
|
-
if
|
|
359
|
-
return self.__unicode__()
|
|
360
|
-
else:
|
|
361
|
-
return self.__unicode__().encode('utf-8')
|
|
347
|
+
return ensure_text(self.error if self.traceback is None else self.traceback)
|
|
362
348
|
|
|
363
349
|
|
|
364
350
|
def _on_destroy_thread():
|
kongalib/async_client.py
CHANGED
|
@@ -16,11 +16,15 @@
|
|
|
16
16
|
from __future__ import absolute_import
|
|
17
17
|
|
|
18
18
|
import asyncio
|
|
19
|
+
import nest_asyncio
|
|
19
20
|
import inspect
|
|
20
21
|
|
|
21
22
|
from kongalib import *
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
nest_asyncio.apply()
|
|
26
|
+
|
|
27
|
+
|
|
24
28
|
class AsyncClient(Client):
|
|
25
29
|
"""La classe AsyncClient, analogamente alla classe :class:`~kongalib.Client`, permette di connettersi ad un server Konga e di eseguire
|
|
26
30
|
comandi sullo stesso; la differenza è nel fatto che questa classe è costruita esplicitamente per lavorare con *asyncio* di Python, ed
|
|
@@ -50,7 +54,7 @@ class AsyncClient(Client):
|
|
|
50
54
|
future.set_exception(e)
|
|
51
55
|
|
|
52
56
|
def _make_progress(self, future, progress, userdata):
|
|
53
|
-
def callback(ptype, completeness, state,
|
|
57
|
+
def callback(ptype, completeness, state, dummy):
|
|
54
58
|
loop = future.get_loop()
|
|
55
59
|
try:
|
|
56
60
|
if future.cancelled() or (progress is None):
|
|
@@ -73,6 +77,8 @@ class AsyncClient(Client):
|
|
|
73
77
|
def _make_error(self, future):
|
|
74
78
|
def error(errno, *args):
|
|
75
79
|
loop = future.get_loop()
|
|
80
|
+
if isinstance(errno, int) and (errno in (ABORTED, EXECUTE_ABORTED)):
|
|
81
|
+
errno = Error(errno, '')
|
|
76
82
|
if isinstance(errno, Error):
|
|
77
83
|
if errno.errno in (ABORTED, EXECUTE_ABORTED):
|
|
78
84
|
loop.call_soon_threadsafe(future.cancel)
|
|
@@ -105,7 +111,7 @@ class AsyncClient(Client):
|
|
|
105
111
|
answer = output[OUT_LOG] or []
|
|
106
112
|
error_list = ErrorList(answer)
|
|
107
113
|
if output[OUT_ERRNO] == OK:
|
|
108
|
-
if
|
|
114
|
+
if error_list.errno != OK:
|
|
109
115
|
if log is None:
|
|
110
116
|
loop.call_soon_threadsafe(self._safe_set_exception, future, error_list)
|
|
111
117
|
else:
|
|
@@ -113,13 +119,21 @@ class AsyncClient(Client):
|
|
|
113
119
|
if log.has_errors():
|
|
114
120
|
loop.call_soon_threadsafe(self._safe_set_exception, future, error_list)
|
|
115
121
|
else:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
try:
|
|
123
|
+
if finalize_output is not None:
|
|
124
|
+
output = finalize_output(output)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, e)
|
|
127
|
+
else:
|
|
128
|
+
loop.call_soon_threadsafe(self._safe_set_result, future, output)
|
|
119
129
|
else:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
130
|
+
try:
|
|
131
|
+
if finalize_output is not None:
|
|
132
|
+
output = finalize_output(output)
|
|
133
|
+
except Exception as e:
|
|
134
|
+
loop.call_soon_threadsafe(self._safe_set_exception, future, e)
|
|
135
|
+
else:
|
|
136
|
+
loop.call_soon_threadsafe(self._safe_set_result, future, output)
|
|
123
137
|
else:
|
|
124
138
|
loop.call_soon_threadsafe(self._safe_set_exception, future, ErrorList.from_error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
125
139
|
return success
|
|
@@ -179,10 +193,10 @@ class AsyncClient(Client):
|
|
|
179
193
|
"""
|
|
180
194
|
if (server is None) and (host is None):
|
|
181
195
|
raise ValueError("either 'host' or 'server' parameter must be specified")
|
|
182
|
-
if isinstance(server,
|
|
196
|
+
if isinstance(server, str) and (host is None):
|
|
183
197
|
host = server
|
|
184
198
|
server = None
|
|
185
|
-
if isinstance(host,
|
|
199
|
+
if isinstance(host, str) and (port is None) and (':' in host):
|
|
186
200
|
pos = host.rfind(':')
|
|
187
201
|
host = host[:pos]
|
|
188
202
|
try:
|
|
@@ -455,7 +469,7 @@ class AsyncClient(Client):
|
|
|
455
469
|
def get_permissions(self, user_id):
|
|
456
470
|
return self._execute(CMD_GET_PERMISSIONS, {
|
|
457
471
|
IN_USER_ID: user_id
|
|
458
|
-
})
|
|
472
|
+
}, OUT_PERMISSIONS)
|
|
459
473
|
|
|
460
474
|
def set_permissions(self, user_id, permissions):
|
|
461
475
|
return self._execute(CMD_SET_PERMISSIONS, {
|
|
@@ -547,7 +561,7 @@ class AsyncClient(Client):
|
|
|
547
561
|
Se *get_total* è ``False``, il risultato sarà il solo *result_set*, ossia una lista di righe risultato della query, dove ogni riga è una
|
|
548
562
|
lista di valori.
|
|
549
563
|
"""
|
|
550
|
-
if isinstance(fieldnamelist,
|
|
564
|
+
if isinstance(fieldnamelist, str):
|
|
551
565
|
fieldnamelist = [ fieldnamelist ]
|
|
552
566
|
elif fieldnamelist:
|
|
553
567
|
fieldnamelist = list(fieldnamelist)
|
|
@@ -566,7 +580,7 @@ class AsyncClient(Client):
|
|
|
566
580
|
def select_data_as_dict(self, tablename, fieldnamelist=None, where_expr=None, order_by=None, order_desc=False, offset=0, count=None, get_total=False, progress=None):
|
|
567
581
|
"""Esattamente come :meth:`.select_data`, ma l'oggetto ``asyncio.Future`` restituito una volta completato ritornerà un *result_set* sotto
|
|
568
582
|
forma di lista di ``dict``, anzichè una lista di liste."""
|
|
569
|
-
if isinstance(fieldnamelist,
|
|
583
|
+
if isinstance(fieldnamelist, str):
|
|
570
584
|
fieldnamelist = [ fieldnamelist ]
|
|
571
585
|
elif fieldnamelist:
|
|
572
586
|
fieldnamelist = list(fieldnamelist)
|
|
@@ -672,12 +686,13 @@ class AsyncClient(Client):
|
|
|
672
686
|
IN_EXTRA_WHERE: where(extra_where),
|
|
673
687
|
}, OUT_EXISTS)
|
|
674
688
|
|
|
675
|
-
def get_next_available_code(self, tablename, code_azienda, num_esercizio, dry_run=False):
|
|
689
|
+
def get_next_available_code(self, tablename, code_azienda, num_esercizio, dry_run=False, force=False):
|
|
676
690
|
return self._execute(CMD_GET_NEXT_CODE, {
|
|
677
691
|
IN_TABLE_NAME: tablename,
|
|
678
692
|
IN_CODE_AZIENDA: code_azienda,
|
|
679
693
|
IN_NUM_ESERCIZIO: num_esercizio,
|
|
680
694
|
IN_DRY_RUN: dry_run,
|
|
695
|
+
IN_FORCE: force,
|
|
681
696
|
}, OUT_CODE)
|
|
682
697
|
|
|
683
698
|
def get_last_npfe(self, code_azienda, num_esercizio):
|
|
@@ -693,24 +708,26 @@ class AsyncClient(Client):
|
|
|
693
708
|
IN_CODE_AZIENDA: code_azienda,
|
|
694
709
|
IN_NUM_ESERCIZIO: num_esercizio,
|
|
695
710
|
IN_TX: tx,
|
|
696
|
-
}, OUT_DATA, progress=progress)
|
|
711
|
+
}, OUT_DATA, progress=progress, log=log)
|
|
697
712
|
|
|
698
|
-
def list_binaries(self, field_or_tablename, id, type=None, progress=None):
|
|
713
|
+
def list_binaries(self, field_or_tablename, id, type=None, progress=None, full=False):
|
|
699
714
|
"""Ottiene la lista dei dati binari associati ad una scheda del database, identificata da *field_or_tablename* (che può essere un nome
|
|
700
715
|
tabella o un campo da cui risolvere il nome tabella) e *id*. La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato
|
|
701
716
|
una volta completato sarà una lista di tuple, in cui la n-esima tupla ha la forma ``( Tipo, NomeAllegato, NomeOriginale )``; *Tipo*
|
|
702
717
|
è un intero ed è uno dei valori della *Choice* ``Resources``, *NomeAllegato* è il nome assegnato internamente a Konga per identificare
|
|
703
718
|
univocamente il contenuto binario, mentre *NomeOriginale* è il nome del file originale da cui è stato caricato il contenuto. Se *type*
|
|
704
|
-
è specificato, la funzione filtrerà i risultati in baso ad esso, ritornando solo le tuple con il *Tipo* corretto.
|
|
719
|
+
è specificato, la funzione filtrerà i risultati in baso ad esso, ritornando solo le tuple con il *Tipo* corretto. Se *full* è ``True``
|
|
720
|
+
la n-esima tupla ritornata avrà un valore in più corrispondente all'etichetta dell'immagine aggiuntiva se specificata.
|
|
705
721
|
"""
|
|
706
722
|
def get_result(output):
|
|
707
723
|
return [ tuple(row) for row in output[OUT_LIST] if ((type is None) or (row[0] == type)) ]
|
|
708
724
|
return self._execute(CMD_LIST_BINARIES, {
|
|
709
725
|
IN_FIELD_NAME: field_or_tablename,
|
|
710
726
|
IN_ROW_ID: id,
|
|
727
|
+
IN_FULL: full,
|
|
711
728
|
}, get_result, progress=progress)
|
|
712
729
|
|
|
713
|
-
def fetch_image(self, fieldname, id, type, progress=None):
|
|
730
|
+
def fetch_image(self, fieldname, id, type, progress=None, label=None):
|
|
714
731
|
"""Piccolo wrapper alla funzione :meth:`.fetch_binary`, dedicato alle immagini, con l'unica differenza che l'oggetto ``asyncio.Future``
|
|
715
732
|
restituito una volta completato avrà come valore di ritorno direttamente il contenuto binario dell'immagine.
|
|
716
733
|
"""
|
|
@@ -718,35 +735,47 @@ class AsyncClient(Client):
|
|
|
718
735
|
IN_FIELD_NAME: fieldname,
|
|
719
736
|
IN_ROW_ID: id,
|
|
720
737
|
IN_TYPE: type,
|
|
738
|
+
IN_LABEL: label,
|
|
721
739
|
}, OUT_DATA, progress=progress)
|
|
722
740
|
|
|
723
|
-
def fetch_binary(self, field_or_tablename, id, type, filename=None, check_only=False, progress=None):
|
|
741
|
+
def fetch_binary(self, field_or_tablename, id, type, filename=None, check_only=False, progress=None, label=None, with_extra=False):
|
|
724
742
|
"""Carica un contenuto binario dal server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
725
743
|
questa tabella unita a *id* identificano la scheda del database da cui caricare la risorsa; *type* è uno dei valori della *Choice*
|
|
726
|
-
``Resources``, mentre *filename*
|
|
744
|
+
``Resources``, mentre *filename* e *label* hanno senso solo per identificare rispettivamente le risorse di tipo documento ed immagine
|
|
745
|
+
aggiuntiva.
|
|
727
746
|
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà una tupla di quattro elementi:
|
|
728
747
|
( *dati*, *filename*, *original_filename*, *checksum* ). *dati* sono i dati binari che sono stati caricati dal server; *filename* è
|
|
729
748
|
il nome file interno con cui è identificata la risorsa, *original_filename* è il nome del file originale che è stato specificato
|
|
730
|
-
all'atto del salvataggio della risorsa sul server, mentre *checksum* è un checksum dei dati. Se *
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
749
|
+
all'atto del salvataggio della risorsa sul server, mentre *checksum* è un checksum dei dati. Se *with_extra* è ``True``, la funzione
|
|
750
|
+
ritorna sei elementi, e gli elementi aggiuntivi sono un ``dict`` con i metadata associati alla risorsa, o ``None`` se non ci sono
|
|
751
|
+
metadati associati, e il codice della tipologia allegato se presente. Se *check_only* è ``True``, i dati binari della risorsa non
|
|
752
|
+
verranno effettivamente caricati dal dispositivo di archiviazione in cui sono depositati, e *dati* sarà ``None``; questa modalità è
|
|
753
|
+
utile per verificare l'esistenza di una risorsa e il suo checksum senza effettivamente caricarla da remoto (nel caso di archiviazione
|
|
754
|
+
su cloud il caricamento potrebbe essere lento)."""
|
|
734
755
|
if (type == 0) and (not filename):
|
|
735
756
|
raise ValueError('filename must be specified for document type resources')
|
|
757
|
+
if with_extra:
|
|
758
|
+
out_params = ( OUT_DATA, OUT_FILENAME, OUT_ORIGINAL_FILENAME, OUT_DATA_CHECKSUM, OUT_METADATA, OUT_CODE_TIPOLOGIA )
|
|
759
|
+
else:
|
|
760
|
+
out_params = ( OUT_DATA, OUT_FILENAME, OUT_ORIGINAL_FILENAME, OUT_DATA_CHECKSUM )
|
|
736
761
|
return self._execute(CMD_FETCH_BINARY, {
|
|
737
762
|
IN_FIELD_NAME: field_or_tablename,
|
|
738
763
|
IN_ROW_ID: id,
|
|
739
764
|
IN_TYPE: type,
|
|
740
765
|
IN_FILENAME: filename,
|
|
766
|
+
IN_LABEL: label,
|
|
741
767
|
IN_CHECK: check_only,
|
|
742
|
-
},
|
|
768
|
+
}, out_params, progress=progress)
|
|
743
769
|
|
|
744
|
-
def store_binary(self, field_or_tablename, id, type, filename=None, original_filename=None, data=None, desc=None, force_delete=False, code_azienda=None, progress=None):
|
|
770
|
+
def store_binary(self, field_or_tablename, id, type, filename=None, original_filename=None, data=None, desc=None, force_delete=False, code_azienda=None, progress=None, label=None, metadata=None, code_tipologia=None, log=None):
|
|
745
771
|
"""Salva un contenuto binario sul server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
746
772
|
questa tabella unita a *id* identificano la scheda a cui abbinare la risorsa; *type* è uno dei valori della *Choice*``Resources``;
|
|
747
773
|
*filename* permette di specificare un nome file interno con cui identificare la risorsa (se ``None`` il server genererà un nome univoco
|
|
748
774
|
automaticamente); *original_filename* è il nome file originale i cui dati si stanno salvando sul server; *data* sono i dati binari
|
|
749
|
-
effettivi; *desc* è la descrizione da abbinare alla risorsa; *code_azienda* infine identifica l'azienda su cui si sta operando
|
|
775
|
+
effettivi; *desc* è la descrizione da abbinare alla risorsa; *code_azienda* infine identifica l'azienda su cui si sta operando, mentre
|
|
776
|
+
*code_tipologia* permette di specificare una tipologia da abbinare al dati. Per le risorse di tipo immagine aggiuntiva è necessario
|
|
777
|
+
specificare una *label* da abbinare all'immagine per identificarla univocamente. *metadata* può essere un ``dict`` in cui sia chiavi che
|
|
778
|
+
valori siano delle semplici stringhe, e permette di specificare dei metadati aggiuntivi associati alla risorsa binaria che si sta inserendo.
|
|
750
779
|
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà il nome del file interno usato dal
|
|
751
780
|
server per identificare la risorsa, che come detto sopra è uguale a *filename* se quest'ultimo è diverso da ``None``, altrimenti sarà
|
|
752
781
|
il nome file generato dal server.
|
|
@@ -762,13 +791,17 @@ class AsyncClient(Client):
|
|
|
762
791
|
IN_DATA: data,
|
|
763
792
|
IN_DESC: desc,
|
|
764
793
|
IN_FORCE_DELETE: force_delete,
|
|
765
|
-
|
|
794
|
+
IN_LABEL: label,
|
|
795
|
+
IN_METADATA: metadata,
|
|
796
|
+
IN_CODE_TIPOLOGIA: code_tipologia,
|
|
797
|
+
}, OUT_FILENAME, progress=progress, log=log)
|
|
766
798
|
|
|
767
|
-
def translate(self, field, value, language):
|
|
799
|
+
def translate(self, field, value, language, code_azienda=None):
|
|
768
800
|
return self._execute(CMD_TRANSLATE, {
|
|
769
801
|
IN_FIELD: field,
|
|
770
802
|
IN_VALUE: value,
|
|
771
|
-
IN_LANGUAGE: language
|
|
803
|
+
IN_LANGUAGE: language,
|
|
804
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
772
805
|
}, OUT_TEXT)
|
|
773
806
|
|
|
774
807
|
def set_database_language(self, language, progress=None):
|
|
@@ -776,38 +809,3 @@ class AsyncClient(Client):
|
|
|
776
809
|
IN_LANGUAGE: language,
|
|
777
810
|
}, progress=progress)
|
|
778
811
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
if __name__ == '__main__':
|
|
782
|
-
import progress.bar
|
|
783
|
-
import progress.spinner
|
|
784
|
-
|
|
785
|
-
async def main():
|
|
786
|
-
client = AsyncClient()
|
|
787
|
-
await client.connect('127.0.0.1')
|
|
788
|
-
await client.open_database('sqlite', 'demo')
|
|
789
|
-
await client.authenticate('admin', '')
|
|
790
|
-
|
|
791
|
-
# bar = progress.bar.IncrementalBar()
|
|
792
|
-
bar = progress.spinner.Spinner()
|
|
793
|
-
def prog(completeness, state, userdata):
|
|
794
|
-
# bar.goto(completeness)
|
|
795
|
-
bar.next()
|
|
796
|
-
return False
|
|
797
|
-
|
|
798
|
-
with bar:
|
|
799
|
-
# async with client:
|
|
800
|
-
try:
|
|
801
|
-
df = await client.select_data('EB_DocumentiFiscali', ['NumeroInterno'], progress=prog)
|
|
802
|
-
except Exception as e:
|
|
803
|
-
df = None
|
|
804
|
-
print(e)
|
|
805
|
-
|
|
806
|
-
# print("DONE")
|
|
807
|
-
drivers = client.list_drivers()
|
|
808
|
-
dbs = client.list_databases()
|
|
809
|
-
print(await asyncio.gather(drivers, dbs))
|
|
810
|
-
print(df)
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
asyncio.run(main(), debug=True)
|
kongalib/client.py
CHANGED
|
@@ -22,10 +22,9 @@ from kongalib import Error, ErrorList
|
|
|
22
22
|
from .constants import *
|
|
23
23
|
from .expression import *
|
|
24
24
|
from .data_dictionary import *
|
|
25
|
-
from .compat import *
|
|
26
25
|
|
|
27
|
-
from
|
|
28
|
-
from
|
|
26
|
+
from _kongalib import Client as ClientImpl
|
|
27
|
+
from _kongalib import start_timer
|
|
29
28
|
|
|
30
29
|
|
|
31
30
|
DEFAULT_DISCOVER_TIMEOUT = 5000
|
|
@@ -56,6 +55,7 @@ GET_FLAG_DEFAULT = GET_FLAG_GET_IMAGES | GET_FLAG_GET_ATTACHMENTS | GET_FLAG_
|
|
|
56
55
|
IMAGE_NORMAL = 1
|
|
57
56
|
IMAGE_WEB = 2
|
|
58
57
|
IMAGE_THUMBNAIL = 3
|
|
58
|
+
IMAGE_EXTRA = 4
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
def make_callbacks(success, error, log=None):
|
|
@@ -171,10 +171,10 @@ class Client(object):
|
|
|
171
171
|
"""
|
|
172
172
|
if (server is None) and (host is None):
|
|
173
173
|
raise ValueError("either 'host' or 'server' parameter must be specified")
|
|
174
|
-
if isinstance(server,
|
|
174
|
+
if isinstance(server, str) and (host is None):
|
|
175
175
|
host = server
|
|
176
176
|
server = None
|
|
177
|
-
if isinstance(host,
|
|
177
|
+
if isinstance(host, str) and (port is None) and (':' in host):
|
|
178
178
|
pos = host.rfind(':')
|
|
179
179
|
host = host[:pos]
|
|
180
180
|
try:
|
|
@@ -534,7 +534,7 @@ class Client(object):
|
|
|
534
534
|
parametro *get_total* come descritto sopra), la chiamata restituisce immediatamente un oggetto :class:`~kongalib.Deferred` e l'operazione
|
|
535
535
|
viene eseguita in modo asincrono; la callback *success* verrà invocata a tempo debito.
|
|
536
536
|
"""
|
|
537
|
-
if isinstance(fieldnamelist,
|
|
537
|
+
if isinstance(fieldnamelist, str):
|
|
538
538
|
fieldnamelist = [ fieldnamelist ]
|
|
539
539
|
elif fieldnamelist:
|
|
540
540
|
fieldnamelist = list(fieldnamelist)
|
|
@@ -582,7 +582,7 @@ class Client(object):
|
|
|
582
582
|
|
|
583
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
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,
|
|
585
|
+
if isinstance(fieldnamelist, str):
|
|
586
586
|
fieldnamelist = [ fieldnamelist ]
|
|
587
587
|
elif fieldnamelist:
|
|
588
588
|
fieldnamelist = list(fieldnamelist)
|
|
@@ -769,12 +769,13 @@ class Client(object):
|
|
|
769
769
|
})
|
|
770
770
|
return output['EXISTS']
|
|
771
771
|
|
|
772
|
-
def get_next_available_code(self, tablename, code_azienda, num_esercizio, dry_run=False):
|
|
772
|
+
def get_next_available_code(self, tablename, code_azienda, num_esercizio, dry_run=False, force=False):
|
|
773
773
|
return self.execute(CMD_GET_NEXT_CODE, {
|
|
774
774
|
IN_TABLE_NAME: tablename,
|
|
775
775
|
IN_CODE_AZIENDA: code_azienda,
|
|
776
776
|
IN_NUM_ESERCIZIO: num_esercizio,
|
|
777
777
|
IN_DRY_RUN: dry_run,
|
|
778
|
+
IN_FORCE: force,
|
|
778
779
|
})[OUT_CODE]
|
|
779
780
|
|
|
780
781
|
def get_last_npfe(self, code_azienda, num_esercizio):
|
|
@@ -829,13 +830,14 @@ class Client(object):
|
|
|
829
830
|
return output[OUT_DATA]
|
|
830
831
|
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
831
832
|
|
|
832
|
-
def list_binaries(self, field_or_tablename, id, type=None, success=None, error=None, progress=None):
|
|
833
|
+
def list_binaries(self, field_or_tablename, id, type=None, success=None, error=None, progress=None, full=False):
|
|
833
834
|
"""Ottiene la lista dei dati binari associati ad una scheda del database, identificata da *field_or_tablename* (che può essere un nome
|
|
834
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
|
|
835
836
|
forma ``( Tipo, NomeAllegato, NomeOriginale )``; *Tipo* è un intero ed è uno dei valori della *Choice* ``Resources``, *NomeAllegato* è
|
|
836
837
|
il nome assegnato internamente a Konga per identificare univocamente il contenuto binario, mentre *NomeOriginale* è il nome del file
|
|
837
838
|
originale da cui è stato caricato il contenuto. Se *type* è specificato, la funzione filtrerà i risultati in baso ad esso, ritornando
|
|
838
|
-
solo le tuple con il *Tipo* corretto.
|
|
839
|
+
solo le tuple con il *Tipo* corretto. Se *full* è ``True`` la n-esima tupla ritornata avrà un valore in più corrispondente all'etichetta
|
|
840
|
+
dell'immagine aggiuntiva se specificata.
|
|
839
841
|
Se *success* è diverso da ``None``, la callback verrà invocata in caso di successo con la lista di tuple di cui sopra.
|
|
840
842
|
"""
|
|
841
843
|
if success is not None:
|
|
@@ -851,17 +853,19 @@ class Client(object):
|
|
|
851
853
|
return self.execute(CMD_LIST_BINARIES, {
|
|
852
854
|
IN_FIELD_NAME: field_or_tablename,
|
|
853
855
|
IN_ROW_ID: id,
|
|
856
|
+
IN_FULL: full,
|
|
854
857
|
}, success=callback, error=errback, progress=progress)
|
|
855
858
|
else:
|
|
856
859
|
output = self.execute(CMD_LIST_BINARIES, {
|
|
857
860
|
IN_FIELD_NAME: field_or_tablename,
|
|
858
861
|
IN_ROW_ID: id,
|
|
862
|
+
IN_FULL: full,
|
|
859
863
|
})
|
|
860
864
|
if output[OUT_ERRNO] == OK:
|
|
861
865
|
return [ tuple(row) for row in output[OUT_LIST] if ((type is None) or (row[0] == type)) ]
|
|
862
866
|
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
863
867
|
|
|
864
|
-
def fetch_image(self, fieldname, id, type, success=None, error=None, progress=None):
|
|
868
|
+
def fetch_image(self, fieldname, id, type, success=None, error=None, progress=None, label=None):
|
|
865
869
|
"""Piccolo wrapper alla funzione :meth:`.fetch_binary`, dedicato alle immagini, con l'unica differenza che il valore di ritorno sarà
|
|
866
870
|
direttamente il contenuto binario dell'immagine in caso di successo (e questo sarà anche l'unico parametro passato alla callback
|
|
867
871
|
*success*)"""
|
|
@@ -879,25 +883,30 @@ class Client(object):
|
|
|
879
883
|
IN_FIELD_NAME: fieldname,
|
|
880
884
|
IN_ROW_ID: id,
|
|
881
885
|
IN_TYPE: type,
|
|
886
|
+
IN_LABEL: label,
|
|
882
887
|
}, success=callback, error=errback, progress=progress)
|
|
883
888
|
else:
|
|
884
889
|
output = self.execute(CMD_FETCH_BINARY, {
|
|
885
890
|
IN_FIELD_NAME: fieldname,
|
|
886
891
|
IN_ROW_ID: id,
|
|
887
892
|
IN_TYPE: type,
|
|
893
|
+
IN_LABEL: label,
|
|
888
894
|
})
|
|
889
895
|
if output[OUT_ERRNO] == OK:
|
|
890
896
|
return output[OUT_DATA]
|
|
891
897
|
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
892
898
|
|
|
893
|
-
def fetch_binary(self, field_or_tablename, id, type, filename=None, check_only=False, success=None, error=None, progress=None):
|
|
899
|
+
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):
|
|
894
900
|
"""Carica un contenuto binario dal server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
895
901
|
questa tabella unita a *id* identificano la scheda del database da cui caricare la risorsa; *type* è uno dei valori della *Choice*
|
|
896
|
-
``Resources``, mentre *filename*
|
|
902
|
+
``Resources``, mentre *filename* e *label* hanno senso solo per identificare rispettivamente le risorse di tipo documento ed immagine
|
|
903
|
+
aggiuntiva.
|
|
897
904
|
La funzione ritorna una tupla di quattro elementi: ( *dati*, *filename*, *original_filename*, *checksum* ). Questi quattro elementi
|
|
898
905
|
sono anche i parametri passati alla callback *success* in caso di successo. *dati* sono i dati binari che sono stati caricati dal
|
|
899
906
|
server; *filename* è il nome file interno con cui è identificata la risorsa, *original_filename* è il nome del file originale che è
|
|
900
|
-
stato specificato all'atto del salvataggio della risorsa sul server, mentre *checksum* è un checksum dei dati.
|
|
907
|
+
stato specificato all'atto del salvataggio della risorsa sul server, mentre *checksum* è un checksum dei dati. Se *with_extra* è
|
|
908
|
+
``True``, la funzione ritorna sei elementi, e gli elementi aggiuntivi sono un ``dict`` con i metadata associati alla risorsa, o ``None``
|
|
909
|
+
se non ci sono metadati associati, e il codice della tipologia allegato se presente.
|
|
901
910
|
Se *check_only* è ``True``, i dati binari della risorsa non verranno effettivamente caricati dal dispositivo di archiviazione in cui
|
|
902
911
|
sono depositati, e *dati* sarà ``None``; questa modalità è utile per verificare l'esistenza di una risorsa e il suo checksum senza
|
|
903
912
|
effettivamente caricarla da remoto (nel caso di archiviazione su cloud il caricamento potrebbe essere lento)."""
|
|
@@ -906,7 +915,10 @@ class Client(object):
|
|
|
906
915
|
if success is not None:
|
|
907
916
|
def callback(output, dummy):
|
|
908
917
|
if output[OUT_ERRNO] == OK:
|
|
909
|
-
|
|
918
|
+
if with_extra:
|
|
919
|
+
success(output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM], output[OUT_METADATA], output[OUT_CODE_TIPOLOGIA])
|
|
920
|
+
else:
|
|
921
|
+
success(output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM])
|
|
910
922
|
elif error is not None:
|
|
911
923
|
error(Error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
912
924
|
def errback(errno, errstr, dummy):
|
|
@@ -918,6 +930,7 @@ class Client(object):
|
|
|
918
930
|
IN_ROW_ID: id,
|
|
919
931
|
IN_TYPE: type,
|
|
920
932
|
IN_FILENAME: filename,
|
|
933
|
+
IN_LABEL: label,
|
|
921
934
|
IN_CHECK: check_only,
|
|
922
935
|
}, success=callback, error=errback, progress=progress)
|
|
923
936
|
else:
|
|
@@ -926,18 +939,25 @@ class Client(object):
|
|
|
926
939
|
IN_ROW_ID: id,
|
|
927
940
|
IN_TYPE: type,
|
|
928
941
|
IN_FILENAME: filename,
|
|
942
|
+
IN_LABEL: label,
|
|
929
943
|
IN_CHECK: check_only,
|
|
930
944
|
})
|
|
931
945
|
if output[OUT_ERRNO] == OK:
|
|
932
|
-
|
|
946
|
+
if with_extra:
|
|
947
|
+
return output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM], output[OUT_METADATA], output[OUT_CODE_TIPOLOGIA]
|
|
948
|
+
else:
|
|
949
|
+
return output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM]
|
|
933
950
|
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
934
951
|
|
|
935
|
-
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):
|
|
952
|
+
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):
|
|
936
953
|
"""Salva un contenuto binario sul server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
937
954
|
questa tabella unita a *id* identificano la scheda a cui abbinare la risorsa; *type* è uno dei valori della *Choice*``Resources``;
|
|
938
955
|
*filename* permette di specificare un nome file interno con cui identificare la risorsa (se ``None`` il server genererà un nome univoco
|
|
939
956
|
automaticamente); *original_filename* è il nome file originale i cui dati si stanno salvando sul server; *data* sono i dati binari
|
|
940
|
-
effettivi; *desc* è la descrizione da abbinare alla risorsa; *code_azienda* infine identifica l'azienda su cui si sta operando
|
|
957
|
+
effettivi; *desc* è la descrizione da abbinare alla risorsa; *code_azienda* infine identifica l'azienda su cui si sta operando, mentre
|
|
958
|
+
*code_tipologia* permette di specificare una tipologia da abbinare al dati. Per le risorse di tipo immagine aggiuntiva è necessario
|
|
959
|
+
specificare una *label* da abbinare all'immagine per identificarla univocamente. *metadata* può essere un ``dict`` in cui sia chiavi che
|
|
960
|
+
valori siano delle semplici stringhe, e permette di specificare dei metadati aggiuntivi associati alla risorsa binaria che si sta inserendo.
|
|
941
961
|
La funzione ritorna il nome del file interno usato dal server per identificare la risorsa, che come detto sopra è uguale a *filename* se
|
|
942
962
|
quest'ultimo è diverso da ``None``, altrimenti verrà ritornato il nome file generato dal server. La callback *success* se specificata
|
|
943
963
|
riceverà *filename* come unico parametro.
|
|
@@ -946,6 +966,8 @@ class Client(object):
|
|
|
946
966
|
if success is not None:
|
|
947
967
|
def callback(output, dummy):
|
|
948
968
|
if output[OUT_ERRNO] == OK:
|
|
969
|
+
if log is not None:
|
|
970
|
+
ErrorList(output[OUT_LOG] or []).prepare_log(log)
|
|
949
971
|
success(output[OUT_FILENAME])
|
|
950
972
|
elif error is not None:
|
|
951
973
|
error(Error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
@@ -963,6 +985,9 @@ class Client(object):
|
|
|
963
985
|
IN_DATA: data,
|
|
964
986
|
IN_DESC: desc,
|
|
965
987
|
IN_FORCE_DELETE: force_delete,
|
|
988
|
+
IN_LABEL: label,
|
|
989
|
+
IN_METADATA: metadata,
|
|
990
|
+
IN_CODE_TIPOLOGIA: code_tipologia,
|
|
966
991
|
}, success=callback, error=errback, progress=progress)
|
|
967
992
|
else:
|
|
968
993
|
output = self.execute(CMD_STORE_BINARY, {
|
|
@@ -975,16 +1000,22 @@ class Client(object):
|
|
|
975
1000
|
IN_DATA: data,
|
|
976
1001
|
IN_DESC: desc,
|
|
977
1002
|
IN_FORCE_DELETE: force_delete,
|
|
1003
|
+
IN_LABEL: label,
|
|
1004
|
+
IN_METADATA: metadata,
|
|
1005
|
+
IN_CODE_TIPOLOGIA: code_tipologia,
|
|
978
1006
|
})
|
|
979
1007
|
if output[OUT_ERRNO] == OK:
|
|
1008
|
+
if log is not None:
|
|
1009
|
+
ErrorList(output[OUT_LOG] or []).prepare_log(log)
|
|
980
1010
|
return output[OUT_FILENAME]
|
|
981
1011
|
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|
|
982
1012
|
|
|
983
|
-
def translate(self, field, value, language):
|
|
1013
|
+
def translate(self, field, value, language, code_azienda=None):
|
|
984
1014
|
output = self.execute(CMD_TRANSLATE, {
|
|
985
1015
|
IN_FIELD: field,
|
|
986
1016
|
IN_VALUE: value,
|
|
987
|
-
IN_LANGUAGE: language
|
|
1017
|
+
IN_LANGUAGE: language,
|
|
1018
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
988
1019
|
})
|
|
989
1020
|
if output[OUT_ERRNO] != OK:
|
|
990
1021
|
raise Error(output[OUT_ERRNO], output[OUT_ERROR])
|