kongalib 2.0.0.post1__cp314-cp314t-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.cp314t-win_amd64.pdb +0 -0
- _kongalib.cp314t-win_amd64.pyd +0 -0
- kongalib/__init__.py +394 -0
- kongalib/async_client.py +811 -0
- kongalib/client.py +1044 -0
- kongalib/constants.py +187 -0
- kongalib/data_dictionary.py +203 -0
- kongalib/db.py +267 -0
- kongalib/expression.py +841 -0
- kongalib/json.py +114 -0
- kongalib/lex.py +1058 -0
- kongalib/scripting.py +764 -0
- kongalib/yacc.py +3276 -0
- kongalib-2.0.0.post1.dist-info/METADATA +150 -0
- kongalib-2.0.0.post1.dist-info/RECORD +21 -0
- kongalib-2.0.0.post1.dist-info/WHEEL +5 -0
- kongalib-2.0.0.post1.dist-info/licenses/LICENSE +165 -0
- kongalib-2.0.0.post1.dist-info/top_level.txt +4 -0
- kongalib-2.0.0.post1.dist-info/zip-safe +1 -0
- kongaui.py +507 -0
- kongautil.py +581 -0
kongalib/constants.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import importlib.resources
|
|
4
|
+
|
|
5
|
+
from . import json
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
OK = 0 #: Nessun errore
|
|
9
|
+
ERROR = -1 #: Errore generico
|
|
10
|
+
INTERNAL_ERROR = 1 #: Errore interno
|
|
11
|
+
OUT_OF_MEMORY = 2 #: Memoria esaurita
|
|
12
|
+
ACCESS_DENIED = 3 #: Accesso negato
|
|
13
|
+
TIMED_OUT = 4 #: Tempo scaduto
|
|
14
|
+
INTERRUPTED = 5 #: Operazione interrotta
|
|
15
|
+
NOT_INITIALIZED = 6 #: Oggetto non inizializzato
|
|
16
|
+
ABORTED = 7 #: Operazione annullata
|
|
17
|
+
TOO_MANY_OPEN_FILES = 8 #: Troppi file aperti
|
|
18
|
+
FILE_NOT_FOUND = 9 #: File non trovato
|
|
19
|
+
IO_ERROR = 10 #: Errore di input/output
|
|
20
|
+
FILE_EXISTS = 11 #: Il file già esiste
|
|
21
|
+
RESOURCE_UNAVAILABLE = 12 #: La risorsa non è disponibile
|
|
22
|
+
DISK_FULL = 13 #: Disco pieno
|
|
23
|
+
WOULD_BLOCK = 14 #: L'operazione sarebbe bloccante
|
|
24
|
+
INVALID_RESOURCE = 15 #: Risorsa non valida
|
|
25
|
+
BROKEN_PIPE = 16 #: Pipe terminata
|
|
26
|
+
CANNOT_CREATE_SOCKET = 100 #: Impossibile creare il socket
|
|
27
|
+
PROTOCOL_NOT_SUPPORTED = 101 #: Protocollo non supportato
|
|
28
|
+
BAD_ADDRESS = 102 #: Indirizzo dell'host non valido
|
|
29
|
+
CONNECTION_REFUSED = 103 #: Connessione rifiutata
|
|
30
|
+
NETWORK_IS_UNREACHABLE = 104 #: La rete non è raggiungibile
|
|
31
|
+
HOST_IS_UNREACHABLE = 105 #: L'host non è raggiungibile
|
|
32
|
+
ADDRESS_ALREADY_IN_USE = 106 #: Indirizzo già in uso
|
|
33
|
+
CANNOT_CONNECT = 107 #: Impossibile connettersi
|
|
34
|
+
CANNOT_CONFIGURE_SOCKET = 108 #: Impossibile configurare il socket
|
|
35
|
+
CANNOT_BIND_SOCKET = 109 #: Impossibile effettuare il bind del socket
|
|
36
|
+
CANNOT_LISTEN_SOCKET = 110 #: Impossibile mettere il socket in ascolto
|
|
37
|
+
WINSOCK_VERSION_NOT_SUPPORTED = 111 #: Versione di Winsock non supportata
|
|
38
|
+
ERROR_READING_SOCKET = 112 #: Errore in lettura dal socket
|
|
39
|
+
ERROR_WRITING_SOCKET = 113 #: Errore in scrittura sul socket
|
|
40
|
+
NOT_CONNECTED = 114 #: Non connesso
|
|
41
|
+
CONNECTION_LOST = 115 #: La connessione è stata persa
|
|
42
|
+
ALREADY_CONNECTED = 116 #: Connessione già stabilita
|
|
43
|
+
BAD_SOCKET = 117 #: Socket non valido
|
|
44
|
+
NO_NICS_FOUND = 118 #: Nessuna interfaccia di rete trovata
|
|
45
|
+
BAD_REQUEST = 200 #: Richiesta di esecuzione non valida
|
|
46
|
+
BAD_REPLY = 201 #: Risposta dal server non valida
|
|
47
|
+
NOT_AUTHORIZED = 202 #: Autorizzazione fallita
|
|
48
|
+
AUTHORIZATION_DATA_TOO_BIG = 203 #: Dati di autorizzazione troppo grandi
|
|
49
|
+
EXECUTE_FAILED = 204 #: La richiesta di esecuzione è fallita sul server
|
|
50
|
+
EXECUTE_ABORTED = 205 #: Richiesta di esecuzione annullata dall'utente
|
|
51
|
+
LISTENER_PORT_UNAVAILABLE = 206 #: Porta di ascolto non disponibile
|
|
52
|
+
RESPONDER_PORT_UNAVAILABLE = 207 #: Porta di risposta non disponibile
|
|
53
|
+
CLIENT_NOT_FOUND = 208 #: Client ID non trovato
|
|
54
|
+
SKIP_REQUEST = 209 #: Non registrare la richiesta al server
|
|
55
|
+
OK_NO_TRANSACTION = 212 #: Completa la richiesta con successo senza commit/rollback di transazione
|
|
56
|
+
ARCHIVE_NOT_FOUND = 300 #: Archivio non trovato
|
|
57
|
+
MALFORMED_RESOURCE_INDEX = 301 #: Indice delle risorse non valido nell'archivio
|
|
58
|
+
MALFORMED_RESOURCE_DEFINITION = 302 #: Definizione della risorsa non valida
|
|
59
|
+
CANNOT_FIND_RESOURCE_IN_ARCHIVE = 303 #: Risorsa non trovata nell'archivio
|
|
60
|
+
CANNOT_READ_RESOURCE = 304 #: Impossibile leggere la risorsa
|
|
61
|
+
CONFLICTING_RESOURCE_FILE_NAME = 305 #: Il nome del file di risorsa è in conflitto con un altro file nell'archivio
|
|
62
|
+
CANNOT_WRITE_RESOURCE = 306 #: Impossibile scrivere la risorsa
|
|
63
|
+
ARCHIVE_NOT_LOADED = 307 #: Archivio non caricato
|
|
64
|
+
BAD_STREAM = 400 #: Flusso dati corrotto
|
|
65
|
+
END_STREAM = 401 #: Flusso dati terminato
|
|
66
|
+
NO_MATCH = 500 #: Nessun risultato
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
CMD_GET_PERMISSIONS = 33
|
|
70
|
+
CMD_SET_PERMISSIONS = 34
|
|
71
|
+
CMD_BEGIN_TRANSACTION = 28
|
|
72
|
+
CMD_COMMIT_TRANSACTION = 29
|
|
73
|
+
CMD_ROLLBACK_TRANSACTION = 30
|
|
74
|
+
CMD_LOCK = 48
|
|
75
|
+
CMD_UNLOCK = 49
|
|
76
|
+
CMD_SELECT = 37
|
|
77
|
+
CMD_GET = 39
|
|
78
|
+
CMD_INSERT_FROM_DICT = 44
|
|
79
|
+
CMD_UPDATE_FROM_DICT = 45
|
|
80
|
+
CMD_DELETE_FROM_CODE = 47
|
|
81
|
+
CMD_CODE_EXISTS = 42
|
|
82
|
+
CMD_GET_NEXT_CODE = 43
|
|
83
|
+
CMD_GET_LAST_NPFE = 89
|
|
84
|
+
CMD_START_ELAB = 54
|
|
85
|
+
CMD_LIST_BINARIES = 86
|
|
86
|
+
CMD_FETCH_BINARY = 55
|
|
87
|
+
CMD_STORE_BINARY = 74
|
|
88
|
+
CMD_TRANSLATE = 66
|
|
89
|
+
CMD_SET_DATABASE_LANGUAGE = 67
|
|
90
|
+
|
|
91
|
+
IN_CHECK = 'CHECK'
|
|
92
|
+
IN_CODE = 'CODE'
|
|
93
|
+
IN_CODE_AZIENDA = 'CODE_AZIENDA'
|
|
94
|
+
IN_COLUMN_NAMES = 'COLUMN_NAMES'
|
|
95
|
+
IN_COMMAND = 'COMMAND'
|
|
96
|
+
IN_COMMAND_NAME = 'COMMAND_NAME'
|
|
97
|
+
IN_DATA = 'DATA'
|
|
98
|
+
IN_DESC = 'DESC'
|
|
99
|
+
IN_DICT_DATA = 'DICT_DATA'
|
|
100
|
+
IN_DRY_RUN = 'DRY_RUN'
|
|
101
|
+
IN_EXTRA_WHERE = 'EXTRA_WHERE'
|
|
102
|
+
IN_FIELD = 'FIELD'
|
|
103
|
+
IN_FIELD_NAME = 'FIELD_NAME'
|
|
104
|
+
IN_FILENAME = 'FILENAME'
|
|
105
|
+
IN_FLAGS = 'FLAGS'
|
|
106
|
+
IN_FORCE_DELETE = 'FORCE_DELETE'
|
|
107
|
+
IN_GET_ROWS_EXIST = 'GET_ROWS_EXIST'
|
|
108
|
+
IN_GET_TOTAL_ROWS = 'GET_TOTAL_ROWS'
|
|
109
|
+
IN_LANGUAGE = 'LANGUAGE'
|
|
110
|
+
IN_NUM_ESERCIZIO = 'NUM_ESERCIZIO'
|
|
111
|
+
IN_OFFSET = 'OFFSET'
|
|
112
|
+
IN_ORDER_BY = 'ORDER_BY'
|
|
113
|
+
IN_ORDER_DESC = 'ORDER_DESC'
|
|
114
|
+
IN_ORIGINAL_FILENAME = 'ORIGINAL_FILENAME'
|
|
115
|
+
IN_PARAMS = 'PARAMS'
|
|
116
|
+
IN_PERMISSIONS = 'PERMISSIONS'
|
|
117
|
+
IN_ROW_COUNT = 'ROW_COUNT'
|
|
118
|
+
IN_ROW_EXTRA_FIELDS = 'ROW_EXTRA_FIELDS'
|
|
119
|
+
IN_ROW_ID = 'ROW_ID'
|
|
120
|
+
IN_TABLE_NAME = 'TABLE_NAME'
|
|
121
|
+
IN_TRANSACTION = 'TRANSACTION'
|
|
122
|
+
IN_TX = 'TX'
|
|
123
|
+
IN_TYPE = 'TYPE'
|
|
124
|
+
IN_USER_ID = 'USER_ID'
|
|
125
|
+
IN_VALUE = 'VALUE'
|
|
126
|
+
IN_WHERE_CLAUSE = 'WHERE_CLAUSE'
|
|
127
|
+
IN_LABEL = 'LABEL'
|
|
128
|
+
IN_METADATA = 'METADATA'
|
|
129
|
+
IN_CODE_TIPOLOGIA = 'CODE_TIPOLOGIA'
|
|
130
|
+
|
|
131
|
+
OUT_CHECKSUM = 'CHECKSUM'
|
|
132
|
+
OUT_CODE = 'CODE'
|
|
133
|
+
OUT_COLUMN_NAMES = 'COLUMN_NAMES'
|
|
134
|
+
OUT_DATA = 'DATA'
|
|
135
|
+
OUT_DATA_CHECKSUM = 'DATA_CHECKSUM'
|
|
136
|
+
OUT_DICT_DATA = 'DICT_DATA'
|
|
137
|
+
OUT_ERRNO = 'ERRNO'
|
|
138
|
+
OUT_ERROR = 'ERROR'
|
|
139
|
+
OUT_EXIST = 'EXIST'
|
|
140
|
+
OUT_FILENAME = 'FILENAME'
|
|
141
|
+
OUT_ID = 'ID'
|
|
142
|
+
OUT_LIST = 'LIST'
|
|
143
|
+
OUT_LOG = 'LOG'
|
|
144
|
+
OUT_NPFE = 'NPFE'
|
|
145
|
+
OUT_ORIGINAL_FILENAME = 'ORIGINAL_FILENAME'
|
|
146
|
+
OUT_PERMISSIONS = 'PERMISSIONS'
|
|
147
|
+
OUT_RESULT_SET = 'RESULT_SET'
|
|
148
|
+
OUT_TEXT = 'TEXT'
|
|
149
|
+
OUT_TOTAL_ROWS = 'TOTAL_ROWS'
|
|
150
|
+
OUT_METADATA = 'METADATA'
|
|
151
|
+
OUT_CODE_TIPOLOGIA = 'CODE_TIPOLOGIA'
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
_CONSTANTS = { key: value for key, value in locals().items() if key.isupper() }
|
|
155
|
+
|
|
156
|
+
_EXTERNAL = {}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _ensure():
|
|
160
|
+
if not _EXTERNAL.get('@fetched', False):
|
|
161
|
+
try:
|
|
162
|
+
data = json.loads(importlib.resources.files('kongalib').joinpath('constants.json').read_bytes())
|
|
163
|
+
except:
|
|
164
|
+
data = None
|
|
165
|
+
if isinstance(data, dict):
|
|
166
|
+
existing = globals()
|
|
167
|
+
for key, value in data.items():
|
|
168
|
+
if (key in existing) and (value != existing[key]) and (key not in ('IO_ERROR','DISK_FULL')):
|
|
169
|
+
raise RuntimeError(f"Value '{value}' for {key} in constants.json mismatch (should be '{existing[key]}')")
|
|
170
|
+
_EXTERNAL.update(data)
|
|
171
|
+
_CONSTANTS.update(_EXTERNAL)
|
|
172
|
+
_EXTERNAL['@fetched'] = True
|
|
173
|
+
return _CONSTANTS.keys()
|
|
174
|
+
__all__ = list(_ensure())
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def __getattr__(name):
|
|
179
|
+
if name in _CONSTANTS:
|
|
180
|
+
return _CONSTANTS[name]
|
|
181
|
+
else:
|
|
182
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r} (!)")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def __dir__():
|
|
187
|
+
return __all__
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# _ _ _ _
|
|
3
|
+
# | | | (_) |
|
|
4
|
+
# | | _____ _ __ __ _ __ _| |_| |__
|
|
5
|
+
# | |/ / _ \| '_ \ / _` |/ _` | | | '_ \
|
|
6
|
+
# | < (_) | | | | (_| | (_| | | | |_) |
|
|
7
|
+
# |_|\_\___/|_| |_|\__, |\__,_|_|_|_.__/
|
|
8
|
+
# __/ |
|
|
9
|
+
# |___/
|
|
10
|
+
#
|
|
11
|
+
# Konga client library, by EasyByte Software
|
|
12
|
+
#
|
|
13
|
+
# https://github.com/easybyte-software/kongalib
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from kongalib import Error, ErrorList
|
|
17
|
+
from .constants import *
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
TYPE_TINYINT = 1 #: Tipo di campo SQL TINYINT; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``int``.
|
|
21
|
+
TYPE_SMALLINT = 2 #: Tipo di campo SQL SMALLINT; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``int``.
|
|
22
|
+
TYPE_INT = 3 #: Tipo di campo SQL INT; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``int``.
|
|
23
|
+
TYPE_BIGINT = 4 #: Tipo di campo SQL BIGINT; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``int``.
|
|
24
|
+
TYPE_FLOAT = 5 #: Tipo di campo SQL FLOAT; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``float``.
|
|
25
|
+
TYPE_DOUBLE = 6 #: Tipo di campo SQL DOUBLE; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``float``.
|
|
26
|
+
TYPE_DECIMAL = 7 #: Tipo di campo SQL DECIMAL; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo :class:`kongalib.Decimal`.
|
|
27
|
+
TYPE_DATE = 8 #: Tipo di campo SQL DATE; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``datetime.date``.
|
|
28
|
+
TYPE_TIME = 9 #: Tipo di campo SQL TIME; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``datetime.time``.
|
|
29
|
+
TYPE_TIMESTAMP = 10 #: Tipo di campo SQL TIMESTAMP; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``datetime.datetime``.
|
|
30
|
+
TYPE_YEAR = 11 #: Tipo di campo SQL YEAR; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``int``.
|
|
31
|
+
TYPE_CHAR = 12 #: Tipo di campo SQL CHAR; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``unicode``.
|
|
32
|
+
TYPE_VARCHAR = 13 #: Tipo di campo SQL VARCHAR; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``unicode``.
|
|
33
|
+
TYPE_TINYTEXT = 14 #: Tipo di campo SQL TINYTEXT; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``unicode``.
|
|
34
|
+
TYPE_TEXT = 15 #: Tipo di campo SQL TEXT; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``unicode``.
|
|
35
|
+
TYPE_LONGTEXT = 16 #: Tipo di campo SQL LONGTEXT; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``unicode``.
|
|
36
|
+
TYPE_TINYBLOB = 17 #: Tipo di campo SQL TINYBLOB; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``bytes``.
|
|
37
|
+
TYPE_BLOB = 18 #: Tipo di campo SQL BLOB; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``bytes``.
|
|
38
|
+
TYPE_LONGBLOB = 19 #: Tipo di campo SQL LONGBLOB; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``bytes``.
|
|
39
|
+
TYPE_JSON = 20 #: Tipo di campo SQL JSON; i valori ottenuti dalla :meth:`~kongalib.Client.select_data` saranno di tipo ``unicode``.
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
TABLE_HAS_IMAGES = 0x1 #: Flag informativo di tabella del data dictionary. Se specificato, i record della tabella possono avere immagini collegate.
|
|
43
|
+
TABLE_IS_INDEXED = 0x2 #: Flag informativo di tabelle del data dictionary. Se specificato, la tabella è indicizzata per la ricerca full-text.
|
|
44
|
+
|
|
45
|
+
FIELD_UNSIGNED = 0x1 #: Flag informativo di campo del data dictionary. Se specificato, il tipo dato è senza segno.
|
|
46
|
+
FIELD_UNIQUE = 0x2 #: Flag informativo di campo del data dictionary. Se specificato, il campo SQL è UNIQUE.
|
|
47
|
+
FIELD_NOT_NULL = 0x4 #: Flag informativo di campo del data dictionary. Se specificato, il campo SQL non può essere NULL.
|
|
48
|
+
FIELD_PRIMARY_KEY = 0x8 #: Flag informativo di campo del data dictionary. Se specificato, il campo SQL è una PRIMARY KEY.
|
|
49
|
+
FIELD_FOREIGN_KEY = 0x10 #: Flag informativo di campo del data dictionary. Se specificato, il campo SQL è una FOREIGN KEY.
|
|
50
|
+
FIELD_AUTO_INCREMENT = 0x20 #: Flag informativo di campo del data dictionary. Se specificato, il campo SQL è un intero incrementato automaticamente.
|
|
51
|
+
FIELD_DEFAULT_NULL = 0x40 #: Flag informativo di campo del data dictionary. Se specificato, il campo SQL ha NULL come valore di default.
|
|
52
|
+
FIELD_DEFAULT_CURRENT_TS = 0x80 #: Flag informativo di campo del data dictionary. Se specificato, il campo SQL ha il timestamp corrente come valore di default.
|
|
53
|
+
FIELD_DEFAULT = 0xC0 #: Flag informativo di campo del data dictionary. Se specificato, il campo SQL ha un valore di default.
|
|
54
|
+
FIELD_ON_UPDATE_CURRENT_TS = 0x100 #: Flag informativo di campo del data dictionary. Se specificato, il campo SQL viene aggiornato automaticamente al timestamp corrente su UPDATE.
|
|
55
|
+
FIELD_ON_DELETE_CASCADE = 0x200 #: Flag informativo di campo del data dictionary. Se specificato, su cancellazione di un record tutti i record ad esso collegati verranno cancellati a loro volta.
|
|
56
|
+
FIELD_ON_DELETE_SET_NULL = 0x400 #: Flag informativo di campo del data dictionary. Se specificato, su cancellazione di un record tutti i record ad esso collegati avranno il collegamento impostato a NULL.
|
|
57
|
+
|
|
58
|
+
FIELD_HIDDEN = 0x10000
|
|
59
|
+
FIELD_READ_ONLY = 0x20000
|
|
60
|
+
FIELD_CHOICE = 0x40000
|
|
61
|
+
FIELD_CODES = 0x80000
|
|
62
|
+
FIELD_REQUIRED = 0x100000
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class DataDictionary(object):
|
|
67
|
+
"""La classe DataDictionary contiene informazioni sul dizionario dei dati usato da un server Konga. Tramite questa classe è possibile
|
|
68
|
+
ottenere la lista delle tabelle e dei campi, e informazioni specifiche su ogni campo.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
class Choice(object):
|
|
72
|
+
"""Piccola classe che descrive le opzioni associate ad una scelta fissa del dizionario dei dati."""
|
|
73
|
+
|
|
74
|
+
def __init__(self, data):
|
|
75
|
+
self.__data = data
|
|
76
|
+
|
|
77
|
+
def __getattr__(self, key):
|
|
78
|
+
"""Ottiene il valore associato alla chiave *key*."""
|
|
79
|
+
return self.__data[key][0]
|
|
80
|
+
|
|
81
|
+
def get_label(self, key):
|
|
82
|
+
"""Ottiene la descrizione della chiave *key* sotto forma di ``dict`` con le traduzioni corrispondenti a ciascuna lingua."""
|
|
83
|
+
if isinstance(key, str):
|
|
84
|
+
return self.__data[key][1]
|
|
85
|
+
for value, label in self.__data.values():
|
|
86
|
+
if value == key:
|
|
87
|
+
return label
|
|
88
|
+
|
|
89
|
+
def keys(self):
|
|
90
|
+
"""Ottiene la lista delle chiavi supportate da questa *Choice*."""
|
|
91
|
+
return list(self.__data.keys())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def __init__(self, data):
|
|
95
|
+
self.__data = data
|
|
96
|
+
self.__table_data = {}
|
|
97
|
+
tables = self.__data['tables']
|
|
98
|
+
for name, tabledata in tables.items():
|
|
99
|
+
self.__table_data[name.lower()] = tabledata
|
|
100
|
+
missing_table = { 'fields': {} }
|
|
101
|
+
|
|
102
|
+
self.__views = {
|
|
103
|
+
'EB_CodiciFissi': {
|
|
104
|
+
'desc': tables['EB_CodiciFissi1']['desc'],
|
|
105
|
+
'flags': tables['EB_CodiciFissi1']['flags'],
|
|
106
|
+
'EB_CodiciFissi1': tables['EB_CodiciFissi1']['fields'].keys(),
|
|
107
|
+
'EB_CodiciFissi2': list(set(tables['EB_CodiciFissi2']['fields'].keys()) - set(tables['EB_CodiciFissi1']['fields'].keys())),
|
|
108
|
+
'EB_CodiciFissi3': list(set(tables.get('EB_CodiciFissi3', missing_table)['fields'].keys()) - set(tables['EB_CodiciFissi1']['fields'].keys())),
|
|
109
|
+
},
|
|
110
|
+
'EB_Clienti': {
|
|
111
|
+
'desc': tables['EB_ClientiFornitori']['desc'],
|
|
112
|
+
'flags': tables['EB_ClientiFornitori']['flags'],
|
|
113
|
+
'EB_ClientiFornitori': tables['EB_ClientiFornitori']['fields'].keys(),
|
|
114
|
+
},
|
|
115
|
+
'EB_Fornitori': {
|
|
116
|
+
'desc': tables['EB_ClientiFornitori']['desc'],
|
|
117
|
+
'flags': tables['EB_ClientiFornitori']['flags'],
|
|
118
|
+
'EB_ClientiFornitori': tables['EB_ClientiFornitori']['fields'].keys(),
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
for view in self.__views.values():
|
|
122
|
+
fields = {}
|
|
123
|
+
for component, cfields in view.items():
|
|
124
|
+
if not component.startswith('EB_'):
|
|
125
|
+
continue
|
|
126
|
+
fields.update(tables.get(component, missing_table)['fields'])
|
|
127
|
+
view.update({
|
|
128
|
+
'fields': fields,
|
|
129
|
+
})
|
|
130
|
+
for view, viewinfo in self.__views.items():
|
|
131
|
+
self.__table_data[view.lower()] = viewinfo
|
|
132
|
+
|
|
133
|
+
def get_version(self):
|
|
134
|
+
"""Ottiene la versione del dizionario dei dati come intero nella forma ``(major << 16) | (minor << 8) | revision``."""
|
|
135
|
+
return self.__data['version']
|
|
136
|
+
|
|
137
|
+
def get_tables_list(self):
|
|
138
|
+
"""Ottiene la lista delle tabelle del dizionario dei dati."""
|
|
139
|
+
return [ table for table in self.__data['tables'].keys() if table not in self.__views ]
|
|
140
|
+
|
|
141
|
+
def get_table_info(self, tablename):
|
|
142
|
+
"""Ottiene un ``dict`` con le informazioni sulla tabella *tablename*. Le chiavi significative sono *desc* (un ``dict`` con le traduzioni
|
|
143
|
+
della descrizione della tabella) e *flags* (flag informativi sulla tabella; vedere le costanti :ref:`flag di tabella <table_flags>`).
|
|
144
|
+
"""
|
|
145
|
+
data = self.__table_data[tablename.lower()]
|
|
146
|
+
return {
|
|
147
|
+
'desc': data['desc'],
|
|
148
|
+
'flags': data['flags']
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
def get_fields_list(self, tablename):
|
|
152
|
+
"""Ottiene la lista dei campi per la tabella *tablename*. I nomi dei campi restituiti non includono il nome tabella."""
|
|
153
|
+
return list(self.__table_data[tablename.lower()]['fields'].keys())
|
|
154
|
+
|
|
155
|
+
def get_field_info(self, fieldname):
|
|
156
|
+
"""Ottiene un ``dict`` con le informazioni sul campo *fieldname*. Il nome del campo deve essere nella forma ``<NomeTabella>.<NomeCampo>``.
|
|
157
|
+
Le chiavi significative sono *desc* (un ``dict`` con le traduzioni della descrizione del campo), *type* (tipo di campo; vedere le
|
|
158
|
+
costanti :ref:`tipi di campo <field_types>`), *default* (valore di default) e *flags* (flag informativi sul campo; vedere le costanti
|
|
159
|
+
:ref:`flag di campo <field_flags>`).
|
|
160
|
+
"""
|
|
161
|
+
table, field = self.resolve_field(fieldname).split('.')
|
|
162
|
+
return self.__table_data[table.lower()]['fields'][field].copy()
|
|
163
|
+
|
|
164
|
+
def resolve_field(self, fieldname):
|
|
165
|
+
"""Converte un nome campo dalla forma ``<NomeTabella>.[<CampoRef>.*]<NomeCampo>`` nella forma ``<NomeTabella>.<NomeCampo>``, risolvendo
|
|
166
|
+
eventuali campi ref_* intermedi."""
|
|
167
|
+
parts = fieldname.split('.')
|
|
168
|
+
table = parts.pop(0)
|
|
169
|
+
if len(parts) == 0:
|
|
170
|
+
raise ValueError("Invalid field name")
|
|
171
|
+
while True:
|
|
172
|
+
fields_data = self.__table_data[table.lower()]['fields']
|
|
173
|
+
field = parts.pop(0)
|
|
174
|
+
if field not in fields_data:
|
|
175
|
+
raise ValueError("Field '%s' not found in table '%s'" % (field, table))
|
|
176
|
+
if len(parts) == 0:
|
|
177
|
+
break
|
|
178
|
+
table = fields_data[field]['reference']['table']
|
|
179
|
+
return '%s.%s' % (table, field)
|
|
180
|
+
|
|
181
|
+
def get_choice(self, choicename):
|
|
182
|
+
"""Ottiene un oggetto ``Choice`` a partire dal nome della scelta *choicename*. L'oggetto può successivamente essere interrogato per
|
|
183
|
+
ottenere informazioni su ogni opzione disponibile nella scelta."""
|
|
184
|
+
data = {}
|
|
185
|
+
options = self.__data['choices'][choicename]['options']
|
|
186
|
+
for value, option_data in options.items():
|
|
187
|
+
data[option_data['define']] = (int(value), option_data['option'])
|
|
188
|
+
return DataDictionary.Choice(data)
|
|
189
|
+
|
|
190
|
+
def get_translation_context(self, fieldname):
|
|
191
|
+
try:
|
|
192
|
+
table, fieldname = self.resolve_field(fieldname).split('.')
|
|
193
|
+
context = '%s(%s)' % (table, fieldname)
|
|
194
|
+
if context in self.__data['contexts']:
|
|
195
|
+
return '%s.%s' % (table, fieldname)
|
|
196
|
+
except:
|
|
197
|
+
pass
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
def get_data(self):
|
|
201
|
+
return self.__data
|
|
202
|
+
|
|
203
|
+
|
kongalib/db.py
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# _ _ _ _
|
|
3
|
+
# | | | (_) |
|
|
4
|
+
# | | _____ _ __ __ _ __ _| |_| |__
|
|
5
|
+
# | |/ / _ \| '_ \ / _` |/ _` | | | '_ \
|
|
6
|
+
# | < (_) | | | | (_| | (_| | | | |_) |
|
|
7
|
+
# |_|\_\___/|_| |_|\__, |\__,_|_|_|_.__/
|
|
8
|
+
# __/ |
|
|
9
|
+
# |___/
|
|
10
|
+
#
|
|
11
|
+
# Konga client library, by EasyByte Software
|
|
12
|
+
#
|
|
13
|
+
# https://github.com/easybyte-software/kongalib
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
import time
|
|
17
|
+
import datetime
|
|
18
|
+
|
|
19
|
+
from kongalib import Client, Decimal, Error as _Error, ErrorList
|
|
20
|
+
from .constants import *
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
apilevel = "2.0" #: Versione delle API, come da specifica
|
|
24
|
+
threadsafety = 2 #: E' possibile usare le funzioni di modulo e gli oggetti :class:`.Connection` da thread diversi
|
|
25
|
+
paramstyle = "format" #: Il formato dei parametri nelle query deve essere nello stile printf (WHERE name=%s)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Error(Exception):
|
|
29
|
+
"""Eccezione base, come da specifica."""
|
|
30
|
+
def __init__(self, msg):
|
|
31
|
+
self.msg = msg
|
|
32
|
+
def __str__(self):
|
|
33
|
+
return self.msg
|
|
34
|
+
|
|
35
|
+
class Warning(Exception):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
class InterfaceError(Error):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
class DatabaseError(Error):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
class InternalError(DatabaseError):
|
|
45
|
+
"""Errore interno."""
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
class OperationalError(DatabaseError):
|
|
49
|
+
"""Eccezione che viene lanciata su errori di connessione e/o comunicazione con il server Konga."""
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
class ProgrammingError(DatabaseError):
|
|
53
|
+
"""Eccezione che viene lanciata se l'esecuzione di una query SQL ha generato un errore."""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
class IntegrityError(DatabaseError):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
class DataError(DatabaseError):
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
class NotSupportedError(DatabaseError):
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class STRING(object):
|
|
67
|
+
def __init__(self, string):
|
|
68
|
+
self.string = string
|
|
69
|
+
|
|
70
|
+
class BINARY(object):
|
|
71
|
+
def __init__(self, binary):
|
|
72
|
+
self.binary = binary
|
|
73
|
+
|
|
74
|
+
class NUMBER(object):
|
|
75
|
+
def __init__(self, number):
|
|
76
|
+
self.number = number
|
|
77
|
+
|
|
78
|
+
class DATETIME(object):
|
|
79
|
+
def __init__(self, datetime):
|
|
80
|
+
self.datetime = datetime
|
|
81
|
+
|
|
82
|
+
class ROWID(object):
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class Connection(Client):
|
|
87
|
+
"""Classe che gestisce una connessione ad un server Konga. Viene usata per instanziare oggetti :class:`.Cursor` su cui poi operare, oppure
|
|
88
|
+
per gestire le transazioni.
|
|
89
|
+
"""
|
|
90
|
+
def close(self):
|
|
91
|
+
"""Chiude la connessione con il server Konga."""
|
|
92
|
+
self.disconnect()
|
|
93
|
+
|
|
94
|
+
def commit(self):
|
|
95
|
+
"""Esegue una ``COMMIT`` per la transazione SQL corrente."""
|
|
96
|
+
self.query("COMMIT")
|
|
97
|
+
|
|
98
|
+
def rollback(self):
|
|
99
|
+
"""Esegue una ``ROLLBACK`` per la transazione SQL corrente."""
|
|
100
|
+
self.query("ROLLBACK")
|
|
101
|
+
|
|
102
|
+
def cursor(self):
|
|
103
|
+
"""Crea un nuovo oggetto :class:`Cursor` associato a questa connessione."""
|
|
104
|
+
return Cursor(self)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class Cursor(object):
|
|
108
|
+
"""Questa classe permette di eseguire query SQL sulla connessione *conn* ad essa associata. Per instanziare oggetti di classe :class:`Cursor`
|
|
109
|
+
si usa il metodo :meth:`.Connection.cursor`.
|
|
110
|
+
La classe può essere anche usato come iteratore; in tal caso per ogni ciclo verrà restituita la prossima riga del result set derivante
|
|
111
|
+
dall'ultima query eseguita sul cursore stesso.
|
|
112
|
+
"""
|
|
113
|
+
def __init__(self, conn):
|
|
114
|
+
self.__connection = conn
|
|
115
|
+
self.__description = None
|
|
116
|
+
self.__rowcount = -1
|
|
117
|
+
self.__rownumber = 0
|
|
118
|
+
self.__arraysize = 1
|
|
119
|
+
self.__result = None
|
|
120
|
+
self.__valid = True
|
|
121
|
+
|
|
122
|
+
def close(self):
|
|
123
|
+
"""Termina l'utilizzo di questo cursore; chiamate successive ai metodi di questo oggetto lanceranno un eccezione di tipo :exc:`.InternalError`."""
|
|
124
|
+
self.__valid = False
|
|
125
|
+
|
|
126
|
+
def execute(self, command, *args):
|
|
127
|
+
"""Esegue la query SQL *command* sulla connessione associata al cursore; *command* può essere nel formato printf, e in tal caso
|
|
128
|
+
*args* sono gli argomenti che vengono sostituiti nella stringa di formato.
|
|
129
|
+
"""
|
|
130
|
+
if not self.__valid:
|
|
131
|
+
raise InternalError('cursor is not valid anymore')
|
|
132
|
+
try:
|
|
133
|
+
self.__rowcount, fields, self.__result = self.__connection.query(command % args)
|
|
134
|
+
except _Error as e:
|
|
135
|
+
if e.errno in (NOT_CONNECTED, CONNECTION_LOST, TIMED_OUT, BAD_REPLY):
|
|
136
|
+
raise OperationalError(str(e))
|
|
137
|
+
else:
|
|
138
|
+
raise ProgrammingError(str(e))
|
|
139
|
+
if len(self.__result) > 0:
|
|
140
|
+
self.__rowcount = len(self.__result)
|
|
141
|
+
self.__description = []
|
|
142
|
+
row = self.__result[0]
|
|
143
|
+
for field, data in zip(fields, row):
|
|
144
|
+
if isinstance(data, (int, float, Decimal)):
|
|
145
|
+
t = NUMBER
|
|
146
|
+
elif isinstance(data, (datetime.date, datetime.datetime)):
|
|
147
|
+
t = DATETIME
|
|
148
|
+
elif isinstance(data, str):
|
|
149
|
+
t = STRING
|
|
150
|
+
else:
|
|
151
|
+
t = BINARY
|
|
152
|
+
self.__description.append((field, t, None, None, None, None, None))
|
|
153
|
+
self.__rownumber = 0
|
|
154
|
+
else:
|
|
155
|
+
self.__result = None
|
|
156
|
+
self.__description = None
|
|
157
|
+
self.__rownumber = None
|
|
158
|
+
|
|
159
|
+
def executemany(self, operation, seq):
|
|
160
|
+
"""Esegue la stessa query SQL tante volte quanta la lunghezza della sequenza *seq*; l'elemento *N* di *seq* deve essere una tupla
|
|
161
|
+
di argomenti da passare come *args* al metodo :meth:`execute` per eseguire la query *N*-esima.
|
|
162
|
+
"""
|
|
163
|
+
for args in seq:
|
|
164
|
+
self.execute(operation, *tuple(args))
|
|
165
|
+
|
|
166
|
+
def fetchone(self):
|
|
167
|
+
"""Restituisce la prossima riga del result set ottenuto dall'ultima query eseguita su questo cursore. La riga è restituita sotto forma di
|
|
168
|
+
tupla di valori."""
|
|
169
|
+
if self.__result is None:
|
|
170
|
+
raise InternalError('no valid result set')
|
|
171
|
+
if self.__rownumber >= len(self.__result):
|
|
172
|
+
return None
|
|
173
|
+
self.__rownumber += 1
|
|
174
|
+
return tuple(self.__result[self.__rownumber - 1])
|
|
175
|
+
|
|
176
|
+
def fetchmany(self, size=None):
|
|
177
|
+
"""Restituisce una lista di righe in cui ogni riga è nello stesso formato restituito da :meth:`fetchone`. La lista includerà al massimo
|
|
178
|
+
*size* righe; se *size* è ``None``, verranno incluse al massimo :attr:`arraysize` righe.
|
|
179
|
+
"""
|
|
180
|
+
if size is None:
|
|
181
|
+
size = self.__arraysize
|
|
182
|
+
result = []
|
|
183
|
+
for c in range(0, size):
|
|
184
|
+
result.append(self.fetchone())
|
|
185
|
+
return result
|
|
186
|
+
|
|
187
|
+
def fetchall(self):
|
|
188
|
+
"""Restituisce tutte le righe del result set corrente."""
|
|
189
|
+
size = len(self.__result or []) - (self.__rownumber or 0)
|
|
190
|
+
return self.fetchmany(size)
|
|
191
|
+
|
|
192
|
+
def __iter__(self):
|
|
193
|
+
return self
|
|
194
|
+
|
|
195
|
+
def __next__(self):
|
|
196
|
+
row = self.fetchone()
|
|
197
|
+
if row is None:
|
|
198
|
+
raise StopIteration
|
|
199
|
+
return row
|
|
200
|
+
|
|
201
|
+
def next(self):
|
|
202
|
+
return self.__next__()
|
|
203
|
+
|
|
204
|
+
def setinputsizes(self, sizes):
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
def setoutputsize(self, size, column):
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def connection(self):
|
|
212
|
+
"""Proprietà in sola lettura che restituisce l'oggetto :class:`Connection` associato a questo cursore."""
|
|
213
|
+
return self.__connection
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def rowcount(self):
|
|
217
|
+
"""Proprietà in sola lettura che restituisce il numero di righe del result set corrente."""
|
|
218
|
+
return self.__rowcount
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def rownumber(self):
|
|
222
|
+
"""Proprietà in sola lettura che restituisce il numero di riga corrente all'interno del result set."""
|
|
223
|
+
return self.__rownumber
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def arraysize(self):
|
|
227
|
+
"""Proprietà in lettura/scrittura che specifica il numero massimo di righe da includere nel risultato di :meth:`fetchmany` se *size* è ``None``."""
|
|
228
|
+
return self.__arraysize
|
|
229
|
+
|
|
230
|
+
@arraysize.setter
|
|
231
|
+
def arraysize(self, size):
|
|
232
|
+
self.__arraysize = size
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def Date(year, month, day):
|
|
236
|
+
return datetime.date(year, month, day)
|
|
237
|
+
|
|
238
|
+
def Time(hour, minute, second):
|
|
239
|
+
return datetime.time(hour, minute, second)
|
|
240
|
+
|
|
241
|
+
def Timestamp(year, month, day, hour, minute, second):
|
|
242
|
+
return datetime.datetime(year, month, day, hour, minute, second)
|
|
243
|
+
|
|
244
|
+
def DateFromTicks(ticks):
|
|
245
|
+
return Date(*time.localtime(ticks)[:3])
|
|
246
|
+
|
|
247
|
+
def TimeFromTicks(ticks):
|
|
248
|
+
return Time(*time.localtime(ticks)[3:6])
|
|
249
|
+
|
|
250
|
+
def TimestampFromTicks(ticks):
|
|
251
|
+
return Timestamp(*time.localtime(ticks)[:6])
|
|
252
|
+
|
|
253
|
+
def connect(host, port=0, driver=None, database=None, user=None, password=None, tenant_key=None):
|
|
254
|
+
"""Esegue una connessione al server Konga identificato da *host* e *port*, usando l'eventuale chiave tenant *tenant_key*, apre *database*
|
|
255
|
+
usando il *driver* specificato, ed infine si autentica usando *user* e *password*. Restituisce un oggetto :class:`Connection`; da questo
|
|
256
|
+
è possibile ottenere un oggetto :class:`Cursor` che permette di eseguire query SQL sul database aperto sulla connessione.
|
|
257
|
+
"""
|
|
258
|
+
conn = Connection()
|
|
259
|
+
try:
|
|
260
|
+
conn.connect({ 'host': host, 'port': port }, options={ 'tenant_key': tenant_key })
|
|
261
|
+
conn.open_database(driver, database)
|
|
262
|
+
conn.authenticate(user, password)
|
|
263
|
+
except _Error as e:
|
|
264
|
+
raise OperationalError(str(e))
|
|
265
|
+
return conn
|
|
266
|
+
|
|
267
|
+
|