Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Relatore:
Prof. Matteo Baldoni
Candidato:
Davide Ferrero
Indice
3 Il cloud computing 11
3.1 Salesforce.com . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3.2 Amazon Web Services . . . . . . . . . . . . . . . . . . . . . . 14
I
INDICE II
5.6.2 Libri . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.6.3 Stanze . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
5.7 Schema della base dati . . . . . . . . . . . . . . . . . . . . . . 32
5.8 Presentazione degli algoritmi utilizzati . . . . . . . . . . . . . 34
5.8.1 Ricerca . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.8.2 Estrazione dei tag da un titolo . . . . . . . . . . . . . . 34
5.8.3 Ricerca dei tag correlati . . . . . . . . . . . . . . . . . 35
5.8.4 Visualizzazione delle stanze . . . . . . . . . . . . . . . 35
5.9 Considerazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Appendice A 37
Il file main.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Il file libri.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Il file stanze.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Codice javascript per Google Maps . . . . . . . . . . . . . . . . . . 67
Il file di configurazione app.yaml . . . . . . . . . . . . . . . . . . . 68
Bibliografia e sitografia 70
Capitolo 1
1
CAPITOLO 1. APP ENGINE SDK 2
come python, senza che questo venga abbinato ad altri linguaggi, per creare
e interagire con pagine web. Tutto ciò abbinato al poter sfruttare i server
google per le proprie applicazioni utilizzando sistemi distribuiti, ma trattan-
doli come sistemi standard e tralasciando gli oneri delle varie configurazioni
e precauzioni, di cui un creatore di siti web deve prendersi carico (progetta-
zione e creazione della base dati in primis).
Con questo kit google rende molto più semplice, intuitiva e veloce la scrittura
di applicazioni web, permette alta scalabilità e offre strumenti di ammini-
strazione utili e funzionali grazie al pannello di controllo disponibile per ogni
applicazione.
user = users.get_current_user()
if user:
print "Benvenuto, "+user.email()+"!"
print "<a href=\"users.create_logout_url("/")\">Esci</a>"
if users.is_current_user_admin():
print "<a href=\"/admin/\">Admin area</a>"
3
CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 4
else:
print "<a href=\"%s\">Accedi o registrati</a>"
%users.create_login_url("/")
2.2 Images
Molte applicazioni necessitano, per diversi motivi, di manipolare delle imma-
gini. App Engine mette a disposizione le stesse funzionalità usate per Picasa,
il web album di Google.
È infatti possibile ridimensionare, ruotare, ritagliare, riflettere (orizzontal-
mente e verticalmente), selezionare un riquadro più ristretto dell’immagine
e addirittura usare la funzione di miglioramente automatico. Quest’ultima
aggiusta colore, luminosità e saturazione dell’immagine.
2.3 Mail
In alcuni casi risulta utile poter contattare un utente della propria web appli-
cation, che sia per comunicazioni generali o per mettere in comunicazione due
o più utenti tra loro. All’interno dell’SDK vi è la funzione mail.send_mail()
che serve proprio a questi scopi. La funzione riceve come parametri in ingres-
so il mittente, il destinatario, l’oggetto e il corpo del messaggio da inviare.
È anche possibile aggiungere allegati al messaggio di posta elettronica da
CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 5
inviare, basterà passare come parametri della funzione, i file che si voglio-
no allegare, e questi verrano inclusi nel messaggio e spediti. Bisogna però
rispettare un elenco di tipi file includibili:
MIME Type Estensione del file
image/x-ms-bmp bmp
text/css css
text/comma-separated-values csv
image/gif gif
text/html htm html
image/jpeg jpeg jpg jpe
application/pdf pdf
image/png png
application/rss+xml rss
text/plain text txt asc diff pot
image/tiff tiff tif
image/vnd.wap.wbmp wbmp
2.4 Memcache
La maggior parte delle applicazioni web che mirano ad avere alte prestazioni
e a gestire grandi quantità di dati, spesso ricorrono all’utilizzo di un servizio
di memorizzazione degli stessi all’interno della cache di un server, in modo
tale da servirli con assoluta immediatezza al momento della loro richiesta e,
soprattutto, senza dover effettuare ogni volta una nuova lettura dal database.
Una cosa importante di cui prendere atto è che questo servizio perde la sua
utilità nel momento in cui i dati sul database cambiano. In questo caso al-
la successiva richiesta, bisognerà provvedere a rieseguire la query e salvarli
nuovamente nella cache del server.
Il servizio di memcache può anche essere usato per caricare valori temporanei
e non accetta necessariamente solo dati letti dal db. Si può usare tranquilla-
mente in alternativa alle variabili di sessione, col vantaggio di non rischiare
che chiunque possa accendere a contenuti riservati, al contrario di quanto
può succedere servendosi delle variabili di sessione. Inoltre queste ultime, nel
caso l’utente non abbia i cookies attivati, non funzionerebbero, al contrario
della memcache che può essere utilizzata indipendentemente dall’attivazione
o meno di questi.
Inoltre il servizio di salvataggio in cache e di reperimento delle chiavi salvate,
può essere utilizzato contemporaneamente da più istanze di una applicazio-
ne, e le chiavi salvate possono essere cancellate da codice, dall’interfaccia di
amministrazione dell’applicazione o possono avere una scadenza settata al
CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 6
greetings = memcache.get("greetings")
if greetings is not None:
return greetings
else:
greetings = self.render_greetings()
if not memcache.add("greetings", greetings, 10):
logging.error("Memcache set failed.")
return greetings
2.5 Datastore
Il datastore di App Engine fornisce un’interprete di query e una memorizza-
zione dei dati tramite transazioni. Il tutto lavora sempre nei sistemi scalabili
di Google. L’interfaccia python fornisce anche uno strumento di modelliz-
zazione dei dati e un linguaggio sql-like chiamato gql1 . Questo datastore
consente di effettuare qualsiasi tipo di operazione su oggetti di dati, i quali
rappresentano le comuni entità. Un’entità possiede una o più proprietà, le
quali rappresentano i valori delle entity e possono appartenere ad una vasta
tipologia, a scelta tra i tipi supportati.
1
L’acronimo sta per Generic Query Language
CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 7
class Accumulator(db.Model):
counter = db.IntegerProperty()
db.run_in_transaction(increment_counter, acc.key(), 5)
In una singola transazione, un’applicazione può creare o modificare un’entità
al massimo una sola volta, e non può eseguire query standard o query gql.
Per ottenere un’istanza di una entity si può solamente utilizzare la funzione
get, la quale prende come parametro di input la chiave dell’oggetto deside-
rato. Come detto, ad un’entità viene associata una chiave, che la identifica
univocamente. Questa key può essere recuperata mediante l’apposita funzio-
ne implicita, presente in ogni oggetto letto da db2 .
Un esempio del codice da scrivere per modellare un’entità è il seguente:
class Book(db.Model):
title=db.StringProperty()
author=db.StringProperty()
version=db.IntegerProperty()
year=db.IntegerProperty()
img=db.BlobProperty(required=False)
class SellBook(db.Model):
book=db.ReferenceProperty(Book)
user=db.UserProperty()
price=db.FloatProperty()
dataSell=db.DateTimeProperty(auto_now_add=True)
location=db.GeoPtProperty(required=False)
È inoltre possibile definire classi-entità gerarchiche tramite le Polymorphic
Models API, e quindi avere una o più sottoclassi di una superclasse. Tutto
ciò ovviamente supportato dal fatto che le query supportano e riconoscono le
2
La funzione a cui ci si riferisce è entity.key(), dove con entity si vuole indicare il nome
della variabile rappresentante l’istanza di una determinata entità.
CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 9
• Datastore
• Url Fetch
• Memcache
• Funzionamento dell’applicazione
Il cloud computing
3.1 Salesforce.com
Saleforce è un’azienda americana leader nel settore dei CRM (Customer Rela-
tionship Management), ovvero nei servizi di fidelizzazione del cliente. Questa
società mette a disposizione diversi servizi per le aziende che vogliono crescere
1
Software visti come servizio, o comunemente detti in gergo SaaS.
11
CAPITOLO 3. IL CLOUD COMPUTING 12
nel mercato. Il CRM le aiuta nella gestione dei propri clienti, nella gestione
dei rapporti con essi e nell’acquisizione di nuovi potenziali acquirenti.
Salesforce.com è stata fondata nel 1999 e nel 2008 è stata riconosciuta come
la 43esima azienda più grande al mondo tra le compagnie di sofware. Conta
filiali in diverse parti del mondo, come Dublino, Singapore, Tokyo, Toronto,
New York, Londra, Sydney oltre alla sede principale di San Mateo in Cali-
fornia.
Nel giugno 2004 è stata quotata in borsa alla New York stock exchange (NY-
SE).
Questa società punta a fornire strumenti online che sostiuiscano i vari soft-
ware di cui un’azienda ha bisogno e in particolare, fornisce servizi che consen-
tono di automatizzare i processi di business che prevedono il contatto diretto
con il cliente, migliorare la conoscenza del cliente attraverso l’estrazione dei
dati dal processo precedente e utilizzare tecnologie integrate per la comuni-
cazione con il cliente (telefono, fax e e-mail). Tutto ciò viene fornito senza
l’installazione di software nelle aziende che si affidano a loro, bensì garanten-
do servizi online, che sfruttano il cloud computing, e permettono alle varie
società di caricare le loro applicazioni sui server di salesforce e utilizzarle
online.
CAPITOLO 3. IL CLOUD COMPUTING 13
4.1 Introduzione
Bigtable ([10]) è un Database Management System (DBMS) distribuito, co-
struito per lavorare con dati strutturati. Esso è nato nel 2004 nei laboratori
di Google ed è stato disegnato per essere altamente scalabile e per supportare
grandi quantità di dati1 .
Più di sessanta progetti di Google salvano i dati tramite questo DBMS, in-
clusi l’indicizzazione del web, Google Earth, Google Finance, Orkut e Google
Docs, per citarne alcuni. Queste applicazioni effettuano numerosi tipi diffe-
renti di richieste, sia in termini di dimensione dei dati (che possono variare
da semplici URLs a intere pagine web fino ad arrivare ad immagini prove-
nienti dai satelliti) sia in termini di elaborazione vera e propria dei dati (dalla
fornitura di dati in real-time alla rielaborazione di una grande mole di dati).
Nonostante questa ampia varietà di esigenze, il sistema ha fornito un’ottima
soluzione, molto flessibile e dalle alte prestazioni per ognuno di questi pro-
dotti di Google. Oltre alle qualità già menzionate, gli sviluppatori dei vari
progetti sopra citati, hanno riscontrato una altissima affidabilità di questo
DBMS. I clusters di Bigtable sono suddivisi in qualche migliaio di servers e
possono immagazzinare diverse centinaia di terabyte di dati. Questo sistema
combina insieme diverse strategie utilizzate nei comuni database. Parallel
databases e main-memory databases sono alcuni esempi di tipologie di data-
base dalle alte prestazioni e che consentono una alta scalabilità, Bigtable però
utilizza solo alcune delle caratteristiche di queste tipologie di DBMS. Esso
non supporta un modello relazionale completo, ma fornisce i dati ai client
seguendo un semplice modello che tiene conto del formato e del layout di ogni
proprietà rappresentata nel db. I dati sono indicizzati utilizzando i nomi di
1
Si parla di un supporto a vari petabytes distribuiti su diversi servers.
15
CAPITOLO 4. IL DBMS DI GOOGLE: BIGTABLE 16
righe e colonne che possono essere stringhe arbitrarie. Bigtable può trattare
anche i dati come stringhe indefinite sebbene spesso i client convertano varie
forme di dati strutturati e semi strutturati in stringhe. I client possono inol-
tre controllare localmente i loro dati prendendosi cura di definirne lo schema
più adatto. Infine esso permette ai client di stabilire dinamicamente se i da-
ti saranno da memorizzare seguendo lo schema in-memory o il tradizionale
salvataggio su disco.
4.2.1 Righe
Le chiavi di riga in una tabella sono stringhe di dimensioni arbitrarie che
variano tra i 10 e i 100 bytes. Le letture/scritture di una singola di riga
sono atomiche, indipendentemente dal numero di colonna nella quale si vuo-
le compiere l’azione, in modo da evitare qualsiasi problema di concorrenza.
Bigtable mantiene i dati ordinati lessicograficamente per chiave di riga. Ogni
riga è suddivisa in intervalli, singolarmente chiamati tablet, che sono l’unità
di bilanciamento e di carico. Di conseguenza le letture di piccoli insiemi di
tablet sono efficienti e richiedono tipicamente comunicazioni con un numero
ristretto di macchine. I client possono così sfruttare questa proprietà sta-
bilendo le chiavi di riga in modo da ottenere un posizionamento contiguo
per facilitare i futuri accessi ai dati. Un possibile esempio può essere citato
prendendo spunto dall’indicizzazione del web: le url delle pagine di domini
identici vengono raggruppate in righe contigue capovolgendo le componenti
dell’url stessa. Ad esempio l’url della pagina maps.google.com/index.html
verrà salvata come com.google.maps/index.html in modo tale che tutte le
pagine appartenenti a questo dominio verranno salvate in posizioni vicine,
rendendo più efficienti le successive letture.
di colonne sono di solito dello stesso tipo (vengono compresse nella stessa
famiglia). Una famiglia di colonne deve essere creata necessariamente prima
che i dati possano essere salvati e raggruppati al suo interno. Queste famiglie
sono state progettate per raggruppare al loro interno un numero ristretto di
colonne(nell’ordine delle centinaia al massimo) e per cambiare raramente una
volta create, ma non vi sono limiti fisici a queste caratteristiche.
Una chiave di colonna è descritta da un sintassi del tipo family:qualifier.
Il nome della famiglia deve essere formato da caratteri stampabili ma il qua-
lificatore può essere una stringa arbitraria. Una famiglia può inoltre venire
creata come derivata di una famiglia già esistente.
4.2.3 Timestamp
Ogni cella di Bigtable può contenere molteplici versioni dello stesso dato,
proprio per questo motivo è stato creato il campo timestamp. Esso viene
rappresentato come intero a 64bit, e può essere generato in automatico dal
sistema, rappresentando l’instante di tempo real in microsecondi oppure può
venire assegnato dal client che sta scrivendo il dato. Una stessa versione di
un dato verrà ordinato in ordine decrescente in modo tale che il dato più
recente possa essere letto per primo. Si può inoltre specificare il numero di
versioni dello stesso dato che si vogliono mantenere, dato che in Bigtable
esiste una sorta di garbage collector dedicato alla funzione di eliminare i dati
ridondanti che superano il numero di ripetizioni scelte dall’utente.
sia configurabile. Alla fine di ogni SSTable esiste un indice dei blocchi che
viene usato per localizzarli, questo indice viene caricato in memoria quan-
do l’SSTable viene aperto. Una ricerca può essere effettuata mediante una
sola scansione del disco, infatti prima viene cercato il blocco desiderato effet-
tuando una ricerca binaria nell’indice all’iterno della cosidetta in-memory, e
succesivamente leggendo il blocco desiderato dal disco. In alternativa, si può
configurare il sistema in modo da mappare in memoria un SSTable, il che
permette di effettuare le ricerche in memoria senza dover toccare il disco.
Bigtable prevede anche una gestione del lock distribuita, tramite un servizio
chiamato Chubby. Esso consiste in cinque repliche attive, una delle quali è
nominata master e si occupa attivamente delle richieste. Questo servizio usa
un particolare algoritmo (Denominato paxo) per fare in modo che le repliche
slave si attivino nel caso in cui il master vada in errore. Chubby fornisce
un namespace che consiste in directory e piccoli files, e ognuno di questi può
essere usato come lock. La lettura e la scrittura di questi file è atomica. Per
poter utilizzare questo servizio ogni client mantiene una sessione, e quando
questa scade, se il client non la può rinnovare, perderà ogni diritto di scrit-
tura/lettura sui file.
Bigtable si serve di Chubby per molti compiti delicati quali: avere la certez-
za che ci sia un solo master attivo in ogni istante, scrivere dove si trova il
bootstrap dai dati di Bigtable, scoprire quali sono i server adibiti ai tablet
e per quanto tempo, salvare le informazioni relative allo schema di Bigtable
e salvare le liste di controllo degli accessi. Se Chubby per qualsiasi motivo
dovesse diventare inattivo per un lungo periodo di tempo, allora Bigtable
diventerebbe inutilizzabile2 .
2
Recenti studi condotti dai Google labs hanno misurato la percentuale di inattività del
sistema in 0.0047%. Le cause principali sono stati problemi di reti o periodi di inattività
di Chubby.
Capitolo 5
5.1 Introduzione
Uno dei problemi che afflige spesso gli studenti universitari, sono i costi che
si devono sostenere per l’acquisto dei libri. Spesso i testi acquistati vengono
utilizzati per i corsi ad essi adibiti e poi finiscono per cadere nel dimenticatoio
e non essere più sfruttati. Fermo restando che un libro può sempre risultare
utile per svariati motivi, si è pensato di creare uno strumento a disposizione
di tutti, per ammortizzare il costo di acquisto dei tomi universitari. Nasce per
questo l’idea di una semplice web application, che metta in comunicazione
gli studenti in possesso di libri diventati ormai inutili, e quelli che cercano un
determinato testo per il loro corso universitario che si apprestano a seguire.
Sellbook si propone come un semplice portale in cui chiunque può vendere
i volumi in proprio possesso tramite l’inserimento di un semplice annuncio
oppure rispondere ad un annuncio, ricercando il libro che a cui si è interessati.
Questa applicazione nasce per il corso di studi in Informatica dell’università
di Torino, ma può tranquillamente essere esteso ad altri corsi di studi e ad
altre università.
19
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 20
• Autore
• Casa Editrice
• Anno di pubblicazione
• Versione
• Categoria1 di appartenenza
• Prezzo
• Tipologia di vendita (Diretta o on-line)
• Luogo in cui si trova l’oggetto
Dovrà anche essere possibile editare un libro inserito, e eliminarlo. In ag-
giunta alla sezione dei libri, dovrà essere possibile per ogni utente, creare,
editare e rimuovere stanze. Le stanze sono una sorta di categoria, in cui
sono contenuti i libri, e verranno utilizzate per suddividere i testi in base al
corso di appartenenza. Tramite una navigazione all’interno delle stanze si
potranno ricercare i libri a cui si è interessati. Ciò permetterà all’utente che
cerca un determinato volume relativo ad un corso di studi, di trovarlo più ra-
pidamente, escludendo libri a cui sicuramente non è interessato. Scendendo
di livello all’interno delle categorie, la ricerca dei libri, sarà via via sempre
più raffinata.
1
Altrimenti denominata stanza.
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 21
• Aggiornamento_Tag
• Nuovo_Tag
Libri:
• Nuovo_Libro
• Editare_Libro
Area riservata:
• Log-in
• Log-out
Stanze:
• Crea_Nuova_Categoria
• Edita_Categoria
• Elimina_Categoria
Ricerca:
• Ricerca_Libro
• Visualizza_Libro
• Ricerca_per_Tag
• Ricerca_per_Categorie
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 22
• Ricerca_per_Keywords
• Contattare_proprietario_libro
• Segnalare_richiesta_altro_utente
• Inviare_Mail
già presente tra i tag registrati nella base dati, il sistema aggiorna il contatore
del tag.
Se viene eliminato un libro, il sistema cerca i tag ad esso associati e li elimina
nel caso abbiano il contatore uguale ad 1, o decrementa il contatore di una
unità in caso contrario.
Nuovo_Tag:
Attori: Sistema
Precondizioni: Viene inserito un nuovo libro.
Flusso: All’inserimento di un nuovo libro, una volta estratto un tag dal titolo,
se questo non è ancora presente tra i tag registrati nella base dati, il sistema
lo aggiunge all’elenco dei tag.
Nuovo_Libro:
Attori: Utente, Sistema
Precondizioni: Un utente vuole inserire un nuovo libro in vendita.
Flusso: Viene presentata la pagina di inserimento di un nuovo libro, e la
mappa di Google Maps per la ricerca della località in cui si trova il libro in
vendita. Una volta premuto il pulsante di inserimento del libro, si attiva il
caso d’uso Nuovo_Libro o Aggiornamento_Tag a seconda della necessità.
Editare_Libro:
Attori: Utente, Sistema
Precondizioni: Si deve modificare un libro precedentemente inserito.
Flusso: Viene eseguita una ricerca del libro da modificare tramite la chiave
associata al libro interessato. Vengono caricati i dati precedentemente inseriti
negli appositi campi della pagina di modifica. Quando l’utente conferma la
modifica dei dati, questi vengono sovrascritti ai precedenti nella base dati.
Log-in:
Attori: Utente
Precondizioni: Si vuole accedere alla propria area personale.
Flusso: L’utente viene reindirizzato alla pagina di Google Accounts. Se è
già in possesso di un account Google, può effettuare l’accesso inserendo sem-
plicemente nome utente e password. Se non è ancora registrato, può farlo e
ottenere un account. Fatto ciò l’utente viene reindirizzato alla pagina da cui
proveniva.
Log-out:
Attori: Utente
Precondizioni: Si vuole uscire dalla propria area personale.
Flusso: L’utente viene reindirizzato alla pagina di Google Accounts. Viene
eseguito il log-out dall’applicazione e in seguito si verrà reindirizzati alla
pagina da cui proveniva la richiesta di log-out.
Crea_Nuova_Categoria:
Attori: Utente, Sistema
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 24
• Banner: contiene un link che rimanda alla home page, questa sezione
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 27
• Tag Cloud: contiene l’insieme delle parole chiave estratte dai titoli dei
volumi presenti nel sistema. La dimensione di ogni parola è determi-
nata dal numero di ripetizioni della parola stessa all’interno di tutti i
titoli dei libri presenti. Ciò aiuterà l’utente a capire quanto ogni pa-
rola è comune, e di conseguenza quanti libri sono accomunati da quel
termine.
Quando si clicca su un tag viene avviata la ricerca, e vengono succesi-
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 28
• Menu: sulla parte destra della pagina si può trovare il menù dell’appli-
5.6.2 Libri
La pagina di inserimento di un nuovo libro è suddivisa in due sezioni, il form
di inserimento dei dati e l’elenco dei propri libri già inseriti.
Il form presenta alcuni campi testuali, un pulsante per la selezione dell’im-
magine di copertina del libro da inserire, una combo box per la scelta della
stanza a cui il libro dovrà appartenere, uno specchietto di selezione per sta-
bilire il tipo di vendita e una mappa interattiva, tramite la quale l’utente
potrà indicare il luogo esatto in cui si trova il libro. Quest’ultima informa-
zione potrebbe risultare utile per definire il luogo di incontro tra acquirente
e venditore, per la vendita effettiva del testo. L’altra sezione della pagina è
posta nella parte bassa del documento ipertestuale, e rappresenta un riepilo-
go dei libri inseriti dall’utente, sotto forma di lista di titoli. Accanto ad ogni
breve descrizione dei volumi, vi sono due pulsanti, uno per la modifica delle
informazioni sul testo, ed una per eliminare dal sistema il libro.
Cliccando sul pulsante per la modifica delle descrizioni di un determinato li-
bro, vengono riempiti tutti i campi del form soprastante, con le informazioni
relative al libro selezionato. Quando si ricerca un libro e si vogliono visua-
lizzare i suoi dettagli, viene presentata una pagina del tutto simile a quella
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 30
5.6.3 Stanze
La pagina delle stanze presenta un semplice elenco gerarchico. Ogni stanza
principale ha, sotto di se, tutte le sue stanze “figlie”. Le stanze figlie si ri-
conoscono perchè hanno un leggero margine di rientro, rispetto alle stanze
gerarchicamente superiori ad essa. Come si può notare dalla figura [5.11],
ogni voce ha al suo fianco tre pulsanti. Il simbolo “+”serve per aggiungere
una sottostanza a quella indicata. Il simbolo “-”, al contrario, serve per eli-
minare la stanza in questione. L’ultima icona consente invece di modificare
la descrizione della stanza. Le ultime due azioni, come detto in precedenza
sono consentite solamente al creatore della stanza e agli utente con privilegi
di amministrazione, mentra l’aggiunta di una sottostanza o di una stanza
radice, è consentita a chiunque abbia effettuato l’accesso. Se un visitatore,
non ha inserito le sue credenziali d’accesso, potrà solamente vedere l’elenco
delle stanze e i dettagli per ogni stanza.
Per vedere i dettagli di una stanza, basta semplicemente cliccare sul nome
della stessa. Verrà aperta una pagina in cui vengono mostrate, oltre alla
descrizione, il creatore della stanza, il livello a cui essa appartiene, la stan-
za “padre”e le stanze “figlie”. In basso verranno presentati i libri che sono
contenuti nella stanza, se presenti.
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 32
class Rooms(db.Model):
descr=db.StringProperty()
listaFigli=db.ListProperty(db.Key)
father=db.SelfReferenceProperty()
livello=db.IntegerProperty()
owner=db.UserProperty()
class Book(db.Model):
title=db.StringProperty()
author=db.StringProperty()
version=db.IntegerProperty()
year=db.IntegerProperty()
room=db.ReferenceProperty(Rooms)
2
Utilizzata ad esempio nel campo father, per descrivere l’istanza padre di quella in
questione.
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 33
casaEd=db.StringProperty()
img=db.BlobProperty(required=False)
class Tag(db.Model):
descr=db.StringProperty()
nTag=db.IntegerProperty()
Questa tabella tiene traccia delle parole chiave estratte dai titoli. Attraverso
il campo nTag si tiene conto del numero di ripetizioni di una determinata
parola. Questo metodo consente di velocizzare notevolmente la costruzione
dello strumento del Tag cloud.
class TaggedBook(db.Model):
tag=db.ReferenceProperty(Tag)
book=db.ReferenceProperty(Book)
class SellBook(db.Model):
book=db.ReferenceProperty(Book)
user=db.UserProperty()
price=db.FloatProperty()
sellType=db.IntegerProperty()
contact=db.IntegerProperty()
dataSell=db.DateTimeProperty(auto_now_add=True)
location=db.GeoPtProperty(required=False)
5.9 Considerazioni
Il caso d’uso SellBook è stato molto utile per sperimentare App Engine, per-
chè ha permesso di usufruire di diversi servizi, tra quelli messi a disposizione
dal team di Google.
In definitiva, questo strumento mette a disposizione tutto ciò di cui uno svi-
luppatore di siti web ha bisogno, semplificando notevolmente il suo lavoro.
Il processo di creazione dell’applicazione è del tutto analogo a quello che si
compie utilizzando php, jsp o asp. Uno dei più grandi vantaggi è sicuramente
quello di avere il database integrato nel kit. Con le librerie per il datastore in-
fatti, quando si vogliono leggere dati dal db è necessaria la sola istruzione che
invia la query, senza nessuna procedura per la connesione e la disconnessione
al dbms. Tutto ciò risparmia al programmatore alcune linee di codice ogni
4
Ogni stanza può contenere un numero variabile e indipendente di sottostanze
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 36
Il file main.py
import wsgiref.handlers
import re
import time
import logging
logging.getLogger().setLevel(logging.DEBUG)
class Rooms(db.Model):
descr=db.StringProperty()
listaFigli=db.ListProperty(db.Key)
father=db.SelfReferenceProperty()
livello=db.IntegerProperty()
owner=db.UserProperty()
class Book(db.Model):
37
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 38
title=db.StringProperty()
author=db.StringProperty()
version=db.IntegerProperty()
year=db.IntegerProperty()
room=db.ReferenceProperty(Rooms)
casaEd=db.StringProperty()
img=db.BlobProperty(required=False)
class Tag(db.Model):
descr=db.StringProperty()
nTag=db.IntegerProperty()
class TaggedBook(db.Model):
tag=db.ReferenceProperty(Tag)
book=db.ReferenceProperty(Book)
class SellBook(db.Model):
book=db.ReferenceProperty(Book)
user=db.UserProperty()
price=db.FloatProperty()
sellType=db.IntegerProperty()
contact=db.IntegerProperty()
dataSell=db.DateTimeProperty(auto_now_add=True)
location=db.GeoPtProperty(required=False)
class MainPage(webapp.RequestHandler):
#gestisce le richieste get
def get(self):
if not cmp(self.request.get("action"),"SearchTag"):
try:
_id=db.get(self.request.get("idtag"))
except:
_id=None
self.redirect(’/’)
else:
q1=db.GqlQuery("SELECT * FROM TaggedBook
WHERE tag= :1",_id)
tot=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1", users.GetCurrentUser())
cont=0
for t in tot:
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 39
else:
ultimi=""
for u in ultimiInseriti:
ultimi+="<a href=\"libro?id="
+str(u.book.key())+"&action=View\"
class=\"no_under\">"+u.book.title+"
("+str(u.price)+"€)
<img src=\"img/icons/edit-find.png\"
alt=\"Richiedi Info\" title=\"Richiedi Info\" /></a><br />"
if not memcache.add("lastBooks",ultimi):
logging.error("MemCache add
error on \"lastBooks\"")
valori={
’user’: users.GetCurrentUser(),
’loginUrl’: users.CreateLoginURL(
self.request.uri),
’logoutUrl’: users.CreateLogoutURL(
self.request.uri),
’contacts’: cont,
’tags’:tags,
’ultimiInseriti’:ultimi
}
#reindirizzamento a pagina html
self.response.out.write(template.render(
link,valori))
result={
’result’: query,
’n_query’: len_query,
’user’: users.GetCurrentUser(),
’loginUrl’: users.CreateLoginURL(
self.request.uri),
’logoutUrl’: users.CreateLogoutURL(
self.request.uri),
’contacts’: cont,
’tempo’: tmp
}
self.response.out.write(template.render(
’search.html’,result))
class Cloud():
descr=’’
dim=’’
key=’’
@staticmethod
def tagCorrelati(self,q2,tags):
for q21 in q2:
flag=0
for t in tags:
if t.descr==q21.tag.descr:
flag=1
tmp=t
if flag==1:
tmp.dim+=1
else:
tmp=Cloud()
tmp.descr=q21.tag.descr
tmp.dim=1
tmp.key=q21.tag.key
tags.append(tmp)
return tags
@staticmethod
def cercaCorrelati(self,q1,_id):
tags=[]
for q in q1:
_id=db.get(q.book.key())
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 42
class SearchObj():
book=None
nKey=0
class Search(object):
@staticmethod
def compare(obj1,obj2):
return cmp(obj1.book.title,obj2.book.title)
@staticmethod
def comparaNKey(x,y):
if x.nKey==y.nKey:
return 0
else:
if x.nKey>y.nKey:
return -1
return 1
@staticmethod
def run(self):
p=re.compile(’(\!|\"|\%|\&|\/|\=|\’|\?|\^)’)
key=p.sub(’’,self.request.get("key"))
p=re.compile(’\+’)
key=p.sub(’ ’,key)
keys=split(key,’ ’)
q1=db.GqlQuery("SELECT * FROM Book")
ris=[]
for q in q1:
for k in keys:
if find(lower(q.title),lower(k))!=-1:
tmp=SearchObj()
tmp.book=q
tmp.nKey=1
flag=0
app=None
for r in ris:
if Search.compare(tmp,r)==0:
flag=1
app=r
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 44
if flag==1:
app.nKey+=1
else:
ris.append(tmp)
ris.sort(Search.comparaNKey)
return ris
def main():
app=webapp.WSGIApplication([(r’.*’,MainPage)],debug=_DEBUG)
wsgiref.handlers.CGIHandler().run(app)
if __name__ == ’__main__’:
main()
Il file libri.py
import wsgiref.handlers
import re
import sys
class Rooms(db.Model):
descr=db.StringProperty()
listaFigli=db.ListProperty(db.Key)
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 45
father=db.SelfReferenceProperty()
livello=db.IntegerProperty()
owner=db.UserProperty()
class Book(db.Model):
title=db.StringProperty()
author=db.StringProperty()
version=db.IntegerProperty()
year=db.IntegerProperty()
room=db.ReferenceProperty(Rooms)
casaEd=db.StringProperty()
img=db.BlobProperty(required=False)
class Tag(db.Model):
descr=db.StringProperty()
nTag=db.IntegerProperty()
class TaggedBook(db.Model):
tag=db.ReferenceProperty(Tag)
book=db.ReferenceProperty(Book)
class SellBook(db.Model):
book=db.ReferenceProperty(Book)
user=db.UserProperty()
price=db.FloatProperty()
sellType=db.IntegerProperty()
contact=db.IntegerProperty()
dataSell=db.DateTimeProperty(auto_now_add=True)
location=db.GeoPtProperty(required=False)
class Image(webapp.RequestHandler):
def get(self):
book=db.get(self.request.get("img_id"))
if book.img:
self.response.headers[’Content-Type’]="image/png"
self.response.out.write(book.img)
else:
self.response.write("No Image")
class Libri(webapp.RequestHandler):
#gestisce le richieste get
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 46
def get(self):
if not users.get_current_user() and cmp(
self.request.get("action"),"View")!=0:
self.redirect(’/’)
msg=None
link=’libri.html’
b=None
sd=None
imgBook=None
output=None
q1=None
tags=[]
p=re.compile(’(\!|\"|\%|\&|\/|\=|\’|\?|\^)’)
if p.sub(’’,self.request.get("msg"))=="1":
msg="Attenzione, compilare
correttamente tutti i campi"
id_key=p.sub(’’,self.request.get("id"))
if self.request.get("action") == "Elimina":
self.elimina()
self.redirect(’/libri’)
else:
if self.request.get("action") == "View":
link=’libro.html’
if id_key:
try:
b=db.get(id_key)
except:
b=None
self.redirect("/libri")
q3=db.GqlQuery("SELECT * FROM Rooms
WHERE livello= :1",0)
if b:
if b.room:
output=Stanze.creaBox(q3,b.room.key())
else:
output=Stanze.creaBox(q3,None)
else:
output=Stanze.creaBox(q3,None)
q3=db.GqlQuery("SELECT * FROM SellBook
WHERE book= :1 LIMIT 0,1",b)
for sb in q3:
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 47
sd=SellDetails()
sd.price=sb.price
sd.location=sb.location
sd.key=sb.key()
if sb.sellType==1:
sd.directSell=1
else:
if sb.sellType==2:
sd.onlineSell=1
else:
if sb.sellType==3:
sd.directSell=1
sd.onlineSell=1
q2=db.GqlQuery("SELECT * FROM TaggedBook
WHERE book= :1",b)
tags=Cloud.tagCorrelati(self,q2,tags)
else:
q1=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1 ORDER BY book DESC",users.GetCurrentUser())
q2=db.GqlQuery("SELECT * FROM Rooms
WHERE livello= :1",0)
output=Stanze.creaBox(q2,None)
if id_key:
try:
b=db.get(id_key)
except:
b=None
self.redirect("/libri")
else:
q3=db.GqlQuery("SELECT * FROM Rooms
WHERE livello= :1",0)
if b:
if b.room:
output=Stanze.creaBox(q3,b.room.key())
else:
output=Stanze.creaBox(q3,None)
else:
output=Stanze.creaBox(q3,None)
q3=db.GqlQuery("SELECT * FROM SellBook
WHERE book= :1 LIMIT 0,1",b)
for sb in q3:
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 48
sd=SellDetails()
sd.price=sb.price
sd.key=sb.key()
sd.location=sb.location
if sb.sellType==1:
sd.directSell=1
else:
if sb.sellType==2:
sd.onlineSell=1
else:
if sb.sellType==3:
sd.directSell=1
sd.onlineSell=1
tot=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1", users.GetCurrentUser())
cont=0
for t in tot:
if t.contact is not None:
cont+=t.contact
valori={
’user’: users.GetCurrentUser(),
’loginUrl’: users.CreateLoginURL(self.request.uri),
’logoutUrl’: users.CreateLogoutURL("/"),
’contacts’: cont,
’books’: q1,
’details’: b,
’sellDetails’:sd,
’imgBook’:imgBook,
’id_key’:id_key,
’tags’:tags,
’selectBox’:output,
’msg’: msg
}
self.response.out.write(template.render(link,valori))
if len(room)>0:
room=db.get(room)
if not room:
room=None
else:
room=None
book=Book()
book.title=p.sub(’’,self.request.get("titolo"))
book.author=p.sub(’’,self.request.get("autore"))
book.casaEd=p.sub(’’,self.request.get("casaEd"))
book.year=int(p.sub(’’,self.request.get("annoP")))
book.version=int(p.sub(’’,
self.request.get("versione")))
if room:
book.room=room.key()
else:
book.room=None
src=self.request.get("imgBook")
if len(src)>0:
img=images.Image(src)
img.resize(100,100)
img.im_feeling_lucky()
book.img=db.Blob(img.execute_transforms(
output_encoding=images.PNG))
else:
book.img=None
sbook=SellBook()
sbook.user=users.GetCurrentUser()
sbook.price=float(p.sub(’’,
self.request.get("prezzo")))
reg=re.compile(’(\:|\;|\!|\"|\$|\%|\&|\/|\
(|\)|\=|\’|\?|\^)’)
loc=reg.sub(’’,self.request.get("location"))
geo=split(loc,",")
sbook.location=db.GeoPt(float(geo[0]),
float(geo[1]))
directSell=self.request.get("directSell")
onlineSell=self.request.get("onlineSell")
sbook.sellType=0
if directSell:
if onlineSell:
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 50
sbook.sellType=3
else:
sbook.sellType=1
else:
if onlineSell:
sbook.sellType=2
db.put(book)
sbook.book=book.key()
db.put(sbook)
self.tagga(self,book.key())
memcache.delete("lastBooks")
memcache.delete("tagCloud")
self.redirect(’/libri’)
except:
self.redirect(’/libri?msg=1’)
else:
if not cmp(self.request.get("action"),"Modifica"):
try:
k=p.sub(’’,self.request.get(’id_key’))
b=db.get(k)
b.title=p.sub(’’,self.request.get("titolo"))
b.author=p.sub(’’,self.request.get("autore"))
b.casaEd=p.sub(’’,self.request.get("casaEd"))
b.year=int(p.sub(’’,self.request.get("annoP")))
b.version=int(p.sub(’’,self.request.get(
"versione")))
k=p.sub(’’,self.request.get("sell_key"))
sbook=db.get(k)
directSell=self.request.get("directSell")
onlineSell=self.request.get("onlineSell")
sbook.sellType=0
if directSell:
if onlineSell:
sbook.sellType=3
else:
sbook.sellType=1
else:
if onlineSell:
sbook.sellType=2
sbook.price=float(p.sub(’’,
self.request.get("prezzo")))
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 51
reg=re.compile(’(\:|\;|\!|\"|\$|\%|\&|\/|\(
|\)|\=|\’|\?|\^)’)
loc=reg.sub(’’,self.request.get("location"))
geo=split(loc,",")
sbook.location=db.GeoPt(float(geo[0]),
float(geo[1]))
db.put(b)
db.put(sbook)
memcache.delete("tagCloud")
memcache.delete("lastBooks")
self.redirect(’/libri’)
except:
self.redirect(’/libri?msg=1’)
else:
if not cmp(self.request.get("mail"),"write"):
link=’mail.html’
id_book=db.get(p.sub(’’,
self.request.get(’id_book’)))
id_sell=db.get(p.sub(’’,
self.request.get(’id_sell’)))
tot=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1", users.GetCurrentUser())
cont=0
for t in tot:
if t.contact is not None:
cont+=t.contact
valori={
’user’: users.GetCurrentUser(),
’loginUrl’: users.CreateLoginURL(
self.request.uri),
’logoutUrl’: users.CreateLogoutURL("/"),
’book’:id_book,
’contacts’:cont,
’sell’:id_sell
}
self.response.out.write(
template.render(link,valori))
else:
if not cmp(self.request.get("mail"),"send"):
mittente=p.sub(’’,
self.request.get(’address’))
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 52
oggetto=p.sub(’’,
self.request.get(’oggetto’))
mex=p.sub(’’,self.request.get(’mex’))
try:
id_sell=db.get(p.sub(’’,
self.request.get(’id_sell’)))
except:
id_sell=None
self.redirect("/libri")
if id_sell:
seller=(id_sell.user).email()
if not seller or not mex or not oggetto
or not mittente or len(mittente)==0 or len(oggetto)==0
or len(mex)==0:
link=’mail.html’
tot=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1", users.GetCurrentUser())
cont=0
for t in tot:
if t.contact is not None:
cont+=t.contact
valori={
’user’: users.GetCurrentUser(),
’loginUrl’: users.CreateLoginURL(
self.request.uri),
’logoutUrl’:
users.CreateLogoutURL("/"),
’book’:id_sell.book,
’sell’:id_sell,
’body’:mex,
’contacts’: cont,
’msg’:"Attenzione: compilare
correttamente tutti i campi!"
}
self.response.out.write(
template.render(link,valori))
else:
mail.send_mail(sender=mittente,
to=seller,subject=oggetto,body=mex)
if id_sell.contact is not None:
id_sell.contact=id_sell.contact+1
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 53
else:
id_sell.contact=1
db.put(id_sell)
self.redirect(’/’)
else:
self.elimina()
memcache.delete("tagCloud")
memcache.delete("lastBooks")
self.redirect(’/libri’)
@staticmethod
def tagga(self,book):
titolo=lower(self.request.get("titolo"))
newList=[]
q=db.GqlQuery("SELECT * FROM Tag")
oldList=[]
fullOldList=[]
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 54
for q1 in q:
oldList.append(q1.descr)
fullOldList.append(q1)
stopWords = [’in’,’from’, ’of’, ’to’, ’and’,\
’an’, ’with’, ’that’, ’a’, ’on’, \
’the’, ’lo’, ’di’,’dei’, ’dell\’’, ’e’, \
’into’, ’E\’’, ’The’, ’by’, ’for’, ’about’, \
’as’, ’’, ’A’, ’E’, ’nei’, ’negli’, \
’based’, ’driven’, ’guided’, ’means’, \
’priori’, ’About’, ’An’, ’0’, ’I’, ’On’, \
’Proc’, ’agli’, ’like’, ’s’, ’valued’, \
’degli’, ’delle’, ’per’, ’un’, ’uno’, \
’una’,’l’,’la’,’le’, ’d’,’i’,’della’, \
’degli’,’il’,’dai’,’del’,’-’,’con’]
p=re.compile(’(\.|\:|\,|\;|\d|\!|\"|\$
|\%|\&|\/|\(|\)|\=|\’|\?|\^)’)
titolo=p.sub(’ ’,titolo)
tags=split(titolo," ")
for t in tags:
if t not in stopWords:
if t not in oldList:
tag=Tag()
tag.descr=t
tag.nTag=1
tag.put()
tagged=TaggedBook()
tagged.tag=tag.key()
tagged.book=book
tagged.put()
else:
for f in fullOldList:
if t == f.descr:
q=db.GqlQuery("SELECT * FROM Tag
WHERE descr= :1 LIMIT 1",f.descr)
for q1 in q:
q2=db.get(q1.key())
if q2:
q2.nTag=(q2.nTag+1)
db.put(q2)
tagged=TaggedBook()
tagged.tag=f.key()
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 55
tagged.book=book
tagged.put()
class SellDetails():
price=None
directSell=None
onlineSell=None
location=None
key=None
class Stanze():
@staticmethod
def creaBox(query,compare):
output=None
if query.count()>0:
output="<form method=\"post\" action=\"\">"
output+="<select name=\"roomBook\">"
for q in query:
if q.listaFigli:
if len(q.listaFigli)>0:
output+="<optgroup label=\""+
str(q.livello)+"−"+q.descr+"\">"
output=Stanze.creaBoxRicorsiva(output,q,compare)
output+="</optgroup>"
else:
if q.key() == compare:
output+="<option value=\""+
str(q.key())+"\" selected=\"selected\">"+q.descr+"</option>"
else:
output+="<option value=\""+
str(q.key())+"\">"+q.descr+"</option>"
else:
if q.key() == compare:
output+="<option value=\""+
str(q.key())+"\" selected=\"selected\">"+q.descr+"</option>"
else:
output+="<option value=\""+
str(q.key())+"\">"+q.descr+"</option>"
if query.count()>0:
output+="</select>"
return output
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 56
@staticmethod
def creaBoxRicorsiva(output,padre,compare):
for l in padre.listaFigli:
tmp=db.get(l)
if tmp:
if tmp.listaFigli:
if len(tmp.listaFigli)>0:
output+="<optgroup label=\""+
str(tmp.livello)+"−"+tmp.descr+"\">"
output=Stanze.creaBoxRicorsiva(
output,tmp,compare)
output+="</optgroup>"
else:
if tmp.key() == compare:
output+="<option value=\""+
str(tmp.key())+"\" selected=\"selected\">"+
tmp.descr+"</option>"
else:
output+="<option value=\""+
str(tmp.key())+"\">"+tmp.descr+"</option>"
else:
if tmp.key() == compare:
output+="<option value=\""+
str(tmp.key())+"\" selected=\"selected\">"+
tmp.descr+"</option>"
else:
output+="<option value=\""+
str(tmp.key())+"\">"+tmp.descr+"</option>"
return output
def main():
app=webapp.WSGIApplication([(’/libri’,Libri),
(’/libro’,Libri),(’/img’,Image)],debug=_DEBUG)
wsgiref.handlers.CGIHandler().run(app)
if __name__ == ’__main__’:
main()
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 57
Il file stanze.py
import wsgiref.handlers
import re
class Rooms(db.Model):
descr=db.StringProperty()
listaFigli=db.ListProperty(db.Key)
father=db.SelfReferenceProperty()
livello=db.IntegerProperty()
owner=db.UserProperty()
class Book(db.Model):
title=db.StringProperty()
author=db.StringProperty()
version=db.IntegerProperty()
year=db.IntegerProperty()
room=db.ReferenceProperty(Rooms)
casaEd=db.StringProperty()
img=db.BlobProperty(required=False)
class Tag(db.Model):
descr=db.StringProperty()
nTag=db.IntegerProperty()
class TaggedBook(db.Model):
tag=db.ReferenceProperty(Tag)
book=db.ReferenceProperty(Book)
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 58
class SellBook(db.Model):
book=db.ReferenceProperty(Book)
user=db.UserProperty()
price=db.FloatProperty()
sellType=db.IntegerProperty()
contact=db.IntegerProperty()
dataSell=db.DateTimeProperty(auto_now_add=True)
location=db.GeoPtProperty(required=False)
class Stanze(webapp.RequestHandler):
#gestisce le richieste get
def get(self):
valori=None
link=’stanze.html’
p=re.compile(’(\!|\"|\%|\&|\/|\=|\’|\?|\^)’)
if not cmp(self.request.get("action"),"add"):
if not users.get_current_user():
self.redirect(’/’)
else:
stanza=p.sub(’’,self.request.get("stanza"))
link="nuovaStanza.html"
if not cmp(stanza,"None"):
stanza=None
nomeStanza=None
else:
try:
nomeStanza=db.get(stanza)
except:
nomeStanza=None
self.redirect(’/stanze’)
if nomeStanza:
nomeStanza=nomeStanza.descr
tot=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1", users.GetCurrentUser())
cont=0
for t in tot:
if t.contact is not None:
cont+=t.contact
valori={
’user’: users.GetCurrentUser(),
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 59
’loginUrl’:
users.CreateLoginURL(self.request.uri),
’logoutUrl’:
users.CreateLogoutURL(self.request.uri),
’contacts’: cont,
’father’:stanza,
’stanza’:nomeStanza
}
else:
if not cmp(self.request.get("action"),"remove"):
if not users.get_current_user():
self.redirect(’/’)
else:
link="stanze.html"
stanza=p.sub(’’,self.request.get("stanza"))
try:
r=db.get(stanza)
except:
stanza=None
self.redirect(’/stanze’)
else:
if r is not None:
if r.father is not None:
r.father.listaFigli.remove(r.key())
self.removeStanza(stanza)
memcache.delete("stanze")
valori=self.visualizzaStanze()
else:
if not cmp(self.request.get("action"),"modify"):
try:
stanza=db.get(p.sub(’’,
self.request.get("stanza")))
except:
stanza=None
self.redirect(’/stanze’)
else:
tot=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1", users.GetCurrentUser())
cont=0
for t in tot:
if t.contact is not None:
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 60
cont+=t.contact
if stanza is not None:
if not users.get_current_user():
self.redirect(’/’)
else:
if users.get_current_user()!=
stanza.owner:
self.redirect(’/stanze’)
else:
if stanza.father is not None:
idFather=stanza.father.key()
else:
idFather=None
link="nuovaStanza.html"
valori={
’user’: users.GetCurrentUser(),
’loginUrl’:
users.CreateLoginURL(self.request.uri),
’logoutUrl’:
users.CreateLogoutURL(self.request.uri),
’contacts’: cont,
’father’:idFather,
’stanza’:stanza.descr,
’modifica’:1
}
else:
tot=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1", users.GetCurrentUser())
cont=0
for t in tot:
if t.contact is not None:
cont+=t.contact
valori={
’user’: users.GetCurrentUser(),
’loginUrl’:
users.CreateLoginURL(self.request.uri),
’logoutUrl’:
users.CreateLogoutURL(self.request.uri),
’father’:idPadre,
’stanza’:nomeStanza,
’contacts’:cont,
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 61
’message’:
"Attenzione: Stanza inesistente."
}
link="stanze.html"
else:
if not cmp(self.request.get("action"),"view"):
link="stanza.html"
books=None
tot=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1", users.GetCurrentUser())
cont=0
for t in tot:
if t.contact is not None:
cont+=t.contact
try:
stanza=db.get(p.sub(’’,
self.request.get("stanza")))
except:
stanza=None
self.redirect(’/stanze’)
listaFigli=[]
if stanza:
for s in stanza.listaFigli:
tmp=db.get(s)
if tmp:
listaFigli.append(tmp)
books=db.GqlQuery("SELECT * FROM Book
WHERE room= :1", stanza.key())
if books:
if books.count()<=0:
books=None
valori={
’user’: users.GetCurrentUser(),
’loginUrl’:
users.CreateLoginURL(self.request.uri),
’logoutUrl’:
users.CreateLogoutURL(self.request.uri),
’contacts’: cont,
’stanza’: stanza,
’stanzeFiglie’: listaFigli,
’books’: books
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 62
}
else:
valori=self.visualizzaStanze()
except:
stanza=None
self.redirect("/stanze")
if stanza:
stanza.descr=p.sub(’’,
self.request.get("nuovaStanza"))
db.put(stanza)
memcache.delete("stanze")
self.redirect(’/stanze’)
def visualizzaStanze(self):
output=None
q=db.GqlQuery("SELECT * FROM Rooms
WHERE livello= :1",0)
output=""
if q.count()!=0 or q is not None:
output=self.creaAlbero(q)
if not users.get_current_user():
output="Stanze presenti:<br/>"+output
else:
output="<a href=\"stanze?
action=add&stanza=None\"><img src=\"
img/icons/list-add.png\"> Inserisci
stanza al livello radice</a>"+output
else:
if not users.get_current_user():
output="Nessuna stanza presente.<br/>"+output
else:
output="<a href=\"stanze?
action=add&stanza=None\"><img src=\"
img/icons/list-add.png\"> Inserisci
stanza al livello radice</a>"+output
tot=db.GqlQuery("SELECT * FROM SellBook
WHERE user= :1", users.GetCurrentUser())
cont=0
for t in tot:
if t.contact is not None:
cont+=t.contact
valori={
’user’: users.GetCurrentUser(),
’loginUrl’: users.CreateLoginURL(self.request.uri),
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 65
’logoutUrl’:
users.CreateLogoutURL(self.request.uri),
’contacts’: cont,
’rooms’:output
}
return valori
def creaAlbero(self,q):
output=""
output+="<ul align=\"center\">"
for q1 in q:
if q1 is not None:
output+="<li>"
output+="<a href=\"stanze?action=view&stanza="
+str(q1.key())+"\">"+q1.descr+"</a> "
if users.get_current_user():
output+="| <a href=\"
stanze?action=add&stanza="+str(q1.key())+"\">
<img src=\"img/icons/list-add.png\"></a> "
if users.get_current_user()==q1.owner or
users.is_current_user_admin():
output+="<a href=\"
stanze?action=remove&stanza="+str(q1.key())+"\">
<img src=\"img/icons/list-remove.png\"></a> "
output+="<a href=\"stanze?
action=modify&stanza="+str(q1.key())+"\"><img src=\"
img/icons/accessories-text-editor.png\"></a>"
if len(q1.listaFigli)>0:
output+="<ul align=\"center\">"
output=self.creaAlberoRicorsiva(q1,output)
output+="</ul>"
output+="</li>"
output+="</ul>"
return output
def creaAlberoRicorsiva(self,q,output):
lista=q.listaFigli
for l in lista:
tmp=db.get(l)
if tmp is not None:
output+="<li>"
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 66
output+="<a href=\"stanze?action=view&stanza="
+str(l)+"\">"+tmp.descr+"</a> "
if users.get_current_user():
output+="| <a href=\"stanze?
action=add&stanza="+str(l)+"\"><img src=\"
img/icons/list-add.png\"></a>"
if users.get_current_user()==tmp.owner or
users.is_current_user_admin():
output+="<a href=\"stanze?
action=remove&stanza="+str(l)+"\"><img src=\"
img/icons/list-remove.png\"></a> "
output+="<a href=\"stanze?
action=modify&stanza="+str(l)+"\"><img src=\"
img/icons/accessories-text-editor.png\"></a>"
if len(tmp.listaFigli)>0:
output+="<ul align=\"center\">"
output=self.creaAlberoRicorsiva(tmp,output)
output+="</ul>"
output+="</li>"
return output
def removeStanza(self,stanza):
try:
r=db.get(stanza)
except:
r=None
if r is not None:
for l in r.listaFigli:
self.removeStanza(l)
r.listaFigli.remove(l)
db.delete(l)
db.delete(r)
def main():
app=webapp.WSGIApplication([(r’.*’,Stanze)],debug=_DEBUG)
wsgiref.handlers.CGIHandler().run(app)
if __name__ == ’__main__’:
main()
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 67
function showAddress(e){
var address=window.document.forms[’formDati’].address.value;
if (geocoder) {
geocoder.getLatLng(
address,
function(point) {
if (!point) {
alert(address + " not found");
}
else{
map.setCenter(point, 13);
window.document.forms[’formDati’]
.location.value=point;
geocoder.getLocations(address, addAddressToMap);
}
}
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 68
);}
}
function addAddressToMap(response){
map.clearOverlays();
if (!response || response.Status.code != 200) {
alert("Sorry, we were unable to geocode that address");
}else{
place = response.Placemark[0];
point = new GLatLng(place.Point.coordinates[1],
place.Point.coordinates[0]);
marker = new GMarker(point);
map.addOverlay(marker);
marker.openInfoWindowHtml(
’<b>Indirizzo:</b> ’ + place.address + ’<br>’ +
’<b>Codice nazione:</b> ’ +
place.AddressDetails.Country.CountryNameCode);
}
}
handlers:
- url: /lib
static_dir: lib
- url: /img
static_dir: img
- url: /src
static_dir: src
- url: /
script: main.py
- url: /tag
script: main.py
- url: /libri
script: libri.py
CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 69
- url: /libro
script: libri.py
- url: /img
script: libri.py
- url: /stanze
script: stanze.py
- url: /info
script: info.py
- url: /.*
script: not_found.py
Bibliografia
[1] http://code.google.com/intl/it-IT/appengine/
[2] http://code.google.com/intl/it-IT/appengine/docs/
[3] http://code.google.com/intl/it-IT/appengine/kb/
[4] http://code.google.com/intl/it-IT/appengine/articles/
[5] http://googleappengine.blogspot.com/
[6] http://groups.google.com/group/google-appengine
[7] http://code.google.com/intl/it-IT/appengine/terms.html
[8] http://code.google.com/intl/it-IT/appengine/downloads.html
[9] http://code.google.com/intl/it-IT/
appengine/docs/python/gettingstarted/
70
Bibliografia e sitografia 71
[16] http://www.aleax.it/it_gae.pdf
[17] http://www.educ.di.unito.it/VisualizzaCorsi/tag_cloud.php
[18] http://www.educ.di.unito.it/VisualizzaCorsi/tag.php?tag=web&year=
[19] http://www.salesforce.com/it/
[20] http://aws.amazon.com/
[21] http://en.wikipedia.org/wiki/Amazon_Web_Services
[22] http://en.wikipedia.org/wiki/Cloud_computing
[23] http://it.wikipedia.org/wiki/Cloud_computing
[24] http://en.wikipedia.org/wiki/Software_as_a_service
[25] http://www.python.it/
[26] http://www.python.org/doc/
[27] http://www.python.org/doc/current/
[28] http://it.diveintopython.org/
[29] http://docs.python.it/html/lib/
[30] http://www.ebay.it/