def close_progress(): """Nasconde la finestra di progresso precedentemente mostrata con :func:`kongaui.open_progress`.""" if _proxy.is_valid(): _proxy.ui.close_progress() else: print('\033[2K\r', end='') sys.stdout.flush()
def select_record(tablename, multi=False, size=None, where_expr=None, code_azienda=None, num_esercizio=None): """Mostra una finestra di selezione record di Konga; la finestra mostrerà i record della tabella *tablename* e avrà dimensione *size* (tupla di due elementi nella forma ``(width, height)``). *where_expr* può essere un'espressione SQL *WHERE* per filtrare i record selezionabili; *code_azienda* e *code_esercizio* filtrano ulteriormente i record visualizzati usando l'azienda e l'esercizio specificati. Se *multi* è ``True``, la funzione restituisce una lista di record selezionati sotto forma di ``dict``, altrimenti restituisce il ``dict`` del singolo record selezionato. In tutti i casi se l'utente annulla l'operazione, la funzione restituirà ``None``. .. warning:: Questa funzione è disponibile solo all'interno di Konga; eseguendola da fuori verrà lanciata l'eccezione :class:`kongautil.KongaRequiredError`. """ if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.select_record(tablename, multi, size, where_expr, code_azienda=code_azienda, num_esercizio=num_esercizio) else: raise kongautil.KongaRequiredError
def choose_directory(message=None, path=''): """Mostra una finestra di selezione directory con titolo *message* e percorso iniziale *path*. La funzione restituisce il percorso della directory selezionata oppure ``None`` se l'utente ha annullato l'operazione. Se eseguita al di fuori di Konga, questa funzione ignora il parametro *path*, e l'utente dovrà inserire il percorso completo della directory; se verrà inserito un percorso vuoto, la funzione restituirà ``None``.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.choose_directory(message, path) else: if message: print(colorama.Style.BRIGHT + textwrap.fill(message, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) while True: try: dirname = input( 'Enter an existing directory to open or none to cancel: ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) return None if not dirname: return None if os.path.exists(dirname) and os.path.isdir(dirname): break return dirname
def open_file(message=None, specs=None, path='', multi=False): """Mostra una finestra di caricamento file con titolo *message. *specs* può essere una lista di tuple ``(extension, description)`` per permettere di caricare solo file di tipi specifici; *path* è il percorso predefinito, e *multi* permette di selezionare più di un file da caricare. Se *multi* è ``False``, la funzione restituisce il percorso del file selezionato o ``None`` se l'utente ha annullato il caricamento, altrimenti restituisce la lista di file selezionati. Se eseguita al di fuori di Konga, questa funzione ignora i parametri *specs*, *path* e *multi*, e l'utente dovrà inserire il percorso completo del file da caricare; se verrà inserito un percorso vuoto, la funzione restituirà ``None``.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.open_file(message, specs, path, multi) else: if message: print(colorama.Style.BRIGHT + textwrap.fill(message, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) while True: try: filename = input( 'Enter an existing filename to open or none to cancel: ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) return None if not filename: return None if os.path.exists(filename) and os.path.isfile(filename): break return filename
def get_site_packages(): """Restituisce una lista di percorsi di installazione dei pacchetti Python.""" if _proxy.is_valid(): return [_proxy.util.get_site_packages()] else: import site return site.getsitepackages()
def get_window_vars(): """Restituisce un ``dict`` contenente una serie di costanti definite per la finestra di navigazione correntemente aperta su Konga, incluse informazioni sull'azienda corrente, l'eventuale selezione se la finestra mostra una vista a lista, e molto altro. Se la funzione è eseguita fuori da Konga, il ``dict`` restituito sarà vuoto.""" if _proxy.is_valid(): return _proxy.util.get_window_vars() else: return {}
def get_context(): """Restituisce un oggetto di classe :class:`kongautil.ScriptContext` da usare per la gestione dell'I/O da parte degli script usati come azioni esterne di Konga. .. warning:: Questa funzione è disponibile solo all'interno di Konga; eseguendola da fuori verrà lanciata l'eccezione :class:`kongautil.KongaRequiredError`. """ if _proxy.is_valid(): return _proxy.util.get_context() else: raise KongaRequiredError
def open_progress(title=None, cancellable=True): """Mostra una finestra di progresso con titolo *title*, potenzialmente annullabile dall'utente se *cancellable* è ``True``; la funzione ritorna immediatamente. Se eseguita fuori da Konga, questa funzione ignora il parametro *cancellable*.""" if _proxy.is_valid(): _proxy.ui.open_progress(title or u'Operazione in corso…', cancellable) else: if title: print(colorama.Style.BRIGHT + textwrap.fill(title, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) set_progress()
def open_window(command, key_id=None, key_code=None, code_azienda=None, num_esercizio=None): """Apre una finestra di Konga mostrando il comando *command*, ed eventualmente aprendo il record identificato univocamente o da *key_id* (ID del record), o dalla tupla (*key_code*, *code_azienda*, *code_esercizio*) (Codice del record, codice dell'azienda e codice dell'esercizio). .. warning:: Questa funzione è disponibile solo all'interno di Konga; eseguendola da fuori verrà lanciata l'eccezione :class:`kongautil.KongaRequiredError`. """ if _proxy.is_valid(): _proxy.ui.open_window(command, key_id, key_code, code_azienda, num_esercizio) else: raise kongautil.KongaRequiredError
def set_progress(progress=None, message=None, state=None): """Imposta l'avanzamento corrente nella finestra di progresso precedentemente mostrata con :func:`kongaui.open_progress`. *progress* può essere ``None`` per specificare un avanzamento indefinito, oppure un valore compreso tra 0 e 100 per specificare la percentuale di avanzamento completata. *message* e *state* sono messaggi aggiuntivi da mostrare nella finestra di avanzamento.""" if _proxy.is_valid(): _proxy.ui.set_progress(progress, message, state) else: term_width = _get_term_width() def elide(s, width): if len(s) > width: parts = s.split(' ') mid = len(parts) // 2 before = parts[:mid] after = parts[mid:] while before or after: if len(before) > len(after): del before[-1] elif after: del after[-1] s = ' '.join(before) + ' [...] ' + ' '.join(after) if len(s) <= width: break if len(s) > width: s = s[:width - 6] + ' [...]' return s text = [] if message: text.append(message) if state: text.append(state) if not text: text.append('Operazione in corso...') if (progress is None) or (progress < 0): tick = ('\\', '|', '/', '-')[int(time.time() * 5) % 4] bar = '%s %s' % (elide(', '.join(text), term_width - 3), tick) else: if PY3: block = u'\u2588' else: block = '#' progress = (block * int((progress * 30) // 100)) bar = '|%-30s| %s' % (progress, elide(', '.join(text), term_width - 34)) print('\033[2K\r' + bar, end='') sys.stdout.flush()
def save_file(message=None, spec=None, path=''): """Mostra una finestra di salvataggio file con titolo *message. *spec* può essere una tupla nella forma ``(extension, description)`` per permettere di salvare file di un tipo specifico; *path* è il percorso predefinito. La funzione restituisce il percorso del file da salvare oppure ``None`` se l'utente ha annullato il salvataggio. Se eseguita al di fuori di Konga, questa funzione ignora i parametri *specs* e *path*, e l'utente dovrà inserire il percorso completo del file da salvare; se verrà inserito un percorso vuoto, la funzione restituirà ``None``.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.save_file(message, spec, path) else: if message: print(colorama.Style.BRIGHT + textwrap.fill(message, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) try: filename = input('Enter filename to be saved or none to cancel: ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) return None return filename or None
def suspend_timeout(): """Sospende il timeout di esecuzione dello script. La funzione non comporta eccezioni ma non ha alcun effetto se eseguita al di fuori di Konga.""" if _proxy.is_valid(): return _proxy.builtin.set_timeout(restore=False)
def print_log(log, title, target=PRINT_TARGET_PREVIEW, filename=None): """Stampa il contenuto dell'oggetto *log* di classe :class:`kongalib.Log`; se si esegue questa funzione dall'interno di Konga, verrà usata la funzione :func:`print_layout`, passando i parametri *target* e *filename*; viceversa se si esegue fuori da Konga, il log verrà stampato su terminale.""" if _proxy.is_valid(): template = """<?xml version='1.0' encoding='utf-8'?> <layout version="2" name="%(title)s" title="%(title)s" orientation="vertical" margin_top="75" margin_right="75" margin_bottom="75" margin_left="75"> <init> <![CDATA[set_datasource(Datasource(['id', 'type', 'message'], DATA, 'Master')) ]]></init> <header width="100%%" height="175" only_first="true"> <label width="100%%" height="100" align="center" font_size="14" bgcolor="#EEE" border_edges="left|right|top|bottom">%(title)s</label> </header> <module width="100%%" alt_bgcolor="#EEE" condition="iterate('id')"> <field top="0" width="10%%" align="top" type="data">type</field> <field left="10%%" top="0" width="90%%" align="top" wrapping="wrap" type="data">message</field> </module> <module width="100%%"> <init> <![CDATA[this.visible = (len(DATA) == 0) ]]></init> <label width="100%%" align="center"> <text> <it>Operazione completata con successo</it> </text> </label> </module> <footer width="100%%" font_size="7" height="175"> <rect top="75" width="100%%" height="100" /> <field padding_left="50" top="75" left="0" width="30%%" height="50" align="bottom" type="expr">DATABASE_NAME</field> <field padding_left="50" top="125" left="0" width="30%%" height="50" align="top" type="expr">COMPANY_NAME</field> <field top="75" left="30%%" width="40%%" height="50" align="hcenter|bottom" type="expr">TITLE</field> <datetime top="125" left="30%%" width="40%%" height="50" align="hcenter|top" format="PdmyHM"> <text> <it>Stampato da $USER_NAME il %%d alle %%t</it> <nl>Afgedrukt door $USER_NAME op %%d om %%t uur</nl> </text> </datetime> <page padding_right="50" top="75" left="80%%" width="20%%" height="100" align="vcenter|right"> <text> <it>Pagina %%p di %%t</it> <nl>Pagina %%p van %%t</nl> </text> </page> </footer> </layout> """ % { 'title': title, } data = [] for index, message in enumerate(log.get_messages()): msg = log.strip_html(message[1]) if isinstance(msg, bytes): msg = msg.decode('utf-8', 'replace') data.append((index, ('INFO', 'WARNING', 'ERROR')[message[0]], msg)) print_layout(template, {'DATA': data}, target=target, filename=filename) else: import colorama print(colorama.Style.BRIGHT + title + colorama.Style.RESET_ALL) status = { kongalib.Log.INFO: colorama.Style.BRIGHT + "INFO " + colorama.Style.RESET_ALL, kongalib.Log.WARNING: colorama.Style.BRIGHT + colorama.Fore.YELLOW + "WARNING " + colorama.Style.RESET_ALL, kongalib.Log.ERROR: colorama.Style.BRIGHT + colorama.Fore.RED + "ERROR " + colorama.Style.RESET_ALL, } for message in log.get_messages(): print('%s%s' % (status[message[0]], message[1]))
def print_layout(command_or_layout, builtins=None, code_azienda=None, code_esercizio=None, target=None, filename=None, progress=True, client=None): """Esegue una stampa su Konga. *command_or_layout* può essere un nome di comando di Konga, oppure un sorgente XML contenente la struttura stessa del layout da stampare; *builtins* è un ``dict`` i cui valori verranno passati al motore di stampa e saranno disponibili all'interno degli script del layout; *code_azienda* e *code_esercizio* identificano l'azienda e l'esercizio per cui eseguire la stampa, mentre *target* è una delle costanti ``PRINT_TARGET_*`` definite sopra, che specificano la destinazione della stampa (se non specificata e la funzione è eseguita all'interno di Konga, verrà assunta ``PRINT_TARGET_PREVIEW``, altrimenti ``PRINT_TARGET_PDF``); *filename* è il nome del file da salvare ed ha un senso solo quando si stampa su file. Se *progress* è ``True`` verrà mostrata a video una barra di progresso dell'operazione di stampa. La funzione restituisce un oggetto di classe :class:`kongalib.Log` con il resoconto dell'operazione di stampa, oppure lancia un'eccezione di classe :class:`kongautil.PrintError` in caso di errori. .. warning:: Se eseguita fuori da Konga, questa funzione richiede che sull'host sia stato installato Konga Client (o Konga), e non supporta come *target* i valori ``PRINT_TARGET_PREVIEW`` e ``PRINT_TARGET_PAPER``. Konga Client (o Konga) verrà invocato e provvederà a connettersi al client corrente e stampare il documento richiesto; i parametri di connessione saranno ricavati automaticamente dal client ottenuto dall'ultima chiamata alla funzione :func:`kongautil.connect` oppure, se specificato, dal parametro ``client``. """ if _proxy.is_valid(): if target is None: target = PRINT_TARGET_PREVIEW log = _proxy.util.print_layout(command_or_layout, builtins or {}, code_azienda, code_esercizio, target, filename, progress) else: log = kongalib.Log() if not filename: raise ValueError("Output filename must be specified") if isinstance(command_or_layout, kongalib.text_base_types) and ( command_or_layout.strip().startswith('<?xml') or command_or_layout.strip().startswith('<layout')): import tempfile temp = tempfile.NamedTemporaryFile(mode='w', delete=False) with temp: temp.write(command_or_layout) command_or_layout = temp.name else: temp = None target = { PRINT_TARGET_PDF: 'pdf', PRINT_TARGET_CSV: 'csv', PRINT_TARGET_XLS: 'xls', }.get(target, 'pdf') builtins = builtins or {} if code_azienda: builtins['COMPANY_CODE'] = code_azienda if code_esercizio: builtins['ACCOUNTING_YEAR_CODE'] = code_esercizio script = [ '.print "%s" %s -o %s -f "%s"' % (command_or_layout, ' '.join([ '%s=%s' % (key, quote(str(value))) for key, value in builtins.items() ]), target, filename) ] try: _run_script(script, log, client) finally: if temp is not None: os.unlink(temp) if log.has_errors(): raise PrintError(log) return log
def connect(host=None, port=None, driver=None, database=None, username=None, password=None, tenant_key=None, config=None): """Restituisce un oggetto :class:`kongalib.Client` già connesso. Se eseguito dall'interno di Konga, la connessione sarà stabilita con il server, il database e l'utenza correntemente aperti sul programma, e i parametri passati a questa funzione saranno ignorati. Se eseguita fuori da Konga, la funzione proverà a collegarsi al primo server disponibile sulla rete locale, aprendo il primo database disponibile autenticandosi col l'utenza *admin* con password vuota; ogni parametro di connessione può essere forzato tramite i parametri passati a questa funzione, oppure da linea di comando specificando gli argomenti ``--host``, ``--port``, ``--driver``, ``-d|--database``, ``-u|--username``, ``-p|--password`` e ``-k|--tenant-key``. Inoltre, è possibile definire i parametri come variabili all'interno di un file di configurazione, nella sezione ``[kongautil.connect]``; tale file deve avere il nome passato a questa funzione con il parametro ``config``, altrimenti verranno ricercati nell'ordine anche il file con lo stesso nome dello script lanciato da terminale, ma con estensione ``.cfg``, il file ``kongalib.cfg`` sempre nella stessa directory da cui si esegue lo script e infine il file ``~/.kongalib`` (sotto Unix) o ``%userprofile%\kongalib.cfg`` (sotto Windows).""" if _proxy.is_valid(): info = _proxy.util.get_connection_info() if info is not None: host, port, driver, database, username, password, tenant_key = info client = kongalib.Client() client.connect(host=host, port=port, options={'tenant_key': tenant_key}) client.open_database(driver, database) client.authenticate(username, password) return client else: global _last_client global _konga_exe global _konga_args client = kongalib.Client() if host is None: import argparse try: import configparser except: import ConfigParser as configparser files = [ os.path.splitext(sys.argv[0])[0] + '.cfg', os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'kongalib.cfg'), os.path.abspath( os.path.expanduser( os.path.join( '~', 'kongalib.cfg' if sys.platform == 'win32' else '.kongalib'))), ] if config: files.insert(0, config) config = configparser.RawConfigParser({ 'host': host or '', 'port': str(port or ''), 'driver': driver or '', 'database': database or '', 'username': username or '', 'password': password or '', 'tenant_key': tenant_key or '', 'konga_exe': '', 'konga_args': '', }) config.add_section('kongautil.connect') config.add_section('kongautil.print_layout') config.read(files) host = config.get('kongautil.connect', 'host') or None try: port = int(config.get('kongautil.connect', 'port')) except: pass driver = config.get('kongautil.connect', 'driver') or None database = config.get('kongautil.connect', 'database') or None username = config.get('kongautil.connect', 'username') or None password = config.get('kongautil.connect', 'password') or None tenant_key = config.get('kongautil.connect', 'tenant_key') or None _konga_exe = config.get('kongautil.print_layout', 'konga_exe') or None _konga_args = config.get('kongautil.print_layout', 'konga_args') or None class ArgumentParser(argparse.ArgumentParser): def _print_message(self, message, file=None): pass def exit(self, status=0, message=None): if status: raise RuntimeError parser = ArgumentParser() parser.add_argument('--host', default=host) parser.add_argument('--port', type=int, default=port) parser.add_argument('--driver', default=driver) parser.add_argument('-d', '--database', default=database) parser.add_argument('-u', '--username', default=username) parser.add_argument('-p', '--password', default=password) parser.add_argument('-k', '--tenant-key', default=tenant_key) try: args = parser.parse_args() host = args.host port = args.port driver = args.driver database = args.database username = args.username password = args.password tenant_key = args.tenant_key except: pass if host is None: servers_list = client.list_servers(timeout=500) if servers_list: host = servers_list[0]['host'] port = servers_list[0]['port'] if host is not None: client.connect(host=host, port=port or 0, options={'tenant_key': tenant_key}) db_list = None if driver is None: if database is not None: db_list = client.list_databases(timeout=500) for driver, dbs in db_list.items(): if database in [db['name'] for db in dbs]: break else: driver = None if driver is None: drivers_list = client.list_drivers(timeout=500) if drivers_list: driver = drivers_list[0]['name'] if (driver is not None) and (database is None): if db_list is None: db_list = client.list_databases(driver, timeout=500) if db_list and (len(db_list[driver]) > 0): database = db_list[driver][0]['name'] if (driver is not None) and (database is not None): client.open_database(driver, database) client.authenticate(username or 'admin', password or '') _last_client = client return client raise kongalib.Error(kongalib.DATABASE_NOT_CONNECTED, "No database connected")
def message_box(text, title='', buttons=BUTTON_OK, icon=ICON_INFORMATION): """Mostra una finestra di dialogo modale con titolo *title*, messaggio *text* e icona *icon* (una delle costanti ``ICON_*``). La finestra mostrerà i bottoni identificati da *buttons*, che può contenere uno o più costanti ``BUTTON_*`` in *or* tra loro, e ritornerà la costante relativa al bottone selezionato dall'utente per chiudere la finestra.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.message_box(text, title, buttons, icon) else: print() if icon == ICON_WARNING: title = colorama.Fore.YELLOW + "WARNING" + ( ': ' if title else '') + colorama.Fore.RESET + (title or '') elif icon == ICON_ERROR: title = colorama.Fore.RED + "ERROR" + ( ': ' if title else '') + colorama.Fore.RESET + (title or '') if title: print(' ' + colorama.Style.BRIGHT + textwrap.fill(title, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) print() print(textwrap.fill(text, width=_get_term_width() - 1)) print() buttons_info = [ (BUTTON_OK, 'ok'), (BUTTON_YES, 'yes'), (BUTTON_YES_ALL, 'yes all'), (BUTTON_NO, 'no'), (BUTTON_NO_ALL, 'no all'), (BUTTON_CANCEL, 'cancel'), (BUTTON_OPEN, 'open'), (BUTTON_SAVE, 'save'), (BUTTON_SAVE_ALL, 'save_all'), (BUTTON_CLOSE, 'close'), (BUTTON_DISCARD, 'discard'), (BUTTON_APPLY, 'apply'), (BUTTON_RESET, 'reset'), (BUTTON_ABORT, 'abort'), (BUTTON_RETRY, 'retry'), (BUTTON_IGNORE, 'ignore'), ] buttons_map = {} labels = [] for bit, label in buttons_info: if buttons & bit: for i, c in enumerate(label): if c not in buttons_map: buttons_map[c] = bit labels.append('%s(%s)%s' % (label[:i], c, label[i + 1:])) break else: for c in string.ascii_letters: if c not in buttons_map: buttons_map[c] = bit labels.append('%s (%s)' % (label, c)) break answer = None while answer not in buttons_map: try: answer = input(', '.join(labels) + ': ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) if buttons & BUTTON_CANCEL: return BUTTON_CANCEL elif buttons & BUTTON_NO: return BUTTON_NO elif buttons & BUTTON_CLOSE: return BUTTON_CLOSE elif buttons & BUTTON_DISCARD: return BUTTON_DISCARD elif buttons & BUTTON_ABORT: return BUTTON_ABORT return buttons & ~(buttons - 1) return buttons_map[answer]
def resume_timeout(timeout): """Ripristina il timeout di esecuzione dello script. La funzione non comporta eccezioni ma non ha alcun effetto se eseguita al di fuori di Konga.""" if _proxy.is_valid(): _proxy.builtin.set_timeout(timeout, restore=False)
def set_timeout(timeout): """Imposta il timeout di esecuzione dello script in secondi, passati i quali verrà mostrata una finestra di avviso. La funzione non comporta eccezioni ma non ha alcun effetto se eseguita al di fuori di Konga.""" if _proxy.is_valid(): _proxy.builtin.set_timeout(timeout * 1000, restore=False)
def is_progress_aborted(): """Restituisce ``True`` se l'utente ha annullato la finestra di progresso precedentemente mostrata con :func:`kongaui.open_progress`.""" if _proxy.is_valid(): return _proxy.ui.is_progress_aborted() else: return False
def notify_data_changes(table_name, row_id=None): """Notifica Konga che uno o tutti i record di una tabella sono stati modificati, causando un aggiornamento a tutti quei client Konga che stanno operando su tali record. Se *row_id* è ``None``, tutti i record della tabella *table_name* verranno marcati come modificati, altrimenti il solo record con *ID* *row_id*. La funzione non comporta eccezioni ma non ha alcun effetto se eseguita al di fuori di Konga.""" if _proxy.is_valid(): _proxy.util.notify_data_changes(table_name, row_id)
def execute_form(form_data, title=None, message=None, condition=None): """Apre un form di immissione dati con titolo *title*; se *message* è specificato, il testo corrispondente sarà visualizzato in alto nella finestra del form. *form_data* deve essere una lista di ``dict`` con le specifiche dei campi da mostrare; nel ``dict`` di un singolo campo, l'unica chiave richiesta è ``name``, che deve identificare univocamente il nome del campo. E' possibile specificare l'etichetta da mostrare accando al campo stesso tramite la chiave ``label``; la tipologia di dato consentità è specificata tramite la chiave ``type``, che può assumere i valori: * ``str``: testo semplice, con possibile lunghezza massima ``length`` se la chiave è specificata; * ``password``: parola chiave; * ``int``: valore intero; * ``decimal``: valore decimale (:class:`kongalib.Decimal`); * ``range``: valore intero compreso tra un valore minimo (specificato dalla chiave ``min`` con valore predefinito ``0``) e un valore massimo (specificato dalla chiave ``max`` con valore predefinito ``100``); * ``slider``: simile a ``range`` ma viene visualizzato come cursore di selezione valore scorrevole; * ``bool``: valore booleano; * ``date``: data (``datetime.date``); * ``choice``: valore interno che identifica l'indice di una scelta tra quelle specificate nella chiave ``items`` (lista di stringhe); * ``listbox``: simile a ``choice`` ma viene visualizzato come lista di elementi da cui fare una scelta; * ``load``: nome di file esistente da caricare; * ``save``: nome di file da salvare; * ``dir``: nome di directory esistente; * ``code``: stringa di testo che identifica il codice di un record, la cui tabella è indicata dalla chiave ``table``; * ``company_code``: simile a ``code``, specifica l'azienda su cui gli altri campi ``code`` possono essere ricercati; * ``accounting_year_code``: simile a ``code``, specifica l'esercizio su cui gli altri campi ``code`` possono essere ricercati; Se presente, la chiave ``default`` permette di specificare il valore predefinito per un dato campo; inoltre se è presente la chiave ``focus`` (con qualsiasi valore), il campo corrispondente prenderà il focus all'avvio della finestra. Se l'utente annulla il form la funzione restituisce ``None``, altrimenti un ``dict`` le cui chiavi sono i nome dei campi e i valori i dati immessi dall'utente. Il parametro *condition*, se presente, permette di specificare una condizione di validazione per il form sotto forma di espressione Python; i nomi dei campi specificati in *form_data* saranno disponibili come variabili nell'esecuzione di questa condizione, il cui esito determinerà se consentire o meno l'uscita dal form con successo.""" if _proxy.is_valid(): with _TimeoutBlocker(): return _proxy.ui.execute_form(form_data, title, message, condition) else: import kongalib, decimal, datetime, getpass class InvalidInput(RuntimeError): pass if title: print(colorama.Style.BRIGHT + textwrap.fill(title, width=_get_term_width() - 1) + colorama.Style.RESET_ALL) if message: print(textwrap.fill(message, width=_get_term_width() - 1)) result = {} for entry in form_data: if not isinstance(entry, dict): raise RuntimeError("Expected dict as form data entry") if 'name' not in entry: raise RuntimeError( "Expected 'name' key in form data entry dict") name = str(entry['name']) label = str(entry.get('label', name)) prompt = input wtype = entry.get('type', str) if wtype in ('integer', 'int'): try: default = str(int(entry.get('default', 0))) except: default = '0' def validate(text): try: return int(text) except: raise InvalidInput('Expected integer number') elif wtype in ('decimal', kongalib.Decimal, decimal.Decimal): try: default = str(kongalib.Decimal(entry.get('default', 0))) except: default = str(kongalib.Decimal(0)) def validate(text): try: return kongalib.Decimal(text) except: raise InvalidInput('Expected decimal number') elif wtype in ('range', 'slider'): try: default = str(int(entry.get('default', 0))) except: default = '0' try: min_value = int(entry.get('min', 0)) except: min_value = 0 try: max_value = int(entry.get('max', 100)) except: max_value = 100 label += ' (%d-%d)' % (min_value, max_value) def validate(text): try: value = int(text) if (value < min_value) or (value > max_value): raise RuntimeError return value except: raise InvalidInput( 'Expected integer number between %d and %d' % (min_value, max_value)) elif wtype in ('bool', 'boolean', bool, 'check'): try: default = 'Y' if bool(entry.get('default', False)) else 'N' except: default = 'N' def validate(text): if text.lower() in ('t', 'true', 'y', 'yes', '1'): return True if text.lower() in ('f', 'false', 'n', 'no', '0'): return False raise InvalidInput('Expected boolean value') elif wtype in ('date', datetime.date): try: default = datetime.datetime.strptime( entry.get('default', datetime.date.today()), '%Y-%m-%d').date().isoformat() except: default = datetime.date.today().isoformat() def validate(text): try: return datetime.datetime.strptime(text, '%Y-%m-%d').date() except: raise InvalidInput('Expected iso date (YYYY-MM-DD)') elif wtype in ('choice', 'listbox', 'combobox'): items = entry.get('items', []) if (not isinstance(items, (tuple, list))) or (not all( [isinstance(item, basestring) for item in items])): raise RuntimeError( "Expected list of strings as 'items' value") print(label) for index, item in enumerate(items): print("%d) %s" % (index + 1, item)) label = 'Enter selection' try: default = str(int(entry.get('default', 0)) + 1) except: default = '1' def validate(text): try: value = int(text) if (value < 1) or (value > len(items)): raise RuntimeError return value - 1 except: raise InvalidInput( 'Expected integer number between %d and %d' % (1, len(items))) else: if wtype == 'password': prompt = getpass.getpass default = None else: try: default = str(entry.get('default', '')) except: default = '' try: length = int(entry.get('length', 0)) except: length = 0 def validate(text): if length and (len(text) > length): raise InvalidInput( 'String lengths exceeds maximum size of %d characters' % length) return text if default is not None: label += ' [%s]' % default while True: try: value = prompt(label + ': ') except KeyboardInterrupt: print(colorama.Fore.YELLOW + "aborted" + colorama.Fore.RESET) return None if (not value) and (default is not None): value = default try: value = validate(value) break except InvalidInput as e: print(colorama.Fore.RED + str(e) + colorama.Fore.RESET) result[name] = value if condition is not None: if not eval(condition, result.copy()): print(colorama.Style.BRIGHT + colorama.Fore.RED + "Form input data validation failed; aborted" + colorama.Style.RESET_ALL) result = None return result
def is_batch(): """Restituisce ``True`` se lo script è eseguito con Python da linea di comando, ``False`` se è eseguito dall'interno di Konga.""" return not _proxy.is_valid()
ICON_ERROR = 24 #: Icona di errore ICON_QUESTION = 25 #: Icona di domanda ICON_WARNING = 26 #: Icona di avviso ICON_INFORMATION = 27 #: Icona informativa def _shutdown(): try: _proxy.ui.shutdown() except: pass atexit.register(_shutdown) if not _proxy.is_valid(): colorama.init() def _get_term_width(): if PY3: import shutil return shutil.get_terminal_size(fallback=(80, 24))[0] else: # from https://stackoverflow.com/questions/566746/how-to-get-linux-console-window-width-in-python if sys.platform == 'win32': try: from ctypes import windll, create_string_buffer h = windll.kernel32.GetStdHandle(-12) csbi = create_string_buffer(22) res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)