UNIVERSITÀ DEGLI STUDI DI SALERNO Gestione dei Progetti Software Documentazione Progetto di Integrazione dei dati su Web A.A. 2016-2017 Docente: Prof. Gennaro Costagliola Studente: Lorenzo Vitale
Panoramica Il sito web attuale di Trenitalia consente agli utenti di ricercare soluzioni di viaggio impostando luogo, data di partenza e data di arrivo. Questo tipo di ricerca, non consente di scovare facilmente promozioni ed offerte a lungo termine su titoli di viaggio che Trenitalia mette quotidianamente, periodicamente ed inaspettatamente a disposizione degli utenti Infatti spesso Trenitalia propone titoli di viaggio a basso costo in alcuni periodi dell anno o in base ad eventi del tipo: «ultimi posti disponibili». Eventi comunque difficili da predire.
L idea. Creare una web application che permetta agli utenti di scovare tutte le promozioni e le offerte più vantaggiose di trenitalia a largo raggio ed in maniera istantanea 50,00 per singolo spazio offerto che permetta, agli utenti interessati ad intraprendere un viaggio verso una certa destinazione, di ricevere notifiche ogni qualvolta trenitalia proponga offerte e promozioni sulle tratte a cui si è interessati che fornisca l elenco di eventi, spettacoli ed incontri più importanti associati al luogo di destinazione e alla data del viaggio in offerta scovato
Architettura ibrida e stratificata Architettura Mediator ad alto livello Due architetture Datawarehouse a basso livello da cui attingere ad informazioni con una rara o scarsa volatilità. Un unica fonte con dati «freschi» (alta volatiltà) 50,00 Garanzia di una maggiore per singolo efficienza spazio offerto con un accesso più veloce e con tempi di risposta ridotti.
Architettura ibrida e stratificata L utente interagisce con l interfaccia grafica inserendo l indirizzo di partenza, l indirizzo di arrivo, selezionando il periodo entro cui effettuare la ricerca e fornendo la propria email Vengono invocate le API di Google Maps per il completamento automatico degli indirizzi e per l individuazione delle stazioni ferroviarie più vicine agli indirizzi indicati 50,00 Alla pressione del tasto per «Calcola singolo spazio il viaggio offerto più economico» il sistema di data extraction dell applicazione viene avviato
Le Fonti Dalla S1, si estraggono le label rigide delle stazioni ferroviarie nazionali dalle risorse html di www.trenitalia.it e le coordinate geografiche degli indirizzi delle stazioni tramite le API di Google. Le informazioni vengono «immagazzinate» in un database XML Stations.xml (Datawarehouse) L aggiornamento dei dati di S1 avviene in tre casi: quando il dato non esiste in automatico quando l ultimo aggiornamento è scaduto (attributo lastupdate) per mano di un User admin quando si ci avvede di 50,00 un cambiamento delle label delle stazioni da parte per singolo spazio offerto di trenitalia digitando il seguente indirizzo: cheaptrip.it/class/stationsdatawh/
Le Fonti La seconda fonte S2 è al livello del Mediator e attinge a dati «freschi» facendo scraping sulle pagine di www.lefrecce.it ottenendo le informazioni riguardo ai treni e alle tratte (Trains.xml). La fonte S3 estrae gli eventi che si tengono in tutte le province italiane dalla sorgente www.giraitalia.it. Anche S3 immagazzina le informazioni degli eventi in un database XML Events.xml (Datawarehouse). L aggiornamento dei dati di S3 avviene in tre casi: quando il dato non esiste Quando il lastupdate scade per mano di un User admin digitando il seguente percorso 50,00 per singolo spazio offerto cheaptrip.it/class/eventsdatawh/
Le Fonti FONTE DESCRIZIONE RISORSE TIPO VOLATILITA Stations.xml Contiene tutte le stazioni ferroviarie attive nazionali e le loro coordinate geografica http://www.trenitalia.com/tcom/treni- Regionali/Abruzzo/Stazioni-servite-da-Trenitalia-Abruzzo. http://www.trenitalia.com/tcom/treni- Regionali/Umbria/Stazioni-servite-da-Trenitalia-in-Umbria https://maps.googleapis.com/maps/api/ HTML Raramente volatili Events.xml Contiene tutte gli eventi catalogati per ogni regione e provincia http://www.giraitalia.it/abruzzo/eventi.htm http://www.giraitalia.it/basilicata/eventi.htm http://www.giraitalia.it/calabria/eventi.htm http://www.giraitalia.it/campania/eventi.htm http://www.giraitalia.it/emilia/eventi.htm http://www.giraitalia.it/veneto/eventi.htm HTML Mediamente volatili Trains.xml 50,00 Contiene le prime n offerte di viaggio più vantaggiose per calcolata singolo spazio offerto a partire da una data fino ad anno e da un luogo di partenza e un luogo di arrivo inseriti da parte dell utente nella fase di ricerca. https://www.lefrecce.it/b2cweb/searchexternal.do?paramete r=initbasesearch&lang=it HTML Cambi periodici (altamente volatili)
Architettura software Due classi astratte che incarnano il paradigma di integrazione dei dati riguardo a Mediator e Datawarehouse: Wrapper e Data Extraction. Dipendenza Estensione 50,00 per singolo spazio offerto
Architettura software Le classi EventsWrapper, TripsWrapper e GoogleMapsWrapper estendono il metodo execute() della classe astratta Wrapper che esegue il matching ed il mapping trasformando lo schema di rappresentazione dei dati delle sorgenti nello schema globale che si attende il Mediator 50,00 per singolo spazio offerto
Architettura software In alto frammento del codice che definisce il metodo astratto execute() (Abstract Class Wrapper.php) A destra frammento che del codice del metodo execute() esteso dalla classe astratta (Abstract Class EventsWrapper.php)
Architettura software Le classi EventsDataExtraction e StationsDataExtraction estendono due metodi della classe astratta DataExtraction: extractdata() invoca il wrapper per l estrazione dei dati ed il salvataggio del database XML Events.xml (procedura lenta). getdata() attinge direttamente da dati immagazzinati nel 50,00 datawarehouse in tempi decisamente minori per singolo spazio offerto
Architettura software In alto frammento che acquisisce I dati degli Eventi dalla risorsa giraitali.it (Abstract Class DataExtraction.php) A destra frammento che del codice dei metodi extractdata() e getdata() estesi dalla classe astratta (Abstract Class EventsDataExtraction.php)
Descrizione dei Wrapper Sono stati sviluppati quattro Wrapper: Uno per l estrazione degli eventi (EventsWrapper) Uno per l estrazione delle stazioni ferroviarie (StationsWrapper) Uno per l estrazione delle tratte ferroviarie ricercate dall utente (TripsWrapper) Un altro wrapper attinge dalle fonti relative alle coordinate offerte dalle API di Google Maps (GoogleMapsWrapper) 50,00 per singolo spazio offerto
EventsWrapper Il metodo execute() della classe EventsWrapper importa una serie di pagine html contenenti i dati degli eventi nazionali durante l intero arco dell anno nelle diverse province italiane (ogni provincia un file html). 50,00 per singolo spazio offerto Frammento che acquisisce I dati degli Eventi dalla risorsa giraitali.it (Class EventsWrapper.php) Il contenuto dei file in formato stringa viene convertito in PHP nell oggetto DOMXPath sul quale è possibile recuperare le informazioni rilevanti attraverso il linguaggio XPath. Il metodo resituisce una serie di Eventi di cui si tiene traccia di: nome dell evento, data inizio, data fine ed eventuale link esterno
StationsWrapper Allo stesso modo anche Il metodo execute() della classe StationsWrapper importa una serie di pagine html contenenti i dati delle stazioni ferroviare (label essenziali per poter effettuare la ricerca). 50,00 per singolo spazio offerto Il contenuto dei file in formato stringa viene convertito in PHP nell oggetto DOMXPath sul quale è possibile poi recuperare, tramite XPath, le informazioni di: nome della stazione, indirizzo, città e provincia. Frammento che acquisisce i dati delle stazioni dalla risorsa trenitalia.it (Class StationsWrapper.php)
TripsWrapper Il metodo execute() della classe TripsWrapper utilizza la libreria CURL di PHP per effettuare lo scraping sulla pagina dinamica del sito lefrecce.it ed ottenere i titoli dei viaggi in offerta di trenitalia. 50,00 per singolo spazio offerto Frammento che riguarda l utilizzo della libreria curl per lo scraping (Class TripsWrapper.php) Il contenuto dei file della response vengono convertiti nell oggetto DOMXPath sul quale poi recuperare, tramite XPath, le informazioni di viaggio quali: giorno, nome stazione di partenza e di arrivo, ora di partenza e di arrivo, durata, numero di cambi, tipologia treno/i e il prezzo.
TripsWrapper Frammento che preleva le informazioni ottenute dallo scraping tramite curl dalla risorsa lefrecce.it (Class StationsWrapper.php) 50,00 per singolo spazio offerto
GoogleMapsWrapper Il metodo execute() della classe GoogleMapsWrapper non esegue alcuna funzione. Riga di codice che invoca le api di Google Maps su un indirizzo (Class GoogleMapsWrapper.php) Il metodo che invece è utile ai fini delle funzionalità del sistema è il metodo getcoordinate(indirizzo) che, dato in input un indirizzo, restituisce le coordinate di quell indirizzo tramite le API di Google
Schema fonti locali Di seguito viene riportata la descrizione dello schema locale di ogni fonte in datalog. S1: Stations (label, address, province, lat, lng) S2: Trains (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost) S3: Events (title, start_date, end_date, link)
Schema globale Di seguito viene riportata la descrizione dello schema globale in datalog. Trips (data, dep_station, arr_station, dep_time, arr_time, duration, changes, cost) EventsTrip (event, start_event, end_event, link_event)
Mapping GAV Di seguito viene mostrato il mapping GAV in datalog che mostra le corrispondenze tra lo schema globale e locale definendo lo schema globale in funzione di quello locale. Trips (date, dep_station, dep_address, arr_station, arr_address, dep_time, arr_time, duration, changes, cost, province) :- S2.Trains (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost) ^ S1.Stations (dep_station, dep_address, _, _, _) ^ S1.Stations (arr_station, arr_address, province, _, _) EventsTrip (event, start_event, end_event, link_event) :- S3.Events (event, start_event, end_event, link); NB: EventsTrip è una identità di Events avendo gli stessi parametri e la stessa arity. La riportiamo solo per completezza. Dal punto di vista dell analisi potrebbe essere ignorata.
Mapping LAV Di seguito invece viene mostrato il mapping LAV in datalog che mostra le corrispondenze tra lo schema globale e locale definendo lo schema locale in funzione di quello globale. S1.Stations (label, address, province) Trips ( _, label, address, _, _, _, _, _, _, _, province) S1.Stations (label, address, province) Trips ( _,_, label, address, _, _, _, _, _, _, province); S2.Trains (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost) Trips (date, dep_station, _, arr_station, _, dep_time, arr_time, duration, changes, cost, _ ); S3.Events (title, start_date, end_date, link) = EventsTrip (title, start_date, end_date, link);
Query Nell applicazione di data integration principale, riportiamo tre query: Q1: permette di ottenere l elenco dei viaggi in treno con le migliori offerte e promozioni dal 26 Giugno 2017 al 27 Giugno 2018 partendo dalla stazione ferroviaria più vicina a Piazza Garibaldi in Napoli e arrivando alla stazione ferroviaria più vicina a Piazza Castello in Torino. Q2: Permette di ottenere l elenco degli eventi nella provincia di Torino il 26 Giugno 2017. Q3: Permette di ottenere una join dell elenco dei viaggi in treno con le migliori offerte e promozioni dal 26 Giugno 2017 al 27 Giugno 2018 partendo dalla stazione ferroviaria più vicina a Piazza Garibaldi in Napoli e arrivando alla stazione ferroviaria più vicina a Piazza Castello in Torino con gli eventi in programma nelle date dei viaggi in promozione nel periodo dal 26 Giugno 2017 al 27 Giugno 2018 nella provincia di Torino.
Query espresse in datalog Q1 (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost, province) :- Trips (date, dep_station, dep_address, arr_station, dep_address, dep_time, arr_time, duration, changes, cost, province) ^ dep_address = 'Piazza Garibaldi, NA' ^ arr_address = 'Piazza Castello, TO' ^ date >= '26-06-2017' ^ date <= '27-06-2018' Q2 (event, province, start, end, link) :- EventsTrip (event, start, end,link) ^ start <= '26-06-2017' ^ end >= '26-06-2017' ^ contains(event, (TO) ) Q3 (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost, event, province, start, end, link) :- Trips (date, dep_station, dep_address, arr_station, dep_address, dep_time, arr_time, duration, changes, cost, province) ^ EventsTrip (event, start, end,link) ^ date >= start ^ date <= end ^ date >= '26-06-2017' ^ date <= '27-06-2018' ^ dep_address = 'Piazza Garibaldi, NA' ^ arr_address = 'Piazza Castello, TO' ^ contains(event, province) NB: contains è una funzione di supporto che per permette di eseguire un controllo di contenimento del contenuto della variabile provincia della subgoal Trips (sigla della provincia dell indirizzo di arrivo) nel titolo dell evento.
Query espresse in SQL Q1: SELECT FROM WHERE Trips.date as date, Trips.dep_station as departure_station, Trips.arr_station as arrival_station, Trips.dep_time as departure_time, Trips.arr_time as arrival_time, Trips.duration as duration_trip, Trips.changes as changes_trip, Trips.cost as cost_trip Trips Trips.dep_address = 'Piazza Garibaldi, NA' AND Trips.arr_address = 'Piazza Castello, TO' AND Trips.date >= 26-06-2017 AND Trips.date <= 27-06-2017
Query espresse in SQL Q2: SELECT FROM WHERE EventsTrip.event as event, EventsTrip.province as province, EventsTrip.start_event as start_event, EventsTrip.end_event as end_event, EventsTrip.link as link EventsTrip EventsTrip.event LIKE %(TO)% AND EventsTrip.start_event <= 26-06-2017 AND EventsTrip.end_event >= 26-06-2017
Query espresse in SQL Q3: SELECT FROM LEFT JOIN ON WHERE ORDER BY Trips.date as date, Trips.dep_station as departure_station, Trips.arr_station as arrival_station, Trips.dep_time as departure_time, Trips.arr_time as arrival_time, Trips.duration as duration_trip, Trips.changes as changes_trip, Trips.cost as cost_trip, EventsTrips.event as event, EventsTrips.province as province, EventsTrips.start_event as start_event, EventsTrips.end_event as end_event, EventsTrips.link as link Trips EventsTrips Trips.date >= EventsTrips.start_event AND Trips.date <= EventsTrips.end_event Trips.dep_address = 'Piazza Garibaldi NA' AND Trips.arr_address = 'Piazza Castello TO' AND EventsTrip.event LIKE %(Trips.province)% Trips.cost OSSERVAZIONE effettuando una left join tra tabelle, si avranno dei duplicati sulla tabella a sinistra (in questo caso sulla tabella Trips). Per rimuovere i duplicati e mantenere una certa uniformità che rende anche una elaborazione più facile dei dati, la query Q3 potrebbe essere riscritta nel seguente modo senza perdita di generalità aggregando le tuple sui dati dei viaggi della tabella trips che produce i duplicati.
Query espresse in SQL Q3 : SELECT Trips.date as date, Trips.dep_station as departure_station, Trips.arr_station as arrival_station, Trips.dep_time as departure_time, Trips.arr_time as arrival_time, Trips.duration as duration_trip, Trips.changes as changes_trip, Trips.cost as cost_trip, CONCAT( Evento:, EventsTrips.event as events, Provincia:, EventsTrips.province, Data inizio:, EventsTrips.start_event, Data fine: EventsTrips.end_event,, Link: EventsTrips.link ) Trips FROM LEFT JOIN EventsTrips ON Trips.date >= E_Trip.start_event AND Trips.date <= E_Trip.end_event WHERE Trips.dep_address = 'Piazza Garibaldi NA' AND Trips.arr_address = 'Piazza Castello TO' AND EventsTrip.event LIKE %Trips.province% GROUP BY ( Trips.date, Trips.dep_station, Trips.arr_station, Trips.dep_time, Trips.arr_time, Trips.duration, Trips.changes, Trips..cost ) ORDER BYTrips.cost
Riformulazione GAV La riscrittura della query in query su fonti locali viene ottenuto tramite l unfolding, cioè, sostituendo ogni atomo che può essere abbinato con testa di qualche vista, dal corpo della vista corrispondente. Q1 (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost, province) :- S2.Trains (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost) ^ S1.Stations (dep_station, dep_address, _, _, _) ^ S1.Stations (arr_station, arr_address, province, _, _) ^ date >= '26-06- 2017' ^ date <= '27-06-2018' ^ dep_address = 'Piazza Garibaldi, NA' ^ arr_address = 'Piazza Castello, TO' Q2 (event, province, start, end, link) :- S3.Events (event, start, end,link) ^ start <= '26-06-2017' ^ end >= '26-06-2017' ^ contains(event, (TO) ) Q3 (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost, event, province, start, end, link) :- S2.Trains (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost) ^ S1.Stations (dep_station, dep_address, _, _, _) ^ S1.Stations (arr_station, arr_address, province, _, _) ^ S3.Events (event, start, end,link) ^ date >= start ^ date <= end ^ date >= '26-06-2017' ^ date <= '27-06-2018' ^ dep_address = 'Piazza Garibaldi, NA' ^ arr_address = 'Piazza Castello, TO' ^ contains(event, province)
Riformulazione LAV Riformuliamo la query in LAV eseguendo il Bucket Algorithm su ogni query. Primo step: Si costruisce per ogni atomo g del corpo della query globale il suo bucket che raggruppa tutte le fonti locali da cui g può essere dedotto. Q3 (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost, event, province, start, end, link) :- Trips (date, dep_station, dep_address, arr_station, dep_address, dep_time, arr_time, duration, changes, cost, province) ^ EventsTrip (event, start, end,link) ^ date >= start ^ date <= end ^ date >= '26-06-2017' ^ date <= '27-06-2018' ^ dep_address = 'Piazza Garibaldi, NA' ^ arr_address = 'Piazza Castello, TO' ^ contains(event, province) Abbiamo due atomi nel corpo della query globale, Trips e EventsTrip.
Riformulazione LAV [bucket di Trips] Trips può essere dedotta solo in funzione delle fonti locali S2.Trains e S1.Stations [bucket di EventsTrip] Trips può essere dedotta solo in funzione della fonte locale S3.Events Secondo step: Costruiamo le query candidate che si ottengono combinando gli atomi di ogni bucket. Abbiamo un unica combinazione possibile Q3 (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost, province) :- ) :- S2.Trains (date, dep_station, arr_station, dep_time, arr_time, duration, changes, cost) ^ S1.Stations (dep_station, dep_address, _, _, _) ^ S1.Stations (arr_station, arr_address, province, _, _) ^ S3.Events (event, start, end,link) ^ date >= start ^ date <= end ^ dep_address = 'Piazza Garibaldi, NA' ^ arr_address = 'Piazza Castello, TO' ^ date >= '26-06-2017' ^ date <= '27-06-2018' Terzo step: E evidente che Q3 è identica a Q3 per cui Q3 Q3 e Q3 Q3.
Tecnologie utilizzate Sono state utilizzate per il progetto il seguente insieme di tecnologie: PHP XPath Ajax JQuery Javascript/HTML curl
Previsioni future L architettura dell applicazione può prevedere l integrazione di ulteriori fonti (oltre a quella di trenitalia) includendo altre tipologie di trasporti come voli aerei, tratte via mare, o autostradali (autobus). Inoltre i filtri di ricerca possono essere ulteriormente elaborati. Saranno previsti, per gli utenti iscritti, funzionalità più avanzate di ricerca.
Buona ricerca di viaggi vantaggiosi www.cheaptrip.it