kongalib 2.0.0.post1__cp313-cp313-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/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
+