Google App Engine: una soluzione alternativa alla creazione di web application.

74
Università degli studi di Torino Facoltà di Scienze Matematiche, Fisiche e Naturali Corso di laurea in Informatica Anno accademico 2007/2008 Google App Engine: una soluzione alternativa per la creazione di web application Relatore: Prof. Matteo Baldoni Candidato: Davide Ferrero

description

La mia tesi di laurea in Informatica alla facoltà di Scienza MFN dell'Università degli studi di Torino

Transcript of Google App Engine: una soluzione alternativa alla creazione di web application.

Page 1: Google App Engine: una soluzione alternativa alla creazione di web application.

Università degli studi di TorinoFacoltà di Scienze Matematiche, Fisiche e Naturali

Corso di laurea in InformaticaAnno accademico 2007/2008

Google App Engine: una soluzionealternativa per la creazione di web

application

Relatore:Prof. Matteo Baldoni

Candidato:Davide Ferrero

Page 2: Google App Engine: una soluzione alternativa alla creazione di web application.

Indice

1 App Engine SDK 11.1 Funzionamento del kit . . . . . . . . . . . . . . . . . . . . . . 2

2 Servizi offerti da App Engine 32.1 Google Account . . . . . . . . . . . . . . . . . . . . . . . . . . 32.2 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.3 Mail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.4 Memcache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.5 Datastore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.6 Admin Console . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3 Il cloud computing 113.1 Salesforce.com . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.2 Amazon Web Services . . . . . . . . . . . . . . . . . . . . . . 14

4 Il DBMS di Google: Bigtable 154.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154.2 Modello di rappresentazione dei dati . . . . . . . . . . . . . . 16

4.2.1 Righe . . . . . . . . . . . . . . . . . . . . . . . . . . . 164.2.2 Famiglie di Colonne . . . . . . . . . . . . . . . . . . . . 164.2.3 Timestamp . . . . . . . . . . . . . . . . . . . . . . . . 17

4.3 Struttura interna . . . . . . . . . . . . . . . . . . . . . . . . . 17

5 Un caso di studio: Sellbook 195.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195.2 Specifica dei requisiti . . . . . . . . . . . . . . . . . . . . . . . 195.3 Sviluppo dell’applicazione . . . . . . . . . . . . . . . . . . . . 205.4 Ricerca dei casi d’uso . . . . . . . . . . . . . . . . . . . . . . . 215.5 Specifiche dei casi d’uso . . . . . . . . . . . . . . . . . . . . . 225.6 Interazione utente/applicazione . . . . . . . . . . . . . . . . . 26

5.6.1 Homepage . . . . . . . . . . . . . . . . . . . . . . . . . 26

I

Page 3: Google App Engine: una soluzione alternativa alla creazione di web application.

INDICE II

5.6.2 Libri . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295.6.3 Stanze . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

5.7 Schema della base dati . . . . . . . . . . . . . . . . . . . . . . 325.8 Presentazione degli algoritmi utilizzati . . . . . . . . . . . . . 34

5.8.1 Ricerca . . . . . . . . . . . . . . . . . . . . . . . . . . . 345.8.2 Estrazione dei tag da un titolo . . . . . . . . . . . . . . 345.8.3 Ricerca dei tag correlati . . . . . . . . . . . . . . . . . 355.8.4 Visualizzazione delle stanze . . . . . . . . . . . . . . . 35

5.9 Considerazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

Appendice A 37Il file main.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37Il file libri.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44Il file stanze.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57Codice javascript per Google Maps . . . . . . . . . . . . . . . . . . 67Il file di configurazione app.yaml . . . . . . . . . . . . . . . . . . . 68

Bibliografia e sitografia 70

Page 4: Google App Engine: una soluzione alternativa alla creazione di web application.

Capitolo 1

App Engine SDK

Da sempre la Google Inc. cerca di rendere disponibile ai suoi utenti, tutte lenuove tecnologie che si affacciano al “mondo” dell’informatica.In questi ultimi anni si è aperta una nuova frontiera sul web: il cloud com-puting.Il team di App Engine, fornisce un Software Development Kit (in breve SDK)per creare applicazioni web, sfruttando le sole potenzialità del linguaggio Py-thon1 e un semplice framework che consente alle classi software di interagirecon le pagine html. Con App Engine l’azienda californiana di MountainView, punta a fornire un valido strumento, in alternativa a strumenti simili,già messi a disposizione da altre aziende americane leaders nel settore infor-matico. Questo kit permette a coloro i quali ne usufruiscono, di utilizzare iservers dell’azienda, e sfruttare, come detto in precedenza, le caratteristichedel cloud computing, le peculiarità di big table (Il database ad oggetti creatoproprio dagli ingegneri dell’azienda americana). e la moltitudine di servizisviluppati da Google Labs, già a disposizione di tutti i possessori di un googleaccount.Sono state messe a disposizione le API per il servizio di mail, per quello dimanipolazione delle immagini, per la gestione degli account con Google, eper l’itegrazione di altri servizi molto comuni agli utenti della rete dei giorninostri. È ad esempio possibile integrare i servizi di youtube, calendar, didocs, contacts, base e picasa web album, oltre naturalmente a tutti quelli giàdisponibili in precedenza.Al primo impatto con questo strumento, si potrebbe erroneamente pensa-re che non vi sia nessuna novità in questo strumento, e che la creazione diweb application era già possibile numerosi anni addietro. Una delle verenovità nell’utilizzare questo SDK è quella del poter sfruttare un linguaggio

1Al momento è il solo linguaggio supportato anche se pare, in futuro, sia previstal’apertura ad altri linguaggi.

1

Page 5: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 1. APP ENGINE SDK 2

come python, senza che questo venga abbinato ad altri linguaggi, per crearee interagire con pagine web. Tutto ciò abbinato al poter sfruttare i servergoogle per le proprie applicazioni utilizzando sistemi distribuiti, ma trattan-doli come sistemi standard e tralasciando gli oneri delle varie configurazionie 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 scritturadi applicazioni web, permette alta scalabilità e offre strumenti di ammini-strazione utili e funzionali grazie al pannello di controllo disponibile per ogniapplicazione.

1.1 Funzionamento del kitIl primo passo da compiere per iniziare a creare un’applicazione con questokit, è quello di scrivere il file di configurazione. Ogni applicazione, per poterfunzionare, richiede un file denominato app.yaml (Vedere appendice A, filedi configurazione app.yaml, per avere un’esempio concreto).Questo documento testuale contiene le informazioni base dell’applicazione(Nome, versione, runtime python necessarie, ecc ecc. . . ),i percorsi delle car-telle e dei file statici utilizzati dall’applicazione e le corrispondenze tra i pathdel sito web che possono essere digitati con i file contenenti le classi pythonche catturano la richiesta ed eseguono le operazioni necessarie (Non ultimoil redirect al documento html appropriato alla pagina richiesta). Fatto ciò sipotrà iniziare a scrivere il primo file python, il quale corrisponderà al percorso“\”ne file di configurazione appena creato.

Page 6: Google App Engine: una soluzione alternativa alla creazione di web application.

Capitolo 2

Servizi offerti da App Engine

2.1 Google AccountAll’interno del kit di sviluppo, sono state messe a disposizione delle librerieper consentire al programmatore di non doversi preoccupare della gestionedel login alla propria web application da parte dei suoi utilizzatori. Con unasemplice funzione si può controllare se l’utente in questione è loggato congoogle oppure deve effettuare il login o la registrazione. Per quest’ultimocaso esiste una funzione (sempre integrata nell’SDK) che crea un link allapagina di Google Account, dedicata alla propria applicazione, che una voltaeseguito il login, consente di ritornare ad una determinata pagina voluta. Inquesto modo gli utenti non necessitano di creare nuovi account, ma sfruttanolo stesso utilizzato per gmail, o più in generale per i servizi di Google.Tutto ciò elimina una buona parte di lavoro al programmatore, il quale nondovrà preoccuparsi di tutti i problemi relativi al login (sql injection, per fareun esempio su tutti) e sarà utile all’utente finale perchè non dovrà creare unnuovo account, e di conseguenza ricordare nuove credenziali d’accesso.Le applicazioni possono così accedere all’indirizzo email dell’utente, al suonickname e stabilire se l’utente loggato può accedere ad aree riservate agliamministratori per una determinata applicazione o meno.Con poche e brevi istruzioni si può facilmente riassumere il funzionamentodi google account:

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

Page 7: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 4

Figura 2.1: Esempi delle funzioni di manipolazione delle immagini.

else:print "<a href=\"%s\">Accedi o registrati</a>"

%users.create_login_url("/")

2.2 ImagesMolte 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’immaginee addirittura usare la funzione di miglioramente automatico. Quest’ultimaaggiusta colore, luminosità e saturazione dell’immagine.

2.3 MailIn alcuni casi risulta utile poter contattare un utente della propria web appli-cation, che sia per comunicazioni generali o per mettere in comunicazione dueo 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

Page 8: Google App Engine: una soluzione alternativa alla creazione di web application.

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 fileimage/x-ms-bmp bmp

text/css csstext/comma-separated-values csv

image/gif giftext/html htm htmlimage/jpeg jpeg jpg jpe

application/pdf pdfimage/png png

application/rss+xml rsstext/plain text txt asc diff potimage/tiff tiff tif

image/vnd.wap.wbmp wbmp

2.4 MemcacheLa maggior parte delle applicazioni web che mirano ad avere alte prestazionie a gestire grandi quantità di dati, spesso ricorrono all’utilizzo di un serviziodi memorizzazione degli stessi all’interno della cache di un server, in modotale 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 suautilità nel momento in cui i dati sul database cambiano. In questo caso al-la successiva richiesta, bisognerà provvedere a rieseguire la query e salvarlinuovamente nella cache del server.Il servizio di memcache può anche essere usato per caricare valori temporaneie non accetta necessariamente solo dati letti dal db. Si può usare tranquilla-mente in alternativa alle variabili di sessione, col vantaggio di non rischiareche chiunque possa accendere a contenuti riservati, al contrario di quantopuò succedere servendosi delle variabili di sessione. Inoltre queste ultime, nelcaso l’utente non abbia i cookies attivati, non funzionerebbero, al contrariodella memcache che può essere utilizzata indipendentemente dall’attivazioneo 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 diamministrazione dell’applicazione o possono avere una scadenza settata al

Page 9: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 6

momento dell’aggiunta in cache del valore. È altresì possibile salvare chiavimultivalore (ad esempio con un prefisso nel nome uguale, cambiando solo ilsuffisso, o viceversa) oppure creare variabili-contatori e incrementarle o de-crementarle senza dover cancellarne il valore e reinserirlo manualmente. Inaggiunta a tutto ciò viene fornita anche una funziona per ottenere le statisti-che di utilizzo della cache comprensive di percentuali di successi e insuccessidelle richieste, numero di oggetti salvati, tempo di permanenza dei vari og-getti nella cache e numero di byte salvati. Un possibile utilizzo di questoservizio è rappresentato dal seguente codice python:

greetings = memcache.get("greetings")if greetings is not None:

return greetingselse:

greetings = self.render_greetings()if not memcache.add("greetings", greetings, 10):

logging.error("Memcache set failed.")return greetings

2.5 DatastoreIl datastore di App Engine fornisce un’interprete di query e una memorizza-zione dei dati tramite transazioni. Il tutto lavora sempre nei sistemi scalabilidi Google. L’interfaccia python fornisce anche uno strumento di modelliz-zazione dei dati e un linguaggio sql-like chiamato gql1. Questo datastoreconsente di effettuare qualsiasi tipo di operazione su oggetti di dati, i qualirappresentano le comuni entità. Un’entità possiede una o più proprietà, lequali rappresentano i valori delle entity e possono appartenere ad una vastatipologia, a scelta tra i tipi supportati.

1L’acronimo sta per Generic Query Language

Page 10: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 7

Proprietà Valore OrdinamentoStringProperty str Unicode (ASCII)

ByteStringProperty ByteString byteBooleanProperty bool False < TrueIntegerProperty int Numerico

longFloatProperty float Numerico

DateTimePropertyDateProperty datetime.datetime ChronologicalTimePropertyListProperty Se ascendente, dal <

StringListProperty lista(tipi supportati) Se discendente, dal >ReferenceProperty

SelfReferenceProperty db.Key (tipo, ID o nome, . . . )UserProperty users.User Per e-mail(Unicode)BlobProperty db.Blob nessunoTextProperty db.Text nessuno

CategoryProperty db.Category UnicodeLinkProperty db.Link UnicodeEmailProperty db.Email UnicodeGeoPtProperty db.GeoPt Latitudine, longitudineIMProperty db.IM Unicode

PhoneNumberProperty db.PhoneNumber UnicodePostalAddressProperty db.PostalAddress Unicode

RatingProperty db.Rating NumericoUna proprietà può essere referenziata ad una entità, permettendo la creazio-ne di relazioni con cardinalità uno-a-molti o molti-a-molti. Può inoltre averedei vincoli di validità.Il datastore può eseguire più istruzioni in una singola transazione ed eseguir-ne il rollback nel caso in cui qualche operazione non dovesse andare a buonfine. Questa caratteristica risulta particolarmente utile per le applicazioniweb distribuite, in cui può accadere che più utenti possano manipolare lostesso oggetto nello stesso istante. La scrittura sulla base dati è atomica. Leoperazioni di put(scrittura) e di delete(cancellazione) possono andare a buonfine oppure fallire (per varie ragioni), ma qualunque sia la motivazione, lamodifica viene persa e il contenuto del datastore non cambia.Per rendere attuabile questo processo basta semplicemente scrivere una fun-zione contenente le operazioni che si vogliono includere nella stessa transa-zione, come nell’esempio sottostante:from google.appengine.ext import db

Page 11: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 8

class Accumulator(db.Model):counter = db.IntegerProperty()

def increment_counter(key, amount):obj = db.get(key)obj.counter += amountobj.put()

q = db.GqlQuery("SELECT * FROM Accumulator")acc = q.get()

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 funzioneget, la quale prende come parametro di input la chiave dell’oggetto deside-rato. Come detto, ad un’entità viene associata una chiave, che la identificaunivocamente. 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 PolymorphicModels API, e quindi avere una o più sottoclassi di una superclasse. Tuttociò ovviamente supportato dal fatto che le query supportano e riconoscono le

2La funzione a cui ci si riferisce è entity.key(), dove con entity si vuole indicare il nomedella variabile rappresentante l’istanza di una determinata entità.

Page 12: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 9

classi gerarchiche, e possono restituire tutta la superclasse comprensiva deidati contenuti nella sottoclasse oppure solamente i dati di una sottoclassedesiderata.Il datastore supporta naturalmente anche gli indici. Questi ultimi possonoessere definiti manualmente dallo sviluppatore dell’applicazione tramite unapposito file di configurazione (denominato index.yaml). In aggiunta il siste-ma, riconosce le query complesse all’interno dell’applicazione, e nel caso vene sia la necessità, crea in automatico l’indice necessario. Questa proceduraautomatica entra in funzione quando il sistema riconosce query che utiliz-zano solamente operatori di confronto tutti positivi (o l’operatore sql IN ) otutti negativi, oppure query con un solo ordinamento su una proprietà, indi-pendentemente che esso sia ascendente o discendente. È inoltre consigliabilecreare indici quando nell’applicazione esistono:

• query con molteplici ordinamenti.

• query con ordinamenti decrescenti sulla chiave uno.

• query con più filtri di inuguaglianza su una proprietà e uno o più filtridi uguaglianza su altre proprietà.

• nel caso di tabelle che sfruttano l’ereditarietà, query con filtri di inu-guaglianza e filtri su entità-parenti.

In aggiunta, il server utilizzato localmente per sviluppare l’applicazione, creagli indici necessari, nel caso in cui le query dovessero fallire e non fossero giàstati creati precedentemente.

2.6 Admin ConsoleUna volta caricata l’applicazione on-line è a disposizione di tutti gli svilup-patori una funzionale console di amministrazione.Essa mette a disposizione una dashboard per verificare l’utilizzo dell’appli-cazione, con grafici che mostrano le richieste, gli errori, i byte trasmessi ericevuti, i secondi di cpu utilizzati e gli eccessi di quota tutti lungo una lineatemporale(in secondi o millisecondi). Inoltre sempre nella stessa pagina vi èun riassunto generale delle percentuali di quota raggiunte nelle 24 ore pre-cendenti.Tramite il menu della console si può accedere alla seconda pagina, ovveroquella dei riepiloghi sulle varie quote, in particolare vengono forniti ampidettagli su:

• Richieste

Page 13: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 2. SERVIZI OFFERTI DA APP ENGINE 10

• Datastore

• Mail

• Url Fetch

• Manipolazione di immagini (Images)

• Memcache

• Funzionamento dell’applicazione

Nell’ultima pagina del gruppo delle funzioni di riepilogo, si possono teneresotto controllo i logs dell’applicazione, ovvero la cronologia di tutte le richie-ste, gli errori, i debug e gli errori critici in cui è incappata l’applicazione.Il secondo gruppo di funzioni della console, riguarda il datastore, ed in par-ticolare è possibile prendere visione degli indici relativi alle varie tabelle eamministrare le entity del database, visualizzando tutte le istanze presentiper ogni entità e con la possibilità di eseguire query o cancellare tupla pertupla di ogni entity.Il terzo ed ultimo gruppo di funzioni, oltre a permettere di settare il titolodell’applicazione, consente di amministrare gli sviluppatori autorizzati perl’applicazione e di invitarne di nuovi, di gestire le varie versioni della webapp e decidere quale sia quella di default e, in ultimo, consente di prenderevisione dei log della console stessa, ovvero tutte le azioni che gli amministra-tori dell’applicazione svolgono (ordinati per data e ora). È inoltre possibilesettare un dominio aggiuntivo per l’applicazione nel qual caso essa ne avesseuno dedicato.

Page 14: Google App Engine: una soluzione alternativa alla creazione di web application.

Capitolo 3

Il cloud computing

Il cloud computing è una tecnologia che consente di avere a disposizione ri-sorse distribuite, (cpu o database ad esempio) le quali vengono viste comeun’unica risorsa. Il termine cloud deriva dall’inglese, significa nuvola e rap-presenta una implementazione delle risorse, definite in modo sommario, concaratteristiche sconosciute al reale utilizzatore e viste come un servizio for-nito su richiesta.L’architettura prevede server distribuiti che collaborano e vengono utilizzaticome se fossero un unico sistema. I vantaggi sono quelli di avere a disposio-ne risorse più prestanti o di capacità notevolmente superiori e di garantiremaggiore scalabilità all’applicazione, con pochi oneri da parte del program-matore. Inoltre un sistema “cloud” possiede una struttura notevolmente piùcomplessa ma, all’utilizzatore, appare come un sistema normale, che non ne-cessita di particolari precauzioni.Il cloud computer si è sviluppato di pari passo con altri concetti, come i Soft-ware as a Service1 e il web 2.0. Un esempio molto conosciuto di SaaS sonoi servizi forniti dall’azienda americana Salesforce.com ([19]).Uno strumento analogo ad App Engine invece, è stato creato da Amazon.com,con gli Amazon Web Services ([20], i quali vengono visti come un System asa Service, ovvero sistemi che vengono forniti come se fossero un servizio perl’utente.

3.1 Salesforce.comSaleforce è un’azienda americana leader nel settore dei CRM (Customer Rela-tionship Management), ovvero nei servizi di fidelizzazione del cliente. Questasocietà mette a disposizione diversi servizi per le aziende che vogliono crescere

1Software visti come servizio, o comunemente detti in gergo SaaS.

11

Page 15: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 3. IL CLOUD COMPUTING 12

Figura 3.1: Schema stilizzato del cloud computing.

nel mercato. Il CRM le aiuta nella gestione dei propri clienti, nella gestionedei rapporti con essi e nell’acquisizione di nuovi potenziali acquirenti.Salesforce.com è stata fondata nel 1999 e nel 2008 è stata riconosciuta comela 43esima azienda più grande al mondo tra le compagnie di sofware. Contafiliali 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 direttocon il cliente, migliorare la conoscenza del cliente attraverso l’estrazione deidati dal processo precedente e utilizzare tecnologie integrate per la comuni-cazione con il cliente (telefono, fax e e-mail). Tutto ciò viene fornito senzal’installazione di software nelle aziende che si affidano a loro, bensì garanten-do servizi online, che sfruttano il cloud computing, e permettono alle variesocietà di caricare le loro applicazioni sui server di salesforce e utilizzarleonline.

Page 16: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 3. IL CLOUD COMPUTING 13

Figura 3.2: L’home page italiana di Salesforce.com

Page 17: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 3. IL CLOUD COMPUTING 14

Figura 3.3: L’home page di Amazon Web Services

3.2 Amazon Web ServicesAmazon Web Services (AWS) è un servizio che consente ai programmatori discrivere web application, utilizzando strumenti di sviluppo forniti da Ama-zon, e renderle pubbliche e disponibili, sui server dell’azienda americana.Amazon mette a disposizione diversi web service per facilitare la creazionedi queste applicazioni ai suoi utilizzatori.Elastic Compute Cloud, meglio conosciuto come EC2 è il web service checonsente di scrivere le applicazioni e di interfacciarsi con gli altri strumentidi AWS. Esso sfrutta le potenzialità del linguaggio java per la scrittura delcodice.Come DBMS, viene fornito uno strumento chiamato Simple DB, dal funzio-namento del tutto analogo al Datastore di Google App Engine il quale deveessere combinato al Simple Storage Service (Amazon S3) per consentire alprogrammatore di memorizzare i dati utilizzati dalla propria applicazione.Questo strumento, utilizza in larga scala il cloud computing e, come App En-gine, consente all’utilizzatore di creare utility semplici, dalle alte prestazionie altamente scalabili, senza particolari oneri da parte sua.

Page 18: Google App Engine: una soluzione alternativa alla creazione di web application.

Capitolo 4

Il DBMS di Google: Bigtable

4.1 IntroduzioneBigtable ([10]) è un Database Management System (DBMS) distribuito, co-struito per lavorare con dati strutturati. Esso è nato nel 2004 nei laboratoridi Google ed è stato disegnato per essere altamente scalabile e per supportaregrandi 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 GoogleDocs, per citarne alcuni. Queste applicazioni effettuano numerosi tipi diffe-renti di richieste, sia in termini di dimensione dei dati (che possono variareda 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 (dallafornitura di dati in real-time alla rielaborazione di una grande mole di dati).Nonostante questa ampia varietà di esigenze, il sistema ha fornito un’ottimasoluzione, molto flessibile e dalle alte prestazioni per ognuno di questi pro-dotti di Google. Oltre alle qualità già menzionate, gli sviluppatori dei variprogetti sopra citati, hanno riscontrato una altissima affidabilità di questoDBMS. I clusters di Bigtable sono suddivisi in qualche migliaio di servers epossono immagazzinare diverse centinaia di terabyte di dati. Questo sistemacombina insieme diverse strategie utilizzate nei comuni database. Paralleldatabases 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. Essonon supporta un modello relazionale completo, ma fornisce i dati ai clientseguendo un semplice modello che tiene conto del formato e del layout di ogniproprietà rappresentata nel db. I dati sono indicizzati utilizzando i nomi di

1Si parla di un supporto a vari petabytes distribuiti su diversi servers.

15

Page 19: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 4. IL DBMS DI GOOGLE: BIGTABLE 16

righe e colonne che possono essere stringhe arbitrarie. Bigtable può trattareanche i dati come stringhe indefinite sebbene spesso i client convertano varieforme di dati strutturati e semi strutturati in stringhe. I client possono inol-tre controllare localmente i loro dati prendendosi cura di definirne lo schemapiù adatto. Infine esso permette ai client di stabilire dinamicamente se i da-ti saranno da memorizzare seguendo lo schema in-memory o il tradizionalesalvataggio su disco.

4.2 Modello di rappresentazione dei datiBigtable usa una sorted map multidimensionale, sparsa, distribuita e persi-stente. La mappa è indicizzata per chiave di riga, chiave di colonna e istantedi inserimento.I valori sono rappresentati al suo interno come array di bytesindefiniti.(row:string,column:string,time:int64) → string

4.2.1 Righe

Le chiavi di riga in una tabella sono stringhe di dimensioni arbitrarie chevariano tra i 10 e i 100 bytes. Le letture/scritture di una singola di rigasono 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. Ogniriga è suddivisa in intervalli, singolarmente chiamati tablet, che sono l’unitàdi bilanciamento e di carico. Di conseguenza le letture di piccoli insiemi ditablet sono efficienti e richiedono tipicamente comunicazioni con un numeroristretto di macchine. I client possono così sfruttare questa proprietà sta-bilendo le chiavi di riga in modo da ottenere un posizionamento contiguoper facilitare i futuri accessi ai dati. Un possibile esempio può essere citatoprendendo spunto dall’indicizzazione del web: le url delle pagine di dominiidentici vengono raggruppate in righe contigue capovolgendo le componentidell’url stessa. Ad esempio l’url della pagina maps.google.com/index.htmlverrà salvata come com.google.maps/index.html in modo tale che tutte lepagine appartenenti a questo dominio verranno salvate in posizioni vicine,rendendo più efficienti le successive letture.

4.2.2 Famiglie di Colonne

Le chiavi delle colonne sono raggruppate in insiemi chiamati column familiesle quali formano l’unità base di accesso. Tutti i dati salvati in una famiglia

Page 20: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 4. IL DBMS DI GOOGLE: BIGTABLE 17

di colonne sono di solito dello stesso tipo (vengono compresse nella stessafamiglia). Una famiglia di colonne deve essere creata necessariamente primache i dati possano essere salvati e raggruppati al suo interno. Queste famigliesono state progettate per raggruppare al loro interno un numero ristretto dicolonne(nell’ordine delle centinaia al massimo) e per cambiare raramente unavolta 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 venirecreata 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 vienerappresentato come intero a 64bit, e può essere generato in automatico dalsistema, rappresentando l’instante di tempo real in microsecondi oppure puòvenire assegnato dal client che sta scrivendo il dato. Una stessa versione diun 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 diversioni dello stesso dato che si vogliono mantenere, dato che in Bigtableesiste una sorta di garbage collector dedicato alla funzione di eliminare i datiridondanti che superano il numero di ripetizioni scelte dall’utente.

4.3 Struttura internaBigtable usa Google File System (GFS,[11]), il file system distribuito creatoda Google, per salvare i file di log e i file di dati.Come abbiamo già detto in precedenza, questo DBMS è un sistema distri-buito; un suo cluster opera generalmente su insieme condiviso di calcolatori,che ospitano una grande varietà di altre applicazioni distribuite e i processidi Bigtable condividono sovente queste macchine con processi di altre appli-cazioni. Bigtable è controllato da un sistema di gestione clusterizzato chegestisce lo scheduling, la gestione delle risorse sulle macchine condivise, lagestione dei rapporti d’errore dei calcolatori e il monitoraggio dello stato deisistemi. I dati vengono salvati in Bigtable utilizzando un file che rispettanoun formato chiamato SSTable, esso fornisce una mappa persistente, ordina-ta e immutabile di corrispondenze tra chiavi e valori, dove entrambe sonostringhe arbitrarie. Al suo interno, ogni SSTable contiene una sequenza diblocchi, ciascuno delle dimensioni di 64Kb, sebbene la dimensione del blocco

Page 21: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 4. IL DBMS DI GOOGLE: BIGTABLE 18

sia configurabile. Alla fine di ogni SSTable esiste un indice dei blocchi cheviene usato per localizzarli, questo indice viene caricato in memoria quan-do l’SSTable viene aperto. Una ricerca può essere effettuata mediante unasola scansione del disco, infatti prima viene cercato il blocco desiderato effet-tuando una ricerca binaria nell’indice all’iterno della cosidetta in-memory, esuccesivamente leggendo il blocco desiderato dal disco. In alternativa, si puòconfigurare il sistema in modo da mappare in memoria un SSTable, il chepermette di effettuare le ricerche in memoria senza dover toccare il disco.Bigtable prevede anche una gestione del lock distribuita, tramite un serviziochiamato Chubby. Esso consiste in cinque repliche attive, una delle quali ènominata master e si occupa attivamente delle richieste. Questo servizio usaun particolare algoritmo (Denominato paxo) per fare in modo che le replicheslave si attivino nel caso in cui il master vada in errore. Chubby fornisceun 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. Perpoter utilizzare questo servizio ogni client mantiene una sessione, e quandoquesta 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 ilbootstrap dai dati di Bigtable, scoprire quali sono i server adibiti ai tablete per quanto tempo, salvare le informazioni relative allo schema di Bigtablee salvare le liste di controllo degli accessi. Se Chubby per qualsiasi motivodovesse diventare inattivo per un lungo periodo di tempo, allora Bigtablediventerebbe inutilizzabile2.

2Recenti studi condotti dai Google labs hanno misurato la percentuale di inattività delsistema in 0.0047%. Le cause principali sono stati problemi di reti o periodi di inattivitàdi Chubby.

Page 22: Google App Engine: una soluzione alternativa alla creazione di web application.

Capitolo 5

Un caso di studio: Sellbook

5.1 IntroduzioneUno dei problemi che afflige spesso gli studenti universitari, sono i costi chesi devono sostenere per l’acquisto dei libri. Spesso i testi acquistati vengonoutilizzati per i corsi ad essi adibiti e poi finiscono per cadere nel dimenticatoioe non essere più sfruttati. Fermo restando che un libro può sempre risultareutile per svariati motivi, si è pensato di creare uno strumento a disposizionedi tutti, per ammortizzare il costo di acquisto dei tomi universitari. Nasce perquesto l’idea di una semplice web application, che metta in comunicazionegli studenti in possesso di libri diventati ormai inutili, e quelli che cercano undeterminato testo per il loro corso universitario che si apprestano a seguire.Sellbook si propone come un semplice portale in cui chiunque può venderei volumi in proprio possesso tramite l’inserimento di un semplice annunciooppure 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 adaltre università.

5.2 Specifica dei requisitiCome detto in precedenza, l’applicazione dovrà consentire di inserire annun-ci di libri in vendita, ricercare un determinato libro (aiutando l’utente nellaricerca, fornendo diverse tipologie di ricerca), consentire a due utenti di co-municare tra di loro.In particolare durante l’inserimento di un libro si devono poter specificare leseguenti caratteristiche:

• Titolo

19

Page 23: Google App Engine: una soluzione alternativa alla creazione di web application.

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 cuisono contenuti i libri, e verranno utilizzate per suddividere i testi in base alcorso di appartenenza. Tramite una navigazione all’interno delle stanze sipotranno ricercare i libri a cui si è interessati. Ciò permetterà all’utente checerca un determinato volume relativo ad un corso di studi, di trovarlo più ra-pidamente, escludendo libri a cui sicuramente non è interessato. Scendendodi livello all’interno delle categorie, la ricerca dei libri, sarà via via semprepiù raffinata.

5.3 Sviluppo dell’applicazioneApp Engine al momento supporta solamente python come linguaggio di pro-grammazione, quindi non vi sono state alternative nella scelta del linguaggio.Il vantaggio di questo kit di sviluppo è che in esso sono contenuti tutti glistrumente necessari per creare una web application partendo dal nulla. Big-table è il DMBS ad oggetti incluso nella SDK, e tutte le librerie utilizzatesono quelle fornite insieme al tool. Le pagine HTML sono scritte rispettandole specifiche relative all’XHTML 1.0 Strict.Si è voluto inoltre cercare di progettare e sviluppare l’applicazione con stru-menti open source. Nella fase di progettazione, i diagrammi in UML sonostati realizzati con Umbrello UMLModeler. Per la scrittura del codice pythone html è stato utilizzato GEditor con alcuni utili plugin per la programmazio-ne. Il sistema operativo utilizzato per lo sviluppo è Ubuntu Linux 8.04-8.10.

1Altrimenti denominata stanza.

Page 24: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 21

Figura 5.1: Il banner di SellBook

5.4 Ricerca dei casi d’usoDurante la fase di progettazione sono emersi due attori per l’applicazione:l’utente e il sistema. Lo studio dei requisiti dell’applicazione ha portatoall’individuazione dei seguenti casi d’uso (suddivisi per categorie di interesse):Tag:

• 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

Page 25: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 22

Figura 5.2: Diagramma dei casi d’uso

• Ricerca_per_Keywords

Comunicazione tra utenti:

• Contattare_proprietario_libro

• Segnalare_richiesta_altro_utente

• Inviare_Mail

5.5 Specifiche dei casi d’usoAggiornamento_Tag:Attori: SistemaPrecondizioni: Viene inserito un nuovo libro, o ne viene eliminato uno giàpresente.Flusso: Se viene inserito un nuovo libro, una volta estratto un tag dal titolo,

Page 26: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 23

già presente tra i tag registrati nella base dati, il sistema aggiorna il contatoredel tag.Se viene eliminato un libro, il sistema cerca i tag ad esso associati e li eliminanel caso abbiano il contatore uguale ad 1, o decrementa il contatore di unaunità in caso contrario.

Nuovo_Tag:Attori: SistemaPrecondizioni: 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 sistemalo aggiunge all’elenco dei tag.

Nuovo_Libro:Attori: Utente, SistemaPrecondizioni: Un utente vuole inserire un nuovo libro in vendita.Flusso: Viene presentata la pagina di inserimento di un nuovo libro, e lamappa di Google Maps per la ricerca della località in cui si trova il libro invendita. Una volta premuto il pulsante di inserimento del libro, si attiva ilcaso d’uso Nuovo_Libro o Aggiornamento_Tag a seconda della necessità.

Editare_Libro:Attori: Utente, SistemaPrecondizioni: Si deve modificare un libro precedentemente inserito.Flusso: Viene eseguita una ricerca del libro da modificare tramite la chiaveassociata al libro interessato. Vengono caricati i dati precedentemente inseritinegli appositi campi della pagina di modifica. Quando l’utente conferma lamodifica dei dati, questi vengono sovrascritti ai precedenti nella base dati.

Log-in:Attori: UtentePrecondizioni: 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 eottenere un account. Fatto ciò l’utente viene reindirizzato alla pagina da cuiproveniva.

Log-out:Attori: UtentePrecondizioni: Si vuole uscire dalla propria area personale.Flusso: L’utente viene reindirizzato alla pagina di Google Accounts. Vieneeseguito il log-out dall’applicazione e in seguito si verrà reindirizzati allapagina da cui proveniva la richiesta di log-out.

Crea_Nuova_Categoria:Attori: Utente, Sistema

Page 27: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 24

Precondizioni: È necessario aggiungere una nuova stanza. L’utente ha effet-tuato con successo l’accesso al sistema.Flusso: Viene presentata una pagina che permette di inserire il nome dellastanza. Quando l’utente conferma l’inserimento, i dati della stanza vengonoregistrati nella base dati.

Edita_Categoria:Attori: Utente, SistemaPrecondizioni: È necessario modificare una stanza. L’utente ha effettuato consuccesso l’accesso al sistema. L’utente è il creatore della stanza o possiedeprivilegi di amministrazione.Flusso: Viene presentata una pagina che permette di inserire il nome dellastanza. Quando l’utente conferma l’inserimento, i dati della stanza vengonoregistrati nella base dati.

Elimina_Categoria:Attori: Utente, SistemaPrecondizioni: Si vuole eliminare una stanza. L’utente ha effettuato consuccesso l’accesso al sistema. L’utente è il creatore della stanza o possiedeprivilegi di amministrazione.Flusso: Quando un utente clicca sul simbolo “-” posto a fianco di una stanza,questa viene cancellata dalla base dati.

Ricerca_Libro:Attori: UtentePrecondizioni: Vi è la necessità di ricercare un determinato libro o un insiemedi libri.Flusso: L’utente è nella condizione di poter scegliere fra tre tipologie distintedi ricerca, quella che si addice di più alle sue esigenze.

Visualizza_Libro:Attori: UtentePrecondizioni: L’utente vuole visionare le informazioni dettagliate relativead un libro.Flusso: Il caso d’uso in questione può venire attivato in seguito ad una ricerca,o tramite link diretto al libro da visualizzare. Dopo aver selezionato il libroda visualizzare, vengono presentati i dettagli del libro.

Ricerca_per_Tag:Attori: UtentePrecondizioni: L’utente vuole ricercare un libro selezionando un tag dall’ap-posito spazio riservato al tag cloud.Flusso: L’utente seleziona un tag tra quelli proposti. Viene attivata la ricercasul database, di tutti i libri il cui titolo corrisponde alla parola scelta.

Ricerca_per_Categorie:Attori: Utente

Page 28: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 25

Precondizioni: L’utente vuole ricercare un libro navigando tra le stanze pre-senti.Flusso: Partendo dall’elenco delle stanze, l’utente può ricercare la presuntastanza interessata. Dalla pagina della descrizione di una stanza si può navi-gare verso stanze più specifiche (se ne esistono) oppure verso stanze antenatedi quella che si sta visualizzando. Una volta trovata la stanza cercata, se con-tiene libri, questi ultimi verranno proposti all’utente. Il link dei vari volumi,rimanderà alla pagina descrittiva del libro.

Ricerca_per_Keywords:Attori: UtentePrecondizioni: L’utente vuole ricercare un libro immettendo delle parole chia-ve.Flusso: Tramite il campo di testo apposito, l’utente può inserire parole chiaveche pensa possano corrispondere al libro che interessa. Avviando la ricerca, ilsistema analizza le parole inserite e le confronta con i vari titoli dei libri. Seuna porzione di testo, inserita nel suddetto campo, corrisponde ad una parolapresente nel titolo di un determinato libro, questo verrà proposto nell’elencodei risultati della ricerca.

Contattare_proprietario_libro:Attori: Utente, SistemaPrecondizioni: L’utente vuole stabilire un contatto con il proprietario di unlibro a cui è interessato.Flusso: Dalla pagina della descrizione di un libro, un utente può scrivereun messaggio indirizzato al proprietario del testo in questione. Questo casod’uso attiva Inviare_Mail.

Segnalare_richiesta_altro_utente:Attori: SistemaPrecondizioni: È stato scritto un messaggio da un utente indirizzato al pro-prietario di un libro.Flusso: Viene presentato nel menù dall’applicazione, un contatore che rap-presenta il numero di messaggi di ricevuti dagli altri utenti dell’applicazione.

Inviare_Mail:Attori: SistemaPrecondizioni: È stato scritto un messaggio da un utente indirizzato al pro-prietario di un libro.Flusso: Il sistema inoltra il messaggio scritto da un utente, al proprietariodel libro contattato, tramite e-mail.

I primi due casi d’uso, riguardano la taggatura dei libri. I tag sono pa-role chiave estratte in automatico dal titolo ogni qualvolta viene inserito unnuovo libro. Ogni parola presente in un titolo può essere condivisa da piùtitoli. Perciò potrebbe risultare utile ricercare mediante i tag, un libro che

Page 29: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 26

riguarda un determinato argomento, rappresentato da una parola significa-tiva. Ad esempio, se uno studente è alla ricerca di un volume che tratti illinguaggio uml, potrà cliccare sulla parola uml tra i tag, e trovare tutti i libriche all’interno del titolo contengono la suddetta parola.I due casi d’uso successivi, riguardano l’inserimento, la modifica o la cancel-lazione dei testi, per gli utenti che vogliono inserire un annuncio di vendita.Immediatamente succesivi a questi, vi sono i due casi d’uso che riguardanol’accesso e l’uscita dall’area personale di ogni utente. Grazie a questi, l’uti-lizzatore potrà inserire o modificare i propri volumi da vendere, controllarese sono state inviate richieste per un proprio libro da parte di altri utenti eaggiungere o modificare le stanze presenti nel sistema.I tre casi d’uso riguardanti le categorie, sono quelli che prendono in analisi lagestione delle stanze. Come per i libri, anche qui è possibile inserire, modifi-care o eliminare una stanza. La scelta implementativa fatta in questo caso èstata quella di permettere a chiunque di poter inserire una stanza per rendereindipendente il sistema e non renderlo suddito dell’attività di un’amministra-tore. La modifica o la cancellazione di una stanza sono invece consentite alsolo utente responsabile della creazione della stanza in questione e all’ammi-nistratore del sistema, che in questo caso può moderare e mantenere pulito,ordinato e senza ridondanze l’elenco delle stanze presenti.Immediatamente sotto vengono presentati i casi d’uso relativi alla ricerca diun libro con le varie tipologie che consentono ad un utente di trovare il titoloa cui egli è interessato percorrendo tre diverse strade: una ricerca mirata laquale consente di immenttere le parole da cercare, una ricerca per stanze diappartenenza dei libri in modo da navigare all’interno delle aree di interessericoperte dal testo che si vuole cercare e una più generica, per affinità diparole (tag).In conclusione vi sono i casi d’uso riguardanti la comunicazione tra i cosi-detti “venditore” e “compratore”. Questa avviene tramite l’invio di messaggie-mail da una pagina dell’applicazione.

5.6 Interazione utente/applicazione

5.6.1 Homepage

La pagina principale dell’applicazione è stata pensata per essere semplice eallo stesso tempo intuitiva.Essa è suddivisa in più sezioni:

• Banner: contiene un link che rimanda alla home page, questa sezione

Page 30: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 27

Figura 5.3: L’homepage di SellBook

Figura 5.4: Il form di ricerca di SellBook

sarà presente in tutte le pagine dell’applicazione, per consentire unabuona navigabilità agli utenti.

• Ricerca: è un semplice campo testuale. Una volta che l’utente inserisceun valore e preme il pulsante “Cerca”, viene avviata la routine di ricerca,che porterà ad una pagina con una lista dei risultati trovati.

• Tag Cloud: contiene l’insieme delle parole chiave estratte dai titoli deivolumi presenti nel sistema. La dimensione di ogni parola è determi-nata dal numero di ripetizioni della parola stessa all’interno di tutti ititoli dei libri presenti. Ciò aiuterà l’utente a capire quanto ogni pa-rola è comune, e di conseguenza quanti libri sono accomunati da queltermine.Quando si clicca su un tag viene avviata la ricerca, e vengono succesi-

Page 31: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 28

Figura 5.5: Lo spazio dedicato alla visualizzazione dei tag

Figura 5.6: Interfaccia per accedere o disconnettersi dal sistema

vamente presentati i libri che sono associati al tag selezionato. Sotto irisultati appare un altro specchietto, del tutto simile a quello del tagcloud, dove vengono proposti i tag correlati. Questi tag non sono al-tro che tutte le parole associate ai libri trovati dalla ricerca. Accantoad ogni tag correlato, appare tra parentesi un numero, il quale indicaquanti dei libri sopra citati, sono associati alla parola in questione.

• Log-in/Log-out: questa sezione cambia a seconda se l’utente ha giàeffettuato l’accesso o meno, nel sistema di Google Account. In casoaffermativo, vengono visualizzate l’email usata per l’accesso, un linkper accedere alla pagina di riepilogo dei libri inseriti o di inserimento diun nuovo testo e un link per effettuare la disconnessione dal sistema. Incaso contrario viene presentato un semplice link per effettuare l’accesso.

• Ultimi libri inseriti: questo specchietto, consente di tenere traccia del-l’attività del sistema. Permette di visualizzare gli ultimi 5 libri inseriti,e fornisce un’alternativa in più all’utente per trovare un libro. Questalista è formata infatti da link, che reindirizzano alla pagina contenentele informazioni dettagliate su un determinato volume.

• Menu: sulla parte destra della pagina si può trovare il menù dell’appli-

Figura 5.7: Specchietto per la visualizzazione degli ultimi 5 libri inseriti

Page 32: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 29

Figura 5.8: Il menù di SellBook

cazione. Se l’utente ha effettuato il login, qui appariranno 3 diciture,di cui due link. Il primo per accedere alla pagina dei libri, mentre il se-condo per accedere alla sezione delle stanze. La terza voce rappresentail contatore di contatti ricevuti al proprio indirizzo e-mail, da parte diutenti interessati ad un proprio volume in vendita su SellBook. Clic-cando sull’icona a fianco del numero, verrà azzerato questo contatore.Nel caso un utente non avesse effettuato il login, il menù conterrà so-lamente il link per poter visualizzare le stanze presenti.Questa sezione sarà presente in ogni parte del sito, in modo tale dafornire all’utente la possibilità di raggiungere velocemente le funzioni asua disposizione nell’applicazione.

5.6.2 Libri

La pagina di inserimento di un nuovo libro è suddivisa in due sezioni, il formdi 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 dellastanza 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’utentepotrà indicare il luogo esatto in cui si trova il libro. Quest’ultima informa-zione potrebbe risultare utile per definire il luogo di incontro tra acquirentee 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 ognibreve descrizione dei volumi, vi sono due pulsanti, uno per la modifica delleinformazioni 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 informazionirelative 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

Page 33: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 30

Figura 5.9: Form di inserimento di un nuovo libro

Figura 5.10: La lista di riepilogo dei libri inseriti dall’utente

Page 34: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 31

Figura 5.11: La lista delle stanze presenti

per l’inserimento, con la differenza che le informazioni e la mappa di google,sono solamente visualizzabili e non modificabili. In aggiunta vengono anchevisualizzati i tag associati a quel libro. Sotto la mappa è anche presente ilpulsante che permette di contattare il prorietario del libro.

5.6.3 Stanze

La pagina delle stanze presenta un semplice elenco gerarchico. Ogni stanzaprincipale ha, sotto di se, tutte le sue stanze “figlie”. Le stanze figlie si ri-conoscono perchè hanno un leggero margine di rientro, rispetto alle stanzegerarchicamente superiori ad essa. Come si può notare dalla figura [5.11],ogni voce ha al suo fianco tre pulsanti. Il simbolo “+”serve per aggiungereuna sottostanza a quella indicata. Il simbolo “-”, al contrario, serve per eli-minare la stanza in questione. L’ultima icona consente invece di modificarela descrizione della stanza. Le ultime due azioni, come detto in precedenzasono consentite solamente al creatore della stanza e agli utente con privilegidi amministrazione, mentra l’aggiunta di una sottostanza o di una stanzaradice, è consentita a chiunque abbia effettuato l’accesso. Se un visitatore,non ha inserito le sue credenziali d’accesso, potrà solamente vedere l’elencodelle stanze e i dettagli per ogni stanza.Per vedere i dettagli di una stanza, basta semplicemente cliccare sul nomedella stessa. Verrà aperta una pagina in cui vengono mostrate, oltre alladescrizione, 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 sonocontenuti nella stanza, se presenti.

Page 35: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 32

Figura 5.12: La visualizzazione dei dettagli di una stanza

5.7 Schema della base datiLa base dati dell’applicazione presenta 5 entità, descritte dal codice pythonsottostante, utilizzato per descrivere lo schema del database in App Engine:

class Rooms(db.Model):descr=db.StringProperty()listaFigli=db.ListProperty(db.Key)father=db.SelfReferenceProperty()livello=db.IntegerProperty()owner=db.UserProperty()

Il campo listaFigli è una lista contenente le chiavi di ogni stanza “figlia”diuna data istanza di Rooms. È del tutto simili alla ReferenceProperty2 ma,siccome le stanze possono avere più sottostanze, non vi era altro modo didescrivere questa situazione.Il campo user contiene le informazioni relative ad un utente di Google Ac-counts.

class Book(db.Model):title=db.StringProperty()author=db.StringProperty()version=db.IntegerProperty()year=db.IntegerProperty()room=db.ReferenceProperty(Rooms)

2Utilizzata ad esempio nel campo father, per descrivere l’istanza padre di quella inquestione.

Page 36: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 33

casaEd=db.StringProperty()img=db.BlobProperty(required=False)

Il campo room contiene il riferimento ad un’istanza dell’entità Rooms, men-tre il campo img contiene l’immagine della copertina di un libro. Vienespecificato tra parentesi che può contenere un valore nullo.

class Tag(db.Model):descr=db.StringProperty()nTag=db.IntegerProperty()

Questa tabella tiene traccia delle parole chiave estratte dai titoli. Attraversoil campo nTag si tiene conto del numero di ripetizioni di una determinataparola. Questo metodo consente di velocizzare notevolmente la costruzionedello strumento del Tag cloud.

class TaggedBook(db.Model):tag=db.ReferenceProperty(Tag)book=db.ReferenceProperty(Book)

TaggedBook viene utilizzata per mantenere i riferimenti tra un determi-nato libro e un tag ad esso associato. Tutti e due i campi sono di tipoReferenceProperty.

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)

L’entity SellBook, risulta utile per immagazzinare le informazioni relativealla vendita di un libro.La proprietà book si riferisce all’istanza del libro in vendita, mentre ownerall’utente proprietario del libro in questione.Il campo dataSell rappresenta la data di creazione dell’istanza nella base dati.Ciò è consentito infatti dalla clausola auto_now_add=True, che alla scritturadei dati sul db, registra in questo campo l’istante di avvenuta scrittura.L’ultimo campo (location) rappresenta le coordinate geografice restituite daGoogle Maps, quando l’utente inserisce il luogo in cui il libro si trova.

Page 37: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 34

5.8 Presentazione degli algoritmi utilizzati

5.8.1 Ricerca

L’algoritmo di ricerca, per prima cosa esegue una split del testo inseritodall’utente nel campo di ricerca, usando come carattere di suddivisione unospazio. In questo modo si ottiene un’array contenente le parole cercate, chia-mato keys.A questo punto vengono letti dalla base dati, tutti i libri presenti nella ta-bella Book.Per scorrere l’insieme dei libri vi è un primo ciclo for, subito sotto ce n’è unsecondo per scorrere l’array delle parole, le quali vengono cercate tramite lafunzione find all’interno del titolo del libro. Se la funzione di confronto delledue parole da esito positivo, e il libro non era ancora nella lista dei risultati,viene inserito, viceversa, se il libro era già tra i risultati della ricerca, vienesemplicemente incrementato il suo contatore.Terminata questa procedura di ricerca, prima di restituire la lista di risulta-ti, questa viene ordinata in base al numero di parole corrispondenti a quellecercate, trovate in ogni titolo.Per questo algoritmo sono stati anche scritti due metodi di confronto. Ilprimo metodo (compare) è una ridefinizione del metodo predefinito per con-frontare due oggetti, di modo da che essi vengano confrontati semplicementeper titolo. Il secondo invece (comparaNKey) viene passato nel metodo sortsulla lista, per stabilire il criterio di ordinamento della stessa.

5.8.2 Estrazione dei tag da un titolo

L’idea alla base di questo algoritmo è molto semplice([12]). Si salvano inuna lista tutti i tag già presenti nel db, e si crea un’altra lista con delle stopwords3. A questo punto sul titolo del libro viene eseguita un’operazione displit, ossia viene divisa la frase (usando come separatore lo spazio) in parole,che andranno poi a riempire una terza lista. Fatto ciò basterà confrontarele parole della terza lista con le stop words ed eliminarle quando verrannotrovate delle uguaglianze. Dopodichè tutte le altre parole saranno da salvarecome tag. Bisognerà solamente distinguere quali parole sono già presenti neldb, e in questo caso si dovrà solamente incrementare il contatore della parolanel db, e quali sono nuovi termini, da inserire come nuovo tag.L’algoritmo viene avviato ogni qualvolta si inserisce un nuovo testo.

3Questo termine viene usato per indicare parole che non sono da considerarsi come tag.Quando vengono trovate all’interno del testo, si devono ignorare.

Page 38: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 35

5.8.3 Ricerca dei tag correlati

Il metodo cercaCorrelati([18]) riceve in input una lista di libri. Per ognunodi essi richiama la funzione tagCorrelati. Quest’ultima trova i tag correlatiper un libro e li aggiunge alla lista tags.Il primo metodo richiama ciclicamente il secondo passandogli sempre la stessalista di tag, in modo tale che essa cresca con lo scorrere della lista di libridi cui cercare i termini correlati. Il secondo metodo infatti, prima di inserireun termine nella lista di ritorno, controlla se questo è già presente. In casopositivo, non lo dovrà aggiungere, ma dovrà solamente incrementare il suocontatore.

5.8.4 Visualizzazione delle stanze

All’apparenza, questo, sembra un problema insignificante. Purtroppo il si-stema di programmazione di App Engine rende tutto ciò più complicato. Ilfatto che nelle pagine html non si possano effettuare controlli veri e propri,ma solo if per controllare se una variabile è nulla o meno e cicli for per scor-rere liste, combinato alla realizzazione di stanze dinamiche4 che si è deciso diadottare in SellBook, obbliga a dover scrivere un algoritmo più complesso.Per elencare le stanze infatti, l’algoritmo legge inizialmente tutte quelle a li-vello radice. Questo elenco viene passato ad una funzione che, ricorsivamente,scorre tutte le sottostanze delle rooms radice. Queste funzioni creano cosìuna stringa contenente codice html che verrà poi semplicemente stampatanel documento ipertestuale.

5.9 ConsiderazioniIl caso d’uso SellBook è stato molto utile per sperimentare App Engine, per-chè ha permesso di usufruire di diversi servizi, tra quelli messi a disposizionedal 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 sicompie utilizzando php, jsp o asp. Uno dei più grandi vantaggi è sicuramentequello 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 cheinvia la query, senza nessuna procedura per la connesione e la disconnessioneal dbms. Tutto ciò risparmia al programmatore alcune linee di codice ogni

4Ogni stanza può contenere un numero variabile e indipendente di sottostanze

Page 39: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 36

qualvolta si voglia effettuare una lettura dal database e contribuisce anche asnellire il codice dell’applicazione. In aggiunta, utilizzando python, il codicerisulta molto comprensibile, e bastano poche linee per eseguire qualsiasi ope-razione, sia grazie alle peculiarità insite nel linguaggio, sia per merito dellevarie librerie realizzate da Google e presenti all’interno dell’SDK.

Page 40: Google App Engine: una soluzione alternativa alla creazione di web application.

Appendice A

Il file main.pyimport wsgiref.handlersimport reimport timeimport logging

from string import *from google.appengine.api import usersfrom google.appengine.ext import dbfrom google.appengine.api import datastorefrom google.appengine.api import datastore_typesfrom google.appengine.ext import webappfrom google.appengine.api import memcachefrom google.appengine.ext.webapp \

import template

#da impostare a True se si vuole avere il#report degli errori a runtime_DEBUG=True

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

Page 41: Google App Engine: una soluzione alternativa alla creazione di web application.

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 getdef get(self):

if not cmp(self.request.get("action"),"SearchTag"):try:

_id=db.get(self.request.get("idtag"))except:

_id=Noneself.redirect(’/’)

else:q1=db.GqlQuery("SELECT * FROM TaggedBook

WHERE tag= :1",_id)tot=db.GqlQuery("SELECT * FROM SellBook

WHERE user= :1", users.GetCurrentUser())cont=0for t in tot:

Page 42: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 39

if t.contact is not None:cont+=t.contact

tags=Cloud.cercaCorrelati(self,q1,_id)link=’tag.html’valori={

’user’: users.GetCurrentUser(),’loginUrl’: users.CreateLoginURL(

self.request.uri),’logoutUrl’: users.CreateLogoutURL(

self.request.uri),’contacts’: cont,’books’:q1,’tags’:tags

}#reindirizzamento a pagina htmlself.response.out.write(template.render(

link,valori))else:

if not cmp(self.request.get("action"),"Pulisci"):tot=db.GqlQuery("SELECT * FROM SellBook

WHERE user= :1", users.GetCurrentUser())cont=0for t in tot:

t.contact=Nonedb.put(t)

self.redirect("/")else:

tags=Cloud.tagCloud()link=’index.html’tot=db.GqlQuery("SELECT * FROM SellBook

WHERE user= :1", users.GetCurrentUser())cont=0for t in tot:

if t.contact is not None:cont+=t.contact

ultimi=memcache.get("lastBooks")if ultimi is None:

ultimiInseriti=db.GqlQuery("SELECT * FROM SellBook ORDER BY dataSell DESC LIMIT 0,5")

if ultimiInseriti.count()<=0:ultimi=None

Page 43: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 40

else:ultimi=""for u in ultimiInseriti:

ultimi+="<a href=\"libro?id="+str(u.book.key())+"&action=View\"class=\"no_under\">"+u.book.title+"&nbsp;("+str(u.price)+"&euro;)&nbsp;<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 htmlself.response.out.write(template.render(

link,valori))

#gestisce le richieste postdef post(self):

tStart=time.clock()query=Search.run(self)len_query=len(query)if len_query==0:

len_query=NonetEnd=time.clock()tmp=round(tEnd-tStart,4)tot=db.GqlQuery("SELECT * FROM SellBook

WHERE user= :1", users.GetCurrentUser())cont=0for t in tot:

if t.contact is not None:cont+=t.contact

Page 44: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 41

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=’’@staticmethoddef tagCorrelati(self,q2,tags):

for q21 in q2:flag=0for t in tags:

if t.descr==q21.tag.descr:flag=1tmp=t

if flag==1:tmp.dim+=1

else:tmp=Cloud()tmp.descr=q21.tag.descrtmp.dim=1tmp.key=q21.tag.keytags.append(tmp)

return tags

@staticmethoddef cercaCorrelati(self,q1,_id):

tags=[]for q in q1:

_id=db.get(q.book.key())

Page 45: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 42

q2=db.GqlQuery("SELECT * FROM TaggedBookWHERE book= :1",_id)

tags=Cloud.tagCorrelati(self,q2,tags)return tags

@staticmethoddef tagCloud():

tags=memcache.get("tagCloud")if tags is None:

q1=db.GqlQuery("SELECT * FROM Tag ORDER BY descr ASC")tags=""for q in q1:

dim=’’if q.nTag < 2:

dim=’s1’else:

if q.nTag < 3:dim=’s2’

else:if q.nTag < 4:

dim=’s3’else:

if q.nTag < 5:dim=’s4’

else:if q.nTag < 6:

dim=’s5’else:

if q.nTag < 7:dim=’s6’

else:if q.nTag < 8:

dim=’s7’else:

if q.nTag < 9:dim=’s8’

else:dim=’s9’

tags+="<a href=\"tag?idtag="+str(q.key())+"&action=SearchTag\"class=\""+dim+"\">"+q.descr+"</a>&nbsp;\n"

if not memcache.add("tagCloud",tags):

Page 46: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 43

logging.error("MemCache add error on \"tagCloud\"")return tags

class SearchObj():book=NonenKey=0

class Search(object):@staticmethoddef compare(obj1,obj2):

return cmp(obj1.book.title,obj2.book.title)@staticmethoddef comparaNKey(x,y):

if x.nKey==y.nKey:return 0

else:if x.nKey>y.nKey:

return -1return 1

@staticmethoddef 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=qtmp.nKey=1flag=0app=Nonefor r in ris:

if Search.compare(tmp,r)==0:flag=1app=r

Page 47: Google App Engine: una soluzione alternativa alla creazione di web application.

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.pyimport wsgiref.handlersimport reimport sys

from main import Cloudfrom string import *from google.appengine.api import usersfrom google.appengine.ext import dbfrom google.appengine.api import datastorefrom google.appengine.api import datastore_typesfrom google.appengine.ext import webappfrom google.appengine.api import memcachefrom google.appengine.api import imagesfrom google.appengine.api import mailfrom google.appengine.ext.webapp \

import template

#da impostare a True se si vuole avere#il report degli errori a runtime_DEBUG=False

class Rooms(db.Model):descr=db.StringProperty()listaFigli=db.ListProperty(db.Key)

Page 48: Google App Engine: una soluzione alternativa alla creazione di web application.

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

Page 49: Google App Engine: una soluzione alternativa alla creazione di web application.

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=Nonelink=’libri.html’b=Nonesd=NoneimgBook=Noneoutput=Noneq1=Nonetags=[]p=re.compile(’(\!|\"|\%|\&|\/|\=|\’|\?|\^)’)if p.sub(’’,self.request.get("msg"))=="1":

msg="Attenzione, compilarecorrettamente 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=Noneself.redirect("/libri")

q3=db.GqlQuery("SELECT * FROM RoomsWHERE 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:

Page 50: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 47

sd=SellDetails()sd.price=sb.pricesd.location=sb.locationsd.key=sb.key()if sb.sellType==1:

sd.directSell=1else:

if sb.sellType==2:sd.onlineSell=1

else:if sb.sellType==3:

sd.directSell=1sd.onlineSell=1

q2=db.GqlQuery("SELECT * FROM TaggedBookWHERE book= :1",b)

tags=Cloud.tagCorrelati(self,q2,tags)else:

q1=db.GqlQuery("SELECT * FROM SellBookWHERE user= :1 ORDER BY book DESC",users.GetCurrentUser())

q2=db.GqlQuery("SELECT * FROM RoomsWHERE livello= :1",0)

output=Stanze.creaBox(q2,None)if id_key:

try:b=db.get(id_key)

except:b=Noneself.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 SellBookWHERE book= :1 LIMIT 0,1",b)

for sb in q3:

Page 51: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 48

sd=SellDetails()sd.price=sb.pricesd.key=sb.key()sd.location=sb.locationif sb.sellType==1:

sd.directSell=1else:

if sb.sellType==2:sd.onlineSell=1

else:if sb.sellType==3:

sd.directSell=1sd.onlineSell=1

tot=db.GqlQuery("SELECT * FROM SellBookWHERE user= :1", users.GetCurrentUser())

cont=0for 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))

#gestisce le richieste postdef post(self):

p=re.compile(’(\!|\"|\%|\&|\/|\=|\’|\?|\^)’)if not cmp(self.request.get("action"),"Inserisci"):

try:room=p.sub(’’,self.request.get("roomBook"))

Page 52: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 49

if len(room)>0:room=db.get(room)if not room:

room=Noneelse:

room=Nonebook=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=Nonesrc=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=Nonesbook=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=0if directSell:

if onlineSell:

Page 53: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 50

sbook.sellType=3else:

sbook.sellType=1else:

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=0if directSell:

if onlineSell:sbook.sellType=3

else:sbook.sellType=1

else:if onlineSell:

sbook.sellType=2sbook.price=float(p.sub(’’,

self.request.get("prezzo")))

Page 54: Google App Engine: una soluzione alternativa alla creazione di web application.

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=0for 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’))

Page 55: Google App Engine: una soluzione alternativa alla creazione di web application.

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=Noneself.redirect("/libri")

if id_sell:seller=(id_sell.user).email()

if not seller or not mex or not oggettoor not mittente or len(mittente)==0 or len(oggetto)==0or len(mex)==0:

link=’mail.html’tot=db.GqlQuery("SELECT * FROM SellBook

WHERE user= :1", users.GetCurrentUser())cont=0for 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

Page 56: Google App Engine: una soluzione alternativa alla creazione di web application.

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’)

#elimina un libro dal dbdef elimina(self):

k=self.request.get(’id_key’)try:

b=db.get(k)except:

b=Noneself.redirect(’/libri’)

else:q1=db.GqlQuery("SELECT * FROM SellBook

WHERE book= :1",b)db.delete(q1)q2=db.GqlQuery("SELECT * FROM TaggedBook

WHERE book= :1",b)for t in q2:

tag=db.get(t.tag.key())if tag.nTag<=1:

db.delete(tag)else:

tag.nTag-=1db.put(tag)

db.delete(q2)db.delete(b)

@staticmethoddef tagga(self,book):

titolo=lower(self.request.get("titolo"))newList=[]q=db.GqlQuery("SELECT * FROM Tag")oldList=[]fullOldList=[]

Page 57: Google App Engine: una soluzione alternativa alla creazione di web application.

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=ttag.nTag=1tag.put()tagged=TaggedBook()tagged.tag=tag.key()tagged.book=booktagged.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()

Page 58: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 55

tagged.book=booktagged.put()

class SellDetails():price=NonedirectSell=NoneonlineSell=Nonelocation=Nonekey=None

class Stanze():@staticmethoddef creaBox(query,compare):

output=Noneif 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)+"&minus;"+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

Page 59: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 56

@staticmethoddef 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)+"&minus;"+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()

Page 60: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 57

Il file stanze.pyimport wsgiref.handlersimport re

from google.appengine.api import usersfrom google.appengine.ext import dbfrom google.appengine.api import datastorefrom google.appengine.api import datastore_typesfrom google.appengine.ext import webappfrom google.appengine.api import memcachefrom google.appengine.ext.webapp \

import template

#da impostare a True se si vuole#avere il report degli errori a runtime_DEBUG=False

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)

Page 61: Google App Engine: una soluzione alternativa alla creazione di web application.

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 getdef get(self):

valori=Nonelink=’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=NonenomeStanza=None

else:try:

nomeStanza=db.get(stanza)except:

nomeStanza=Noneself.redirect(’/stanze’)

if nomeStanza:nomeStanza=nomeStanza.descr

tot=db.GqlQuery("SELECT * FROM SellBookWHERE user= :1", users.GetCurrentUser())

cont=0for t in tot:

if t.contact is not None:cont+=t.contact

valori={’user’: users.GetCurrentUser(),

Page 62: Google App Engine: una soluzione alternativa alla creazione di web application.

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=Noneself.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=Noneself.redirect(’/stanze’)

else:tot=db.GqlQuery("SELECT * FROM SellBook

WHERE user= :1", users.GetCurrentUser())cont=0for t in tot:

if t.contact is not None:

Page 63: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 60

cont+=t.contactif 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=Nonelink="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 SellBookWHERE user= :1", users.GetCurrentUser())

cont=0for 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,

Page 64: Google App Engine: una soluzione alternativa alla creazione di web application.

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=Nonetot=db.GqlQuery("SELECT * FROM SellBook

WHERE user= :1", users.GetCurrentUser())cont=0for t in tot:

if t.contact is not None:cont+=t.contact

try:stanza=db.get(p.sub(’’,

self.request.get("stanza")))except:

stanza=Noneself.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

Page 65: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 62

}else:

valori=self.visualizzaStanze()

#reindirizzamento a pagina htmlself.response.out.write(template.render(link,valori))

#gestisce le richieste postdef post(self):

if not users.get_current_user():self.redirect(’/’)

p=re.compile(’(\!|\"|\%|\&|\/|\=|\’|\?|\^)’)if not cmp(self.request.get("action"),"add"):

if not self.request.get("nuovaStanza"):if not cmp(self.request.get("idPadre"),"None"):

idPadre=NonenomeStanza=None

else:idPadre=p.sub(’’,self.request.get("idPadre"))try:

nomeStanza=db.get(idPadre)nomeStanza=nomeStanza.descr

except:nomeStanza=Noneself.redirect("/stanze")

tot=db.GqlQuery("SELECT * FROM SellBookWHERE user= :1", users.GetCurrentUser())

cont=0for 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),’contacts’: cont,’father’:idPadre,’stanza’:nomeStanza,’message’:"Attenzione:

Page 66: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 63

Inserire un nome per la nuova stanza."}#reindirizzamento a pagina htmlself.response.out.write(

template.render("nuovaStanza.html",valori))else:

r=Rooms()idfather=p.sub(’’,self.request.get("idPadre"))if not cmp(self.request.get("idPadre"),"None"):

r.livello=0r.descr=p.sub(’’,

self.request.get("nuovaStanza"))r.owner=users.get_current_user()r.father=Nonedb.put(r)memcache.delete("stanze")

else:try:

father=db.get(idfather)except:

print father.descrfather=Noneself.redirect(’/stanze’)

else:if father is not None:

r.livello=(father.livello)+1r.descr=p.sub(’’,

self.request.get("nuovaStanza"))r.owner=users.get_current_user()r.father=fatherdb.put(r)father.listaFigli.append(r.key())db.put(father)memcache.delete("stanze")

#reindirizzamento a pagina htmlself.redirect(’/stanze’)

else:if not cmp(self.request.get("action"),"modify"):

try:stanza=db.get(p.sub(’’,

self.request.get("stanza")))

Page 67: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 64

except:stanza=Noneself.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=Noneq=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/>"+outputelse:

output="<a href=\"stanze?action=add&stanza=None\"><img src=\"img/icons/list-add.png\">&nbsp;Inserisci&nbsp;stanza&nbsp;al&nbsp;livello&nbsp;radice</a>"+output

else:if not users.get_current_user():

output="Nessuna stanza presente.<br/>"+outputelse:

output="<a href=\"stanze?action=add&stanza=None\"><img src=\"img/icons/list-add.png\">&nbsp;Inserisci&nbsp;stanza&nbsp;al&nbsp;livello&nbsp;radice</a>"+output

tot=db.GqlQuery("SELECT * FROM SellBookWHERE user= :1", users.GetCurrentUser())

cont=0for t in tot:

if t.contact is not None:cont+=t.contact

valori={’user’: users.GetCurrentUser(),’loginUrl’: users.CreateLoginURL(self.request.uri),

Page 68: Google App Engine: una soluzione alternativa alla creazione di web application.

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>&nbsp;&nbsp;&nbsp;"if users.get_current_user():

output+="|&nbsp;<a href=\"stanze?action=add&stanza="+str(q1.key())+"\"><img src=\"img/icons/list-add.png\"></a>&nbsp;"

if users.get_current_user()==q1.owner orusers.is_current_user_admin():

output+="<a href=\"stanze?action=remove&stanza="+str(q1.key())+"\"><img src=\"img/icons/list-remove.png\"></a>&nbsp;"

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.listaFiglifor l in lista:

tmp=db.get(l)if tmp is not None:

output+="<li>"

Page 69: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 66

output+="<a href=\"stanze?action=view&stanza="+str(l)+"\">"+tmp.descr+"</a>&nbsp;&nbsp;&nbsp;"

if users.get_current_user():output+="|&nbsp;<a href=\"stanze?

action=add&stanza="+str(l)+"\"><img src=\"img/icons/list-add.png\"></a>"

if users.get_current_user()==tmp.owner orusers.is_current_user_admin():

output+="<a href=\"stanze?action=remove&stanza="+str(l)+"\"><img src=\"img/icons/list-remove.png\"></a>&nbsp;"

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=Noneif 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()

Page 70: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 67

Codice javascript per Google Mapsvar map=null;var geocoder=null;function initialize(){

if (GBrowserIsCompatible()) {map = new GMap2(document.getElementById("map"));coord=window.document.getElementById("coord");if(!coord){

var center=new GLatLng(45.0897609,7.658761);}else{

var c=coord.value;geo=c.split(",");var center=new GLatLng(geo[0],geo[1]);

}map.setCenter(center, 13);map.addControl(new GLargeMapControl);map.addControl(new GMapTypeControl);geocoder = new GClientGeocoder();window.document.forms[’formDati’].location.value=center;geocoder.getLocations(center, addAddressToMap);

}}

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);

}}

Page 71: Google App Engine: una soluzione alternativa alla creazione di web application.

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);}

}

Il file di configurazione app.yamlapplication: sbookversion: 1runtime: pythonapi_version: 1

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

Page 72: Google App Engine: una soluzione alternativa alla creazione di web application.

CAPITOLO 5. UN CASO DI STUDIO: SELLBOOK 69

- url: /libroscript: libri.py

- url: /imgscript: libri.py

- url: /stanzescript: stanze.py

- url: /infoscript: info.py

- url: /.*script: not_found.py

Page 73: Google App Engine: una soluzione alternativa alla creazione di web application.

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/

[10] Bigtable: A Distributed Storage System for Structured DataAutori: Fay Chang, Jeffrey Dean, Sanjay Ghemawat, Wilson C. Hsieh,Deborah A. Wallach, Mike Burrows, Tushar Chandra, Andrew Fikes eRobert E. GruberAnno: 2006

[11] The Google File SystemAutori: Sanjay Ghemawat, Howard Gobioff e Shun-Tak LeungAnno: 2003

[12] http://www.di.unito.it/ argo/biblio/bibliofolks.py

[13] http://www.di.unito.it/ argo/biblio/biblio_graph.php?author=Baldoni

[14] http://www.di.unito.it/ argo/biblio/biblio_words.php?author=Baldoni

70

Page 74: Google App Engine: una soluzione alternativa alla creazione di web application.

Bibliografia e sitografia 71

[15] Un’introduzione alla programmazione orientata agli oggetti attraversolo schema“Kernel-Modulo”Autore: Matteo BaldoniAnno: 2002

[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/