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.

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
- try:
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 __unicode__(self):
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
- if PY3:
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
- if PY3:
329
- return self.__unicode__()
330
- else:
331
- return self.__unicode__().encode('utf-8')
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 ._kongalib import Decimal, Deferred, JSONEncoder, JSONDecoder, 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
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 PY3:
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, data, dummy):
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 len(answer) > 0:
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
- if finalize_output is not None:
119
- output = finalize_output(output)
120
- loop.call_soon_threadsafe(self._safe_set_result, future, output)
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
- if finalize_output is not None:
123
- output = finalize_output(output)
124
- loop.call_soon_threadsafe(self._safe_set_result, future, output)
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, text_base_types) and (host is None):
196
+ if isinstance(server, str) and (host is None):
185
197
  host = server
186
198
  server = None
187
- if isinstance(host, text_base_types) and (port is None) and (':' in 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, text_base_types):
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, text_base_types):
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* ha senso solo per identificare le risorse di tipo documento.
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 *check_only* è ``True``, i dati
733
- binari della risorsa non verranno effettivamente caricati dal dispositivo di archiviazione in cui sono depositati, e *dati* sarà
734
- ``None``; questa modalità è utile per verificare l'esistenza di una risorsa e il suo checksum senza effettivamente caricarla da remoto
735
- (nel caso di archiviazione su cloud il caricamento potrebbe essere lento)."""
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
- }, ( OUT_DATA, OUT_FILENAME, OUT_ORIGINAL_FILENAME, OUT_DATA_CHECKSUM ), progress=progress)
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
- }, OUT_FILENAME, progress=progress)
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 ._kongalib import Client as ClientImpl
28
- from ._kongalib import start_timer
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, text_base_types) and (host is None):
174
+ if isinstance(server, str) and (host is None):
175
175
  host = server
176
176
  server = None
177
- if isinstance(host, text_base_types) and (port is None) and (':' in 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, text_base_types):
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, text_base_types):
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* ha senso solo per identificare le risorse di tipo documento.
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
- success(output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM])
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
- return output[OUT_DATA], output[OUT_FILENAME], output[OUT_ORIGINAL_FILENAME], output[OUT_DATA_CHECKSUM]
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])