kongalib 1.12.0__cp311-cp311-win_amd64.whl → 2.0.0.post1__cp311-cp311-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.cp311-win_amd64.pdb +0 -0
- _kongalib.cp311-win_amd64.pyd +0 -0
- kongalib/__init__.py +22 -36
- kongalib/async_client.py +61 -65
- kongalib/client.py +51 -20
- kongalib/constants.py +110 -1853
- kongalib/data_dictionary.py +3 -12
- kongalib/db.py +7 -8
- kongalib/expression.py +17 -23
- kongalib/json.py +1 -2
- kongalib/scripting.py +38 -27
- {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.cp311-win_amd64.pdb +0 -0
- kongalib/_kongalib.cp311-win_amd64.pyd +0 -0
- kongalib/compat.py +0 -492
- kongalib-1.12.0.dist-info/RECORD +0 -22
- {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):
|
|
@@ -107,7 +111,7 @@ class AsyncClient(Client):
|
|
|
107
111
|
answer = output[OUT_LOG] or []
|
|
108
112
|
error_list = ErrorList(answer)
|
|
109
113
|
if output[OUT_ERRNO] == OK:
|
|
110
|
-
if
|
|
114
|
+
if error_list.errno != OK:
|
|
111
115
|
if log is None:
|
|
112
116
|
loop.call_soon_threadsafe(self._safe_set_exception, future, error_list)
|
|
113
117
|
else:
|
|
@@ -115,13 +119,21 @@ class AsyncClient(Client):
|
|
|
115
119
|
if log.has_errors():
|
|
116
120
|
loop.call_soon_threadsafe(self._safe_set_exception, future, error_list)
|
|
117
121
|
else:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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)
|
|
121
129
|
else:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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)
|
|
125
137
|
else:
|
|
126
138
|
loop.call_soon_threadsafe(self._safe_set_exception, future, ErrorList.from_error(output[OUT_ERRNO], output[OUT_ERROR]))
|
|
127
139
|
return success
|
|
@@ -181,10 +193,10 @@ class AsyncClient(Client):
|
|
|
181
193
|
"""
|
|
182
194
|
if (server is None) and (host is None):
|
|
183
195
|
raise ValueError("either 'host' or 'server' parameter must be specified")
|
|
184
|
-
if isinstance(server,
|
|
196
|
+
if isinstance(server, str) and (host is None):
|
|
185
197
|
host = server
|
|
186
198
|
server = None
|
|
187
|
-
if isinstance(host,
|
|
199
|
+
if isinstance(host, str) and (port is None) and (':' in host):
|
|
188
200
|
pos = host.rfind(':')
|
|
189
201
|
host = host[:pos]
|
|
190
202
|
try:
|
|
@@ -457,7 +469,7 @@ class AsyncClient(Client):
|
|
|
457
469
|
def get_permissions(self, user_id):
|
|
458
470
|
return self._execute(CMD_GET_PERMISSIONS, {
|
|
459
471
|
IN_USER_ID: user_id
|
|
460
|
-
})
|
|
472
|
+
}, OUT_PERMISSIONS)
|
|
461
473
|
|
|
462
474
|
def set_permissions(self, user_id, permissions):
|
|
463
475
|
return self._execute(CMD_SET_PERMISSIONS, {
|
|
@@ -549,7 +561,7 @@ class AsyncClient(Client):
|
|
|
549
561
|
Se *get_total* è ``False``, il risultato sarà il solo *result_set*, ossia una lista di righe risultato della query, dove ogni riga è una
|
|
550
562
|
lista di valori.
|
|
551
563
|
"""
|
|
552
|
-
if isinstance(fieldnamelist,
|
|
564
|
+
if isinstance(fieldnamelist, str):
|
|
553
565
|
fieldnamelist = [ fieldnamelist ]
|
|
554
566
|
elif fieldnamelist:
|
|
555
567
|
fieldnamelist = list(fieldnamelist)
|
|
@@ -568,7 +580,7 @@ class AsyncClient(Client):
|
|
|
568
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):
|
|
569
581
|
"""Esattamente come :meth:`.select_data`, ma l'oggetto ``asyncio.Future`` restituito una volta completato ritornerà un *result_set* sotto
|
|
570
582
|
forma di lista di ``dict``, anzichè una lista di liste."""
|
|
571
|
-
if isinstance(fieldnamelist,
|
|
583
|
+
if isinstance(fieldnamelist, str):
|
|
572
584
|
fieldnamelist = [ fieldnamelist ]
|
|
573
585
|
elif fieldnamelist:
|
|
574
586
|
fieldnamelist = list(fieldnamelist)
|
|
@@ -674,12 +686,13 @@ class AsyncClient(Client):
|
|
|
674
686
|
IN_EXTRA_WHERE: where(extra_where),
|
|
675
687
|
}, OUT_EXISTS)
|
|
676
688
|
|
|
677
|
-
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):
|
|
678
690
|
return self._execute(CMD_GET_NEXT_CODE, {
|
|
679
691
|
IN_TABLE_NAME: tablename,
|
|
680
692
|
IN_CODE_AZIENDA: code_azienda,
|
|
681
693
|
IN_NUM_ESERCIZIO: num_esercizio,
|
|
682
694
|
IN_DRY_RUN: dry_run,
|
|
695
|
+
IN_FORCE: force,
|
|
683
696
|
}, OUT_CODE)
|
|
684
697
|
|
|
685
698
|
def get_last_npfe(self, code_azienda, num_esercizio):
|
|
@@ -695,24 +708,26 @@ class AsyncClient(Client):
|
|
|
695
708
|
IN_CODE_AZIENDA: code_azienda,
|
|
696
709
|
IN_NUM_ESERCIZIO: num_esercizio,
|
|
697
710
|
IN_TX: tx,
|
|
698
|
-
}, OUT_DATA, progress=progress)
|
|
711
|
+
}, OUT_DATA, progress=progress, log=log)
|
|
699
712
|
|
|
700
|
-
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):
|
|
701
714
|
"""Ottiene la lista dei dati binari associati ad una scheda del database, identificata da *field_or_tablename* (che può essere un nome
|
|
702
715
|
tabella o un campo da cui risolvere il nome tabella) e *id*. La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato
|
|
703
716
|
una volta completato sarà una lista di tuple, in cui la n-esima tupla ha la forma ``( Tipo, NomeAllegato, NomeOriginale )``; *Tipo*
|
|
704
717
|
è un intero ed è uno dei valori della *Choice* ``Resources``, *NomeAllegato* è il nome assegnato internamente a Konga per identificare
|
|
705
718
|
univocamente il contenuto binario, mentre *NomeOriginale* è il nome del file originale da cui è stato caricato il contenuto. Se *type*
|
|
706
|
-
è 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.
|
|
707
721
|
"""
|
|
708
722
|
def get_result(output):
|
|
709
723
|
return [ tuple(row) for row in output[OUT_LIST] if ((type is None) or (row[0] == type)) ]
|
|
710
724
|
return self._execute(CMD_LIST_BINARIES, {
|
|
711
725
|
IN_FIELD_NAME: field_or_tablename,
|
|
712
726
|
IN_ROW_ID: id,
|
|
727
|
+
IN_FULL: full,
|
|
713
728
|
}, get_result, progress=progress)
|
|
714
729
|
|
|
715
|
-
def fetch_image(self, fieldname, id, type, progress=None):
|
|
730
|
+
def fetch_image(self, fieldname, id, type, progress=None, label=None):
|
|
716
731
|
"""Piccolo wrapper alla funzione :meth:`.fetch_binary`, dedicato alle immagini, con l'unica differenza che l'oggetto ``asyncio.Future``
|
|
717
732
|
restituito una volta completato avrà come valore di ritorno direttamente il contenuto binario dell'immagine.
|
|
718
733
|
"""
|
|
@@ -720,35 +735,47 @@ class AsyncClient(Client):
|
|
|
720
735
|
IN_FIELD_NAME: fieldname,
|
|
721
736
|
IN_ROW_ID: id,
|
|
722
737
|
IN_TYPE: type,
|
|
738
|
+
IN_LABEL: label,
|
|
723
739
|
}, OUT_DATA, progress=progress)
|
|
724
740
|
|
|
725
|
-
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):
|
|
726
742
|
"""Carica un contenuto binario dal server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
727
743
|
questa tabella unita a *id* identificano la scheda del database da cui caricare la risorsa; *type* è uno dei valori della *Choice*
|
|
728
|
-
``Resources``, mentre *filename*
|
|
744
|
+
``Resources``, mentre *filename* e *label* hanno senso solo per identificare rispettivamente le risorse di tipo documento ed immagine
|
|
745
|
+
aggiuntiva.
|
|
729
746
|
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà una tupla di quattro elementi:
|
|
730
747
|
( *dati*, *filename*, *original_filename*, *checksum* ). *dati* sono i dati binari che sono stati caricati dal server; *filename* è
|
|
731
748
|
il nome file interno con cui è identificata la risorsa, *original_filename* è il nome del file originale che è stato specificato
|
|
732
|
-
all'atto del salvataggio della risorsa sul server, mentre *checksum* è un checksum dei dati. Se *
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
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)."""
|
|
736
755
|
if (type == 0) and (not filename):
|
|
737
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 )
|
|
738
761
|
return self._execute(CMD_FETCH_BINARY, {
|
|
739
762
|
IN_FIELD_NAME: field_or_tablename,
|
|
740
763
|
IN_ROW_ID: id,
|
|
741
764
|
IN_TYPE: type,
|
|
742
765
|
IN_FILENAME: filename,
|
|
766
|
+
IN_LABEL: label,
|
|
743
767
|
IN_CHECK: check_only,
|
|
744
|
-
},
|
|
768
|
+
}, out_params, progress=progress)
|
|
745
769
|
|
|
746
|
-
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):
|
|
747
771
|
"""Salva un contenuto binario sul server. *field_or_tablename* può essere un nome tabella o un campo da cui risolvere il nome tabella;
|
|
748
772
|
questa tabella unita a *id* identificano la scheda a cui abbinare la risorsa; *type* è uno dei valori della *Choice*``Resources``;
|
|
749
773
|
*filename* permette di specificare un nome file interno con cui identificare la risorsa (se ``None`` il server genererà un nome univoco
|
|
750
774
|
automaticamente); *original_filename* è il nome file originale i cui dati si stanno salvando sul server; *data* sono i dati binari
|
|
751
|
-
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.
|
|
752
779
|
La funzione restituisce un oggetto ``asyncio.Future`` il cui risultato una volta completato sarà il nome del file interno usato dal
|
|
753
780
|
server per identificare la risorsa, che come detto sopra è uguale a *filename* se quest'ultimo è diverso da ``None``, altrimenti sarà
|
|
754
781
|
il nome file generato dal server.
|
|
@@ -764,13 +791,17 @@ class AsyncClient(Client):
|
|
|
764
791
|
IN_DATA: data,
|
|
765
792
|
IN_DESC: desc,
|
|
766
793
|
IN_FORCE_DELETE: force_delete,
|
|
767
|
-
|
|
794
|
+
IN_LABEL: label,
|
|
795
|
+
IN_METADATA: metadata,
|
|
796
|
+
IN_CODE_TIPOLOGIA: code_tipologia,
|
|
797
|
+
}, OUT_FILENAME, progress=progress, log=log)
|
|
768
798
|
|
|
769
|
-
def translate(self, field, value, language):
|
|
799
|
+
def translate(self, field, value, language, code_azienda=None):
|
|
770
800
|
return self._execute(CMD_TRANSLATE, {
|
|
771
801
|
IN_FIELD: field,
|
|
772
802
|
IN_VALUE: value,
|
|
773
|
-
IN_LANGUAGE: language
|
|
803
|
+
IN_LANGUAGE: language,
|
|
804
|
+
IN_CODE_AZIENDA: code_azienda,
|
|
774
805
|
}, OUT_TEXT)
|
|
775
806
|
|
|
776
807
|
def set_database_language(self, language, progress=None):
|
|
@@ -778,38 +809,3 @@ class AsyncClient(Client):
|
|
|
778
809
|
IN_LANGUAGE: language,
|
|
779
810
|
}, progress=progress)
|
|
780
811
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
if __name__ == '__main__':
|
|
784
|
-
import progress.bar
|
|
785
|
-
import progress.spinner
|
|
786
|
-
|
|
787
|
-
async def main():
|
|
788
|
-
client = AsyncClient()
|
|
789
|
-
await client.connect('127.0.0.1')
|
|
790
|
-
await client.open_database('sqlite', 'demo')
|
|
791
|
-
await client.authenticate('admin', '')
|
|
792
|
-
|
|
793
|
-
# bar = progress.bar.IncrementalBar()
|
|
794
|
-
bar = progress.spinner.Spinner()
|
|
795
|
-
def prog(completeness, state, userdata):
|
|
796
|
-
# bar.goto(completeness)
|
|
797
|
-
bar.next()
|
|
798
|
-
return False
|
|
799
|
-
|
|
800
|
-
with bar:
|
|
801
|
-
# async with client:
|
|
802
|
-
try:
|
|
803
|
-
df = await client.select_data('EB_DocumentiFiscali', ['NumeroInterno'], progress=prog)
|
|
804
|
-
except Exception as e:
|
|
805
|
-
df = None
|
|
806
|
-
print(e)
|
|
807
|
-
|
|
808
|
-
# print("DONE")
|
|
809
|
-
drivers = client.list_drivers()
|
|
810
|
-
dbs = client.list_databases()
|
|
811
|
-
print(await asyncio.gather(drivers, dbs))
|
|
812
|
-
print(df)
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
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])
|