Dive into Genropy: cosa dovrei sapere prima di tuffarmi

Perché Genropy?

Genropy nasce nel 2004, dall’idea di un team di Sviluppatori di Software gestionali che, avendo intuito le enormi potenzialità del mercato Web Application, ha deciso di portare in questo ambiente le proprie applicazioni.

Grazie alla sempre maggiore affidabilità di strumenti Open Source come Python, PostgreSQL e Javascript erano già disponibili ottimi strumenti di base, ma non esisteva ancora nulla che consentisse di creare in modo semplice e rapido le applicazioni web complesse e con GUI paragonabili a quelle di un software desktop.
La necessità era quindi quella di creare una nuova piattaforma per la creazione rapida di applicativi professionali su misura, che facilitasse l’approccio full stack grazie alla totale integrazione che esiste tra la parte di sviluppo back-end e quella front-end.

Nel corso di quindici anni di lavoro, il framework Genropy si è arricchito di strumenti così potenti da rendere la creazione di web application un processo estremamente veloce, in grado di consentire ad una sola persona o ad un piccolo team, di realizzare nel giro di pochissimo tempo un’applicazione funzionante, da poter poi personalizzare, arricchire e perfezionare.
Inoltre Genropy si è dimostrato sempre più in grado di scalare bene dalle applicazioni più semplici a quelle più complesse e mission critical, utilizzate quotidianamente in ambito aziendale da centinaia di utenti.

Crediamo quindi che Genropy possa rappresentare lo strumento ideale per gli sviluppatori che desiderano migrare in ambiente web o realizzare da zero applicazioni con sofisticate GUI, utilizzando un unico ambiente integrato, piuttosto che un insieme eterogeneo di diverse tecnologie.

A chi si rivolge il corso?

Il seminario è rivolto sia a sviluppatori professionisti che a appassionati del settore. In generale è pensato per tutti i curiosi che stanno incominciando o hanno intenzione di muovere i primi passi con Genropy.
Dal momento che Genropy vuole essere un framework per sviluppatori full stack (o aspiranti tali), daremo per assodata una conoscenza almeno basilare di Python, HTML, Javascript ed SQL.

Di cosa si parlerà?

Durante il seminario tratteremo i seguenti argomenti:

  • Veloce panoramica di tutte le parti di cui si compone il progetto Genropy
  • Spiegazione dei concetti fondanti e più peculiari del framework
  • Realizzazione guidata di una semplice web application attraverso una serie di esercizi per prendere confidenza con l’ORM di Genropy, la sintassi per la creazione della pagina e i component più usati.

Cosa dovrei fare per prepararmi al corso?

Il corso sarà prevalentemente di natura pratica, infatti chi vorrà potrà eseguire insieme a noi gli tutti gli esercizi proposti. Pertanto suggeriamo di portare con voi un computer portatile con già installato Genropy. Potete installarlo seguendo questa guida riportata sul sito del progetto.

Se doveste avere delle difficoltà o dei problemi durante l’installazione o la configurazione, potete rivolgerci domande sul gruppo Facebook oppure iscrivendovi alla Mailing List degli sviluppatori Genropy.

Come è organizzata la pausa pranzo?

Nel corso della giornata saranno offerti ai partecipanti un dolce coffee break ed un rinfresco durante la pausa pranzo, così avremo l’occasione per conoscerci e scambiare qualche chiacchiere durante questi momenti di relax e convivialità.

Come faccio ad iscrivermi al corso?

ATTENZIONE: Per partecipare al corso è necessario registrare la propria presenza tramite l’evento eventbrite entro il 16 Ottobre.

Dove si svolgerà il corso?

L’evento, organizzato con la collaborazione di Python Milano sarà ospitato da Mikamai e LinkMe.
Lo spazio si trova in a Milano in Via Venini 42, per raggiungere la sala occorre citofonare Mikamai, proseguire fino al portone e salire al primo piano.

Speriamo con questo articolo di avere risposto a tutti i vostri dubbi, se così non fosse non esitate a contattarci tramite il nostro sito o i nostri canali social.

Assaggi di Genropy

Questo è il primo di una serie di post che avranno lo scopo di illustrare attraverso dei semplici esempi pratici alcuni concetti fondamentali dello sviluppo con Genropy. Tutti questi articoli includeranno codice sorgente e la parte di pagina viva corrispondente: puoi passare dalla visualizzazione del codice e della pagina usando gli appositi bottoni SOURCE e DEMO.

In questo primo assaggio realizzerò la schermata iniziale di un ipotetico gioco in cui i giocatori dovranno inserire il proprio nome e scegliere il loro colore preferito. Con questo esempio voglio spiegare i seguenti concetti:

  • Il datastore
  • Il puntatore dinamico e i triggering path
  • I path gerarchici e relativi

Per approfondimenti e dettagli ti suggerisco di consultare anche il nostro corso base di Genropy.

Incominciamo

Una pagina di Genropy viene costruita dinamicamente sul browser da un engine Javascript che si occupa, tra le molte cose, di conservare uno spazio di memoria riservato per contenere i dati della pagina. Questo spazio si definisce datastore e ad esso accede in lettura e scrittura attraverso dei path separati da punti. Vediamo ora concretamente cosa significa.

Innanzitutto ho creato un box arrotondato (variabile player_box) dentro il quale aggiungiamo un elemento formbuilder, che serve a definire dei campi con etichetta allineati automaticamente (variabile pl_form).

In questa form definisco per il momento solo un campo di tipo textbox e nel suo parametro value mi riferisco al path gerarchico player.name. Esso indica l’indirizzo all’interno del datastore in cui verrà scritto il valore inserito nel campo. Infine aggiungo un semplice div, il cui valore di innerHTML si riferisce al medesimo path a player.name.

Avrai notato il carattere ^ che precede i path sia nel valore del campo che nel contenuto del div. Questo carattere viene chiamato puntatore dinamico o triggering pointer. Esso sta ad indicare che il path è monitorato da un gestore di eventi della pagina, il quale si occuperà di aggiornare automaticamente tutti gli elementi del DOM al variare dei loro attributi nel datastore.

Nel nostro caso fa sì che quando cambiamo il valore nel campo, il div aggiorni il suo contenuto. Definiamo quindi un path preceduto da un puntatore dinamico con il termine triggering path.

Colore dinamico

Qualsiasi parametro di un elemento di pagina può essere reso dinamico, basta che anziché avere un valore hardcoded, contenga un puntatore ad un path del datastore. Per dimostrarlo ho aggiunto nel secondo esempio un campo per selezionare il colore preferito del giocatore, il quale andrà scritto al path player.color. Come campo utilizziamo il widget filteringSelect, il quale presenta un menu con tutte le opzioni selezionabili definite dal parametro values.

A scopo estetico ho spostato il div con nome del giocatore in cima al riquadro, in modo da formare una sorta di barra titolo. Noterai che sia il border_color del riquadro, che lo sfondo border_color del titolo si riferiscono ora al path player.color. E poiché voglio che la pagina venga servita con la barra del titolo già colorata, uso l’elemento data per inizializzare il valore di player.color con il valore gray.

Aggiungiamo il secondo giocatore

In questo terzo esempio ho aggiunto un riquadro per il secondo giocatore. Questo mi offre l’opportunità di parlarti dei path relativi e del perché è molto comodo che i path del datastore siano gerarchici. Noterai infatti che, anziché replicare tutto il codice relativo al box del giocatore, ho definito un metodo playerBox il quale effettua tutte le operazioni mostrate nell’esempio precedente. Tale metodo riceve come solo parametro container, ovvero un elemento contenitore che verrà riempito dal metodo stesso. Nel nostro caso si tratta di un div vuoto.

Noterai che questa volta anziché riferirmi ai path completiplayer.name e player.color, ho usato solamente .name e .color. Tutti i path che iniziano con il punto sono detti path relativi , ovvero espressi a partire da un path di riferimento, precedentemente definito su un elemento contenitore.

Questo path di riferimento si chiama datapath e notiamo infatti che viene messo come parametro dei due div che andranno a contenere i box, rispettivamente con valore player_one e player_two. Il vantaggio di usare path gerarchici e relativi è che mi permettono di avere i dati dei due giocatori separati in due diversi rami del datastore, potendo però replicare tutta la parte di grafica e di logica raccogliendola a fattore comune in un solo metodo.

Inneschiamo un elemento di script

In questo ultimo esempio ho aggiunto un semplice script che viene eseguito ogni volta che uno dei due nomi viene modificato e se entrambi i nomi sono stati inseriti manda a video un messaggio di benvenuto.

Prova a riempire il campo nome di entrambi i giocatori.

Per fare questo ho definito un elemento dataController, il quale serve a lanciare l’esecuzione dello script Javascript che trovi come primo parametro.

Lo script ha nel suo scopo due variabili p1 e p2, le quali sono definite come parametri del dataController e lette ai path assolutiplayer_one.name e player_two.name. Il solo fatto di riferirsi a questi path con il puntatore ^ fa sì che ogni volta che uno dei nomi viene modificato si inneschi l’esecuzione dello script passando ad esso i valori come variabili nello scope dello script.

Fino a questo esempio avevamo visto come i triggering path provocassero l’aggiornamento istantaneo degli elementi di DOM al variare dei valori loro attributi, ora abbiamo visto che possono innescare script sul client e persino chiamate a funzioni remote sul server, passando ad essi i valori letti come variabili vere e proprie da utilizzare.

Se hai domande o appunti ti invito a partecipare alla discussione nei commenti oppure sui nostri canali social.

Ti aspetto al prossimo assaggio!

Come creare velocemente report su dati aggregati

Quando si desidera effettuare un’analisi su dati aggregati, l’operazione che l’utente finale di solito svolge è esportare le selezioni su un foglio elettronico per elaborarli in una seconda fase, effettuando così molti passaggi; oppure richiede allo sviluppatore di creare un report personalizzato in base alle sue necessità.

Con Genropy, invece, l’utente potrà creare viste di dati aggregati con diversi livelli di raggruppamento e totalizzazione degli importi numerici. Lo strumento offre la possibilità di decidere quali siano le colonne di raggruppamento, semplicemente trascinandole dall’editor della vista.
Lo strumento inoltre offre diverse modalità di visualizzazione, ovvero:

  • a griglia semplice
  • ad albero gerarchico, con subtotali ai diversi livelli di aggregazione
  • con raffronto di colonne, sia su griglia piatta che su albero

Nel seguente video vedremo, dalla nostra semplice applicazione demo, come sia possibile creare rapidamente dei report di vendita, aggregando ad esempio i dati della tabella delle righe fattura.

Risalendo ai dati dei clienti verranno creati raggruppamenti per zona geografica, per provincia. Mentre basandoci sull’anno della fattura potremo fare dei confronti anno su anno delle vendite nelle varie zone geografiche.

Per attivare i sottotitoli premere il bottone CC


Come fare una query su una table in Genropy

A volte capita di dover ricercare dei record da una tabella, utilizzando un criterio particolare che non era stato previsto dagli autori dell’applicativo.

In Genropy si può sempre offrire all’utente finale un modo semplice e rapidissimo per creare qualunque tipo di query si desideri.

Entriamo nel dettaglio

Normalmente le pagine che gestiscono tabelle di database, presentano strumenti di interrogazione piuttosto elementari e poco flessibili. Di solito è possibile porre semplici condizioni sui campi più significativi: ad esempio la ragione sociale per una tabella anagrafica, oppure il campo data per una tabella di operazione o un codice progressivo sulla tabella prodotto.

Questo approccio toglie all’utente gran parte dell’espressività che sarebbe invece offerta dalle Query SQL.
Vedremo in questi brevi filmati lo strumento messo a disposizione da Genropy per creare Query personalizzate in pochi click. In particolare mostreremo alcune caratteristiche:

  • Possibilità di porre condizioni su campi appartenenti a tabelle in relazione, senza doversi occupare delle JOIN
  • Possibilità di usare tutti gli operatori di raffronto offerti da SQL
  • Possibilità di sovrapporre più condizioni usando operatori logici ed eventualmente parentesi
  • Possibilità di salvare la Query per rieseguirla in futuro

Query Semplici e Query Complesse

Per attivare i sottotitoli premere il bottone CC

Per maggiori dettagli su come creare e salvare qualunque tipo di query in Genropy clicca qui.

OUT OF THE BOX

Quando nel 2006 abbiamo deciso di portare le nostre applicazioni in ambito web ci siamo resi conto che non esisteva nulla di idoneo a fornire nel browser la stessa esperienza d’uso e la stessa interattività cui erano abituati i nostri utenti nelle loro applicazioni desktop.

Non trovando nulla di pronto abbiamo dovuto pensare a come ottenere un framework web che fosse facile da usare e in grado di produrre in tempi rapidi applicazioni complesse, manutenibili, personalizzabili e che dessero agli utenti la stessa esperienza d’uso cui erano abituati.

Dovevamo insomma pensare fuori dagli schemi, ed inventarci l’uno dopo l’altro gli strumenti con cui costrire il nostro framework.

Per questa ragione abbiamo deciso di intitolare “Out of the box” questa serie di articoli in cui esamineremo proprio quelle idee che rendono Genropy così diverso dagli altri strumenti normalmente usati per fare applicativi web.

E partiamo dalle Bag, il contenitore di dati che costituisce l’ossatura portante di Genropy.

Bag

Gerarchico è meglio

Genropy usa un paradigma data driven per descrivere sotto forma di strutture di dati le varie parti dell’applicazione lasciando poi al framework il compito di interpretare questi dati per trasformarli in codice funzionante.

Queste strutture, chiamate Bag, possono essere immaginate come dei contenitori a cui si accede tramite un percorso gerarchico usando API disponibili sia in Python che in Javascript. In questo modo lo sviluppatore usa sia lato server che lato client lo stesso paradigma agevolando non solo lo sviluppo dell’applicativo ma anche il trasferimento dei dati.

Senza entrare in dettagli tecnici possiamo dire che le Bag rappresentano l’ossatura di tutto Genropy e quindi per capire come funziona il framework è necessario darne almeno una breve descrizione.

Vediamo quindi un esempio di Bag in Python:

 >>>from gnr.core.gnrbag import Bag
 >>>mybag=Bag()
 >>>mybag.setItem('alfa.beta.name','John')
 >>>mybag.setItem('alfa.beta.age', 34)
 >>>mybag.getItem('alfa.beta.name')
'John'
 >>> mybag.getItem('alfa.beta').keys()
 ['name', 'age']

Il modo migliore per vedere il contenuto di una bag è quello di chiederne la rappresentazione XML, tramite il metodo toXml:

>>> print(mybag.toXml()
    <?xml version="1.0" encoding="utf-8"?>
    <GenRoBag>
          <alfa>
                 <beta>
                    <name>John</name>
                    <age _T="L">34</age>
                 </beta>
          </alfa>
    </GenRoBag>
>>>

Vediamo ora lo stesso esempio in Javascript:

var mybag=new gnr.GnrBag
mybag.setItem('alfa.beta.name','John')
mybag.setItem('alfa.beta.age', 34)
mybag.getItem('alfa.beta.name')
"John"
mybag.getItem('alfa.beta').keys()
["name", "age"]

mybag.toXml()
"<?xml version="1.0" encoding="utf-8"?>
 <GenRoBag>
   <alfa>
     <beta>
        <name>John</name>
        <age _T="L">34</age>
     </beta>
   </alfa>
 </GenRoBag>"

Nella versione Python i comandi setItem e getItem possono essere rimpiazzati da una sintassi con parentesi quadre:

>>>mybag['alfa.beta.age'] = 34
>>>print (mybag['alfa.beta.age'])
34
>>>

Una Bag è composta da “nodi di bag”: BagNode. Un nodo ha un’ etichetta che lo identifica, un valore e può anche avere degli attributi. Gli attributi possono essere inseriti usando la setItem, come parametri nominati aggiuntivi.

Ad esempio aggiungiamo il campo indirizzo di cui diamo anche latitudine e longitudine come attributi:

>>>mybag.setItem('alfa.beta.indirizzo','Via Roma 13',
                       latitudine=45.4456761,
                       longitudine=9.0906981)

Ma possiamo anche accedere ad essi in modo diretto attraverso i metodi setAttr e getAttr

>>>mybag.setAttr('alfa.beta.indirizzo', piano=4, interno=21)
>>>mybag.getAttr('alfa.beta.indirizzo', 'latitudine')
45.4456761
>>>

Possiamo leggere gli attributi aggiungendo al percorso gerarchico del nodo a cui si trovano, il nome attributo dopo il carattere ‘?’. Questo path può essere utilizzato dal metodo getItem oppure, limitatamente a Python nella sintassi con le parentesi quadre:

>>>mybag.getItem('alfa.beta.indirizzo?latitudine')
45.4456761
>>>mybag['alfa.beta.indirizzo?longitudine']
9.0906981

Quando creiamo una bag possiamo inizializzarne il contenuto, ad esempio da un XML:

>>>mybagxml=mybag.toXml()
>>>newbag=Bag(mybagxml)

In questo caso avremo una nuova bag che avrà lo stesso contenuto di mybag.

Possiamo anche inizializzare una Bag da un file del filesystem:

>>>mybag.toXml('mybag.xml')
>>>newbag=Bag(('mybag.xml')

In questo caso il primo comando avrà scritto nel file system il documento ‘mybag.xml’ e il secondo avrà popolato newbag a partire dallo stesso documento.

Quindi le bag possono essere serializzate e de-serializzate facilmente con i comandi toXml e fromXml ma il contenuto, a differenza di un xml standard, risulta tipizzato. Quindi se salveremo un intero (ad esempio age) troveremo nell’xml il tipo e la rilettura ripristinerà il tipo desiderato.

Una bag come XML può facilmente essere salvata nel file system, in un campo di database, oppure ricevuta o spedita tramite una chiamata di rete.

Una bag può essere anche popolata da un URL, ad esempio accedendo ad un servizio di rete (in questo caso si tratta di openweathermap):

   >>>tempo_a_milano=Bag("http://api.openweathermap.org/data/2.5/weatherappid=mysecretappid&q=milano&mode=xml&units=metric")

>>>print (tempo_a_milano)
 0 - (Bag) current:
 0 - (Bag) city: <id='6542283' name='Milan'>
     0 - (str) coord:   <lat='45.47' lon='9.19'>
     1 - (unicode) country: IT
     2 - (str) sun:   <rise='2019-04-25T04:21:24' set='2019-04-25T18:20:49'>
 1 - (str) temperature:   <max='18.89' unit='celsius' value='16.26' min='14.44'>
 2 - (str) humidity:   <unit='%' value='87'>
 3 - (str) pressure:   <unit='hPa' value='1019'>
 4 - (Bag) wind:
     0 - (str) speed:   <name='Gentle Breeze' value='3.6'>
     1 - (str) gusts:
     2 - (str) direction:   <code='ESE' name='East-southeast' value='120'>
 5 - (str) clouds:   <name='broken clouds' value='75'>
 6 - (str) visibility:   <value='7000'>
 7 - (str) precipitation:   <unit='1h' mode='rain' value='1.52'>
 8 - (str) weather:   <number='501' value='moderate rain' icon='10d'>
 9 - (str) lastupdate:   <value='2019-04-25T10:55:07'>

Da questo esempio notiamo che nella Bag oltre ai valori sono presenti degli attributi. Ad esempio:

temperature:   <max='18.89' unit='celsius' value='16.26' min='14.44'>

Quindi nel nodo di Bag temperature il valore è None ma sono definiti 4 attributi ovvero max, min, unit, value.

Prendiamo ad esempio la temperatura a Milano:

>>>print (tempo_a_milano['current.temperature?value'])
16.26

La funzione digest consente di estrarre secondo un’opportuna sintassi elementi dalla bag in modo da facilitare le elaborazioni successive.

Prendiamo, ad esempio, con la funzione digest tutte le grandezze disponibili con relativa unità di misura:

>>>tempo_a_milano['current'].digest('#k,#a.unit, #a.value')
[(u'city', None, None),
 (u'temperature', u'celsius', u'16.26'),
 (u'humidity', u'%', u'87'),
 (u'pressure', u'hPa', u'1019'),
 (u'wind', None, None),
 (u'clouds', None, u'75'),
 (u'visibility', None, u'7000'),
 (u'precipitation', u'1h', u'1.52'),
 (u'weather', None, u'moderate rain'),
 (u'lastupdate', None, u'2019-04-25T10:55:07')]

Possiamo accedere in lettura ad un elemento della Bag anche dalla posizione tramite il simbolo # seguito dall’indice in base 0.

Ad esempio:

>>>print(mybag['alfa.#0.#1'])
34

Una bag di norma può essere navigata solo in senso discendente ma tramite il comando setBackRef possiamo abilitare la navigazione ascendente con il segmento speciale ‘#^’.

Ad esempio:

>>>mybag.setBackRef()
>>>mybag['alfa.gamma.color']='red'

>>>beta=mybag['alfa.beta']

>>>print (beta['name'])
John

>>>print (beta['#^.gamma.color'])
red

Il segmento speciale ‘#^’ invece di seguire un ramo discendente della Bag risale al nodo superiore. Usando percorsi misti possiamo navigare la Bag in qualunque modo.

Senza ulteriormente dilungarci con esempi possiamo dire che le Bag sono quindi un ottimo contenitore gerarchico che consente di annidare informazioni in modo facile ed intuitivo fornendo della API coerenti per l’estrazione di dati e anche per il caricamento da fonti esterne.

Resolver

Dinamico è meglio

Uno dei vantaggi di una bag è quello ammettere come contenuto di un nodo non solo dei dati ma anche degli oggetti (chiamati resolver) in grado di recuperare o calcolare il valore nel momento in cui viene richiesto.

Supponiamo di voler creare una bag che ci possa dare le condizioni metereologiche sempre aggiornate. Per prima cosa creiamo una classe resolver

from gnr.core.gnrbag import BagResolver
class MeteoResolver(BagResolver):
    apikey='mysecretkey'
    url="http://api.openweathermap.org/data/2.5/weather?appid=%(apikey)s&q=%(city)s&mode=xml&units=metric"

    def load(self):
        return Bag(self.url%dict(apikey=self.apikey,city=self.city))['current']

E infine creiamo una bag dove prepariamo dei resolver per le città desiderate:

>>>meteo=Bag()
>>>for city in ('milano','torino','roma','firenze','como'):
...     meteo[city]=MeteoResolver(city=city)

>>>meteo.keys()
['milano', 'torino', 'roma', 'firenze', 'como']

In questo momento non abbiamo ancora fatto alcuna chiamata per leggere i dati e potremmo creare un gran numero di nodi senza avere rallentamenti dovuti alle richieste di rete.

Inoltre nel codice non è necessario fare alcuna chiamata specifica: basta infatti leggere il dato dalla bag per averlo in modo completamente trasparente. Ad esempio per conoscere la temperatura di Torino scriveremo:

>>>print (meteo['milano.temperature?value'])
15.37

Ogni volta che accederemo ad elementi interni scatteranno i resolver necessari e avremo sempre dei dati aggiornati.

Usando digest vediamo tutte le temperature:

>>>meteo.digest('#k,#v.temperature?value')
[('milano', u'15.41'), ('torino', u'13.61'), ('roma', u'20.34'), ('firenze', u'17.93'), ('como', u'14.82')]

Ogni volta che accediamo a dei valori interni ad un resolver genereremo una nuova chiamata di rete che reperirà nuovamente i dati. Nella maggioranza dei casi però non ci serve avere valori così aggiornati e quindi possiamo avvalerci di una funzionalità di cache offerta dai resolver.

Modifichiamo quindi la nostra bag meteo aggiungendo il parametro cacheTime:

>>>meteo=Bag()
>>>for city in ('milano','torino','roma','firenze','como'):
...     meteo[city]=MeteoResolver(city=city,cacheTime=3600)
>>>

In questo caso una volta letto un valore per una città, tutti i dati della “sotto-bag” relativa saranno considerati validi per 3600 secondi e quindi i dati meteo di ogni città saranno come massimo vecchi di un’ora. Avremo però evitato continue chiamate di rete per accedere al servizio esterno.

Trigger

Essere avvisati è meglio

Una bag può essere anche vista come un contenitore in grado di eseguire delle azioni ogni volta che viene cambiato il contenuto.

Questa funzionalità si attiva con il comando subscribe che può essere messo sia alla bag di radice che in qualunque sotto-bag.

Le sottoscrizioni possono essere relative a specifici eventi (insert, update, delete) oppure relative a tutti (any). Inoltre alla stessa bag o sotto-bag possiamo aggiungere varie sottoscrizioni con nomi diversi.

Vediamo subito un esempio. Ci proponiamo di farci mostrare tutti gli eventi che accadono in una bag.

Creiamo innanzitutto una funzione logEvents:

>>>def logEvents(**kwargs):
...     print (kwargs)

>>>mybag=Bag()
>>>mybag.subscribe('mylogger',any=logEvents)

>>>mybag['alfa']='foo'
{'node': BagNode : alfa at 4424525520, 'ind': 0, 'reason': None, 'evt': 'ins', 'pathlist': []}

>>>mybag['alfa']='bar']
{'node': BagNode : alfa at 4424525520, 'reason': None, 'evt': 'upd_value', 'pathlist': ['alfa'], 'oldvalue': 'foo'}

mybag.setAttr('alfa',color='red',size=67)
{'node': BagNode : alfa at 4424525520, 'reason': True, 'evt': 'upd_attrs', 'pathlist': ['alfa'], 'oldvalue': None}

Proviamo ora ad inserire in un path annidato:

>>>mybag['this.is.a.nested.bag']=456
{'node': BagNode : this at 4424493328, 'ind': 1, 'reason': 'autocreate', 'evt': 'ins', 'pathlist': []}
{'node': BagNode : is at 4424491472, 'ind': 0, 'reason': 'autocreate', 'evt': 'ins', 'pathlist': ['this']}
{'node': BagNode : a at 4424491856, 'ind': 0, 'reason': 'autocreate', 'evt': 'ins', 'pathlist': ['this', 'is']}
{'node': BagNode : nested at 4424491152, 'ind': 0, 'reason': 'autocreate', 'evt': 'ins', 'pathlist': ['this', 'is', 'a']}
{'node': BagNode : bag at 4424491408, 'ind': 0, 'reason': None, 'evt': 'ins', 'pathlist': ['this', 'is', 'a', 'nested']}

Vediamo quindi che la nostra funzione di logEvent è chiamata ad ogni inserimento e ci fornisce gli elementi necessari per svolgere i compiti desiderati.

Conclusione

Le Bag si sono dimostrate uno strumento estremamente versatile perchè ci hanno consentito usare un unico modello concettuale sia nella definizione del database che nella creazione della GUI, delle stampe e in moltissime altre aree del framework.

Il loro uso però non è limitato a Genropy e crediamo che anche in altri contesti potrebbero rivelarsi uno strumento molto utile ad affrontare anche problematiche diverse.

Come personalizzare le viste su una tabella in un attimo


Quante volte, nella vista di una tabella, manca proprio la colonna di cui avevamo bisogno?

Grazie a Genropy ogni griglia può essere personalizzata in pochi secondi direttamente da interfaccia utente. Semplice a dirsi, ma vediamo come.

Supponiamo di dover esportare dalla tabella Clienti un foglio di calcolo che riporti i seguiti campi:

Ragione sociale, Provincia, Email, Tipo Cliente, Tipo Pagamento, Numero di Fatture, Totale fatturato.

Genropy ti permette di modificare le colonne di una griglia in pochi attimi. Basterà aprire il cassetto laterale di configurazione per entrare in modalità modifica ed aggiungere colonne o rimuovere quelle che non ci servono. Infine si potrà salvare la nuova vista per utilizzi futuri.

Per attivare i sottotitoli premere il bottone CC

Per maggiori dettagli su come funzionano le griglie in Genropy clicca qui.

Tutti al PyCon!

Anche quest’anno saremo presenti al PyCon Italia, la conferenza nazionale che raccoglie professionisti, ricercatori e appassionati dell’universo Python.

Siamo orgogliosi di partecipare ed essere sponsor silver della 10° edizione di uno degli eventi più importanti del nostro settore.

Ci vediamo nella bellissima Firenze, dal 2 al 5 Maggio, per raccontarvi tutte le potenzialità e i vantaggi dell’utilizzo del nostro framework Genropy; vantaggi che offre sia a sviluppatori che desiderino realizzare applicativi web, programmando in Python, sia all’utente finale.

Se vuoi scoprire tutti i numerosi benefici del nostro framework open source non puoi mancare all’appuntamento. Ti aspettiamo e siamo pronti a rispondere a ogni quesito e dimostrare le vere capacità di Genropy.

In ultimo, ma non meno importante, saremo presenti con una recruiting session per chiunque fosse interessato a collaborare con noi.