UNIVERSITÀ POLITECNICA DELLE MARCHE FACOLTÀ DI INGEGNERIA Corso di Laurea in Ingegneria Informatica e dell’Automazione PROGETTO E SVILUPPO DI UN SOFTWARE IN “JAVA” PER LA TELEREFERTAZIONE: SEZIONE CARTELLA CLINICA Laureando: Giovanni Belelli Relatore: Prof. Aldo Franco Dragoni Correlatore: Prof. Paolo Puliti Anno Accademico 2007/2008 INDICE INTRODUZIONE............................................................................... 1 CAPITOLO 1 - LA TELEMEDICINA................................................. 2 1.1 TIPI DI TELEMEDICINA ................................................................................... 3 1.2 OBIETTIVI .................................................................................................... 4 1.3 VANTAGGI ................................................................................................... 5 1.4 SVANTAGGI E PROBLEMI ............................................................................... 6 1.5 APPLICAZIONI .............................................................................................. 7 1.6 LA TELEMEDICINA IN ITALIA ........................................................................... 8 CAPITOLO 2 - IL PROGETTO “MEDTEL”.................................... 10 2.1 FINALITÀ .................................................................................................. 11 2.2 FIGURE PARTECIPANTI............................................................................... 11 2.3 SOLUZIONI TECNICHE ADOTTATE ................................................................ 17 CAPITOLO 3 - LA CARTELLA CLINICA....................................... 22 3.1 COM’È COSTITUITA .................................................................................... 23 3.2 IL PROFILO DEL PAZIENTE .......................................................................... 24 3.3 L’ANAMNESI .............................................................................................. 25 3.4 PROBLEMI CLINICI DEL PAZIENTE ................................................................ 29 CAPITOLO 4 - GOOGLE WEB TOOLKIT ..................................... 33 4.1 CARATTERISTICHE .................................................................................... 35 4.2 ARCHITETTURA ......................................................................................... 40 4.3 LATO CLIENT E LATO SERVER ..................................................................... 41 I CAPITOLO 5 - SOLUZIONI TECNICHE CARTELLA CLINICA .... 51 5.1 COM’È FATTA ............................................................................................ 51 5.2 CLINICALFOLDER ....................................................................................... 55 5.3 PATIENT PROFILE ...................................................................................... 62 5.4 ANAMNESIS ............................................................................................... 69 5.5 ALLERGOLOGY .......................................................................................... 71 5.6 FAMILIARS ................................................................................................. 75 5.7 FAMILY PATHOLOGIES ................................................................................ 80 5.8 PATHOLOGICALS ........................................................................................ 87 5.9 PHYSIOLOGICALS ....................................................................................... 92 5.10 PATIENT CLINICAL PROBLEM .................................................................... 95 CAPITOLO 6 - CONCLUSIONI .................................................... 100 6.1 OBIETTIVI RAGGIUNTI .............................................................................. 100 6.2 PROBLEMI RISCONTRATI .......................................................................... 101 6.3 MIGLIORAMENTI POSSIBILI ....................................................................... 102 6.4 CONFRONTI CON ALTRE TECNOLOGIE ....................................................... 104 6.5 CONFRONTO CON “MIRO ON RAILS”......................................................... 107 6.6 CONSIDERAZIONI FINALI SUL TIROCINIO ..................................................... 110 APPENDICE – CODICE SORGENTE .......................................... 112 CLINICAL FOLDER – LATO CLIENT.................................................................... 112 CLINICAL FOLDER – LATO SERVER ................................................................. 134 ALLERGOLOGY – LATO CLIENT ....................................................................... 135 ALLERGOLOGY – LATO SERVER ...................................................................... 140 BIBLIOGRAFIA ............................................................................ 144 II INTRODUZIONE Le tematiche affrontate da questa tesi riguardano la telerefertazione e la telemedicina in generale. L’utilizzo del Java tramite la tecnologia Google Web Toolkit ha permesso di creare un’ampia e flessibile applicazione software atta a svolgere tali compiti. Lo sviluppo è stato effettuato in collaborazione con l’azienda sanitaria locale, che ha fornito supporto dal punto di vista medico, mentre la parte realizzativa è stata curata da un gruppo di cinque tirocinanti, tra cui il sottoscritto. Nel seguito dei capitoli verrà dapprima presentato il concetto di telemedicina e successivamente si entrerà nel dettaglio del progetto. Nello specifico verrà trattata la sezione riguardante i dati medici del paziente, nonché tutte le altre informazioni presenti nella cartella clinica. Un capito a parte sarà lasciato per descrivere le tecnologie utilizzate e soprattutto verrà fornita una dettagliata descrizione del Google Web Toolkit, in modo da spiegarne i meccanismi di funzionamento. In conclusione sarà fornita una panoramica sugli obiettivi raggiunti, le eventuali migliorie possibili da applicare al software creato ed un confronto con altre tecnologie più conosciute. 1 CAPITOLO 1 - LA TELEMEDICINA La telemedicina ha molte e differenti definizioni. La definizione ufficiale secondo la Commissione Europea è la seguente: la telemedicina è l’integrazione, monitoraggio e gestione dei pazienti, nonché l’educazione dei pazienti e del personale, usando sistemi che consentono un pronto accesso alla consulenza di esperti ed alle informazioni del paziente, indipendentemente da dove il paziente o le informazioni risiedono. Naturalmente la telemedicina è utilizzata quando la distanza è un fattore critico che influisce nella risoluzione ottimale del rapporto medico/paziente e perciò viene necessario l’utilizzo di tecnologie dell’informazione e della comunicazione per lo scambio di informazioni rilevanti per la diagnosi e più in generale per il monitoraggio e la gestione dei pazienti. Un fattore interessante della telemedicina è anche quello riguardante i costi della stessa. Pur permettendo assistenza sanitaria a grande distanza, i costi di tali servizi sono contenuti: questo soprattutto nel caso di zone dove l’assistenza sanitaria è offerta a zone dove la prestazione medica non è disponibile. La telemedicina permette anche la formazione professionale di medici ed infermieri, fondendo anche le varie esperienze accumulate nei vari progetti, così da poter migliorare la qualità dei servizi: un medico può specializzare altri medici che richiedono una seconda opinione su un caso clinico. 2 Capitolo 1 – La Telemedicina L’utilizzo della telemedicina si presta bene tra i paesi dotati di maggiori tecnologie e i paesi in via di sviluppo, attuando anche una crescita tecnologica in quest’ultimi. 1.1 Tipi di telemedicina Ci sono due differenti tipologie di applicazione della telemedicina: “real time” (sincrona) e “store-and-forward” (asincrona). La telemedicina in real time può essere intesa semplicemente come l’utilizzo del telefono come mezzo di comunicazione o in maniera più complessa con l’utilizzo di robot da chirurgia. Naturalmente è necessaria la presenza di entrambe le parti, medico e paziente, nello stesso istante di tempo e il mezzo di comunicazione deve permettere una trasmissioni di dati in real time, per evitare complicazioni dovute a ritardi. La più utilizzata tecnica di telemedicina sincrona è quella mediante degli strumenti di video conferenza utilizzati per la telerefertazione. La telemedicina asincrona, invece, si basa sull’acquisizione dei dati medici, come radiografie digitali e simili, per poi trasmetterli al medico specialista che li valuterà successivamente. Non è richiesta la presenza delle parti nello stesso istante in quanto il dato è salvato su una struttura e quindi recuperabile in qualunque momento. In questa tesi verrà trattata questo tipo di telemedicina in quanto si adatta meglio alle esigenze e non necessita di particolari e specifiche attrezzature. Il funzionamento e la gestione dei dati sarà presentata nel capitolo successivo. 3 Capitolo 1 – La Telemedicina 1.2 Obiettivi Gli obiettivi della telemedicina sono quelli di poter fornire un adeguato servizio sanitario a chi, per un problema di distanza o per altri tipi di problemi, non può ottenere una prestazione sanitaria direttamente. Naturalmente questa non deve essere una condizione necessaria, ma solamente sufficiente, in quanto la telemedicina, come sopra citato, può essere finalizzata anche alla formazione professionale o anche solamente come metodo di confronto e accumulo di esperienze tra i vari Paesi nel mondo: il servizio offerto deve risultare e risulterà sicuramente migliore. La telemedicina quindi propone il suo maggiore utilizzo soprattutto per i paesi in via di sviluppo, come i paesi africani, nei quali le prestazioni mediche spesso non sono possibili o per mancanza di personale o per l’elevato costo. Le diagnosi vengono quindi trattate e i pazienti gestiti da strutture idonee, come possono essere ospedali o missioni umanitarie presenti sul posto. Si creano quindi le possibilità per un sensibile miglioramento della vita dei pazienti, permettendo di curarsi a domicilio o nelle vicinanze, senza quindi venire a creare problemi di alcun genere. Per il paziente è sempre possibile avere a disposizione degli specialisti che ovviamente accrescono la qualità delle decisioni mediche prese: il servizio che si vuole fornire è di elevata qualità. La tempestività e rapidità del sevizio ricopre un ruolo importante in questo campo, in quanto, si deve garantire la più possibile e tempestiva assistenza, soprattutto per la gestione di casi di urgenza. 4 Capitolo 1 – La Telemedicina L’obiettivo finale è quindi permettere a tutti di avere la possibilità di essere diagnosticati con lo stesso grado di affidabilità, e quindi sicurezza medica, in tutte le parti del mondo, anche senza essere direttamente a contatto con il medico, ovviamente avendo un occhio di riguardo per i paesi più bisognosi quali gli stati dell’Africa e tutti i paesi in via di sviluppo. 1.3 Vantaggi Con l’avvento e la diffusione delle tecnologie informatiche e di nuovi canali di comunicazione, come ad esempio internet, è stato possibile diffondere la telemedicina anche a grande distanza, introducendo vantaggi sia per il paziente che per lo stesso personale medico. La nascita della telemedicina permette infatti cure più rapide e minor spostamento di risorse, sia mediche che dei pazienti stessi, senza tralasciare che le comunicazioni e quindi gli aggiornamenti dei dati del paziente sono senza dubbio più veloci. Il tutto provoca un sensibile miglioramento sia della qualità dei servizi per il cittadino, che è più tutelato, sia delle condizioni di lavoro del personale medico, che può accedere e gestire più velocemente le informazioni. I tempi di ricovero, ma soprattutto la transizione casa-medicoospedale è sensibilmente ridotta, perché ovviamente il tutto avviene per vie telematiche e si evita anche molta burocrazia. Per casi particolari, come le calamità naturali o per temporanei aumenti di popolazione cui necessita una cura, l’utilizzo della 5 Capitolo 1 – La Telemedicina telemedicina si adatta perfettamente in quanto la sua struttura è molto flessibile. Inoltre con una ben strutturazione del sistema di gestione dati e organizzando al meglio il sistema sanitario si possono snellire le varie procedure e quindi contenere la spesa sanitaria, avendo perciò un riscontro economico positivo. Un altro vantaggio, che non ha riscontri nel concreto, ma che mi piace citare, è che con l’utilizzo della telemedicina si possono aiutare e salvare molte vite di persone meno fortunate, che non riescono ad avere una prestazione medica o se ce l’hanno è poco affidabile, il tutto anche solo stando comodamente nel proprio Paese ed avendo le migliori attrezzature a disposizione. 1.4 Svantaggi e problemi Un problema in passato è stato senza dubbio l’elevato costo delle attrezzature mediche e delle tecnologie utilizzate per la telecomunicazione. Anche lo stesso utilizzo di computer può essere un fattore negativo: il costo può risultare elevato, ma anche lo stesso utilizzo può risultare di difficile comprensione. Tuttavia questo problema è stato in parte risolto grazie all’abbassamento dei costi e ad una formazione informatica più diffusa: si cerca di diffondere metodi che favoriscano la comprensione delle tecnologie utilizzate in telemedicina. Anche l’utilizzo di formati di codifica e decodifica dei dati trasmessi non sono ancora standardizzati e quindi le apparecchiature possono avere problemi di compatibilità. La soluzione è puramente di 6 Capitolo 1 – La Telemedicina carattere burocratico, cioè basterebbe “mettersi d’accordo” su quale formato debba essere utilizzato: tale scelta dovrebbe essere duratura nel tempo così da poter uniformare il sistema. Un altro svantaggio è quello di dover sensibilmente cambiare i metodi tradizionali di lavoro. L’introduzione della telemedicina comporta infatti un cambio totale di mentalità, che a volte può provocare dei rifiuti verso la stessa. Anche il fatto di utilizzare internet per poter diagnosticare un paziente è una procedura che molti medici possono ritenere poco consona. In questo caso però, grazie alla dovuta formazione e alla presa di coscienza di questa nuova realtà, anche questo problema può essere superato agevolmente. 1.5 Applicazioni I campi in cui la telemedicina è utilizzata sono svariati e sono in continuo evoluzione e diffusione. La telepatologia, che è una branca della telemedicina, prevede infatti il trasferimento di immagini digitali macroscopiche e microscopiche a scopo diagnostico o educativo. In cardiologia è utilizzata per trasmettere i tracciati elettocardiografici, in radiologia invece vengono digitalizzate e trasmesse le radiografie, in dermatologia vengono trasferite foto digitali delle ferite cutanee, per la ginecologia vengono monitorate le gravidanze e via dicendo; ogni branca della medicina può utilizzare questa tecnologia per migliorare l’esercizio delle proprie attività sia cliniche che di formazione professionale. Oltre all’invio di dati la telemedicina prevede anche il mantenimento di dati quali il gruppo sanguigno, la pressione sanguigna 7 Capitolo 1 – La Telemedicina e una anamnesi totale del paziente: tali dati essendo in formato digitale sono semplici da reperire ed eventualmente da modificare. 1.6 La telemedicina in Italia In Italia la telemedicina è nata a partire dal 1976, con la trasmissione di elettrocardiogrammi a distanza. Con l’avvento del 118, per la gestione delle urgenze la telemedicina ha avuto un più ampio utilizzo: nel 1992 l’Italia era il primo paese nella Comunità Europea con il numero maggiore di sperimentazioni sul campo. Iniziative importanti sono stati il collegamento telematico dell’ospedale San Raffaele di Milano con l’ospedale di Sarajevo, il progetto che collega Napoli alle isole di Ischia e Procida e ancora molti altri progetti pilota presenti tra gli istituti di cura e le aziende locali. Nel 2002-2004 si è attuato un piano sanitario proprio propenso a promuovere la telemedicina e a valorizzarla. Si cerca quindi di diffondere la telemedicina al fine di collegare le iniziative periferiche sotto una gestione integrata dei servizi disponibili. Lo stesso sta avvenendo negli altri Paesi dell’Unione Europea. Anche nelle varie università italiane sono stati istituiti dei master, finalizzati alla teledidattica in medicina. Sempre nel 2002 è stato fatto uno studio sull’utilizzo di un “ospedale virtuale per l’assistenza sanitaria italiana all’estero”, che punta ad utilizzare i servizi offerti dalla telemedicina a favore di ospedali, centri italiani e di volontariato all’estero. Un altro progetto avviato nel 2002 è il progetto IPOCM (Integrazione e Promozione degli 8 Capitolo 1 – La Telemedicina Ospedali Italiani e dei Centri di cura con assistenza italiana nel mondo) molto simile al precedente citato e promosso sempre dal Ministero della Salute. 9 CAPITOLO 2 - IL PROGETTO “MEDTEL” In questo capitolo sarà presentato il progetto “MedTel” (Medical Telereporting System), un sistema per la telemedicina progettato in collaborazione con la ASUR di Ancona e l’Università Politecnica delle Marche. Grazie alle continue innovazioni nel campo dell’ampiezza di banda e nella diffusione nel mondo, abbiamo scelto internet come mezzo di comunicazione su cui basare il nostro progetto: internet sarà, se non lo è già, il mezzo del futuro in cui sarà possibile svolgere tutte quelle azioni che prima venivano svolte tramite uffici e via dicendo. Per questo motivo un progetto che sfrutta tale mezzo di comunicazione è destinato a durare nel tempo e a diffondersi, come noi vogliamo, sempre di più. MedTel infatti è un portale su internet che permette la telerefertazione, creato con lo scopo di essere utilizzato da chiunque ne abbia bisogno e in qualunque luogo esso sia, con bisogno solo di un personal computer e di una semplice connessione a internet. Per prima cosa abbiamo analizzato “a chi” fosse indirizzato tale software: come sopra citato tale strumento può essere utilizzato da tutti, ma come scelta di base è stata presa quella di basarsi su popolazioni e comunità africane. Quindi abbiamo dovuto analizzare “cosa” veniva richiesto e solo successivamente “come” doveva essere concepito: il software creato doveva avere una completa flessibilità ed anche essere di semplice utilizzo. Non doveva comunque essere trascurata l’affidabilità e la sicurezza in quanto le informazioni trattate sono molto sensibili. 10 Capitolo 2 – Il progetto MedTel Come idea ci siamo basati su Miro, un progetto già cominciato sempre nato tra la collaborazione della ASUR di Ancona e l’Università Politecninca delle Marche. 2.1 Finalità L’obiettivo del nostro progetto è quello di fornire all’azienda sanitaria di Ancona un prodotto flessibile, compatto, portabile e di facile utilizzo da chiunque in futuro ne voglia approfittare. Il prodotto creato fornisce un servizio asincrono in grado di superare qualunque soglia spaziale in quanto è diffuso tramite internet. La sicurezza e l’affidabilità doveva essere comunque garantita perché i dati trattati sono sensibilmente importanti. Si è deciso infatti di dotare il portale di un accesso limitato e controllato solamente dall’amministratore, per assicurare appunto che informazioni sensibili vengano a contatto solo con le persone corrette. MedTel punta ad essere un software di telemedicina il più completo possibile: mira a gestire tutto il processo di telerefertazione, ma anche tutto il processo di teleconsulto, in quanto entrambi fondamentali. 2.2 Figure partecipanti L’architettura ideale è costituita essenzialmente da due “figure” partecipanti quali il requester, che non è nient’altro che colui che richiede la refertazione, ed il dottore, che invece è colui che effettua fornisce la diagnosi. 11 Capitolo 2 – Il progetto MedTel In mezzo a queste due figure vi è una terza entità che è il luogo dove i dati vengono memorizzati. Tale entità ha riscontro grafico nella la cartella clinica. Infatti la cartella clinica svolge un ruolo centrale nell’ambito della comunicazione dottore-requester perché contiene tutti i dati del paziente sotto esame. L’archiviazione viene effettuata ovviamente in un database, dal quale la cartella clinica preleverà i dati riguardanti e necessari per quel paziente: ovviamente saranno presenti varie informazioni aggiuntive riguardante l’esame quali la data ecc. La cartella clinica verrà presa in esame nel capito successivo. Il funzionamento del portale si basa sul concetto di evento. Questo punto è molto importante nella concezione dell’intero progetto. Un evento è la “formalizzazione informatica” di un problema clinico. Con tale definizione voglio intendere la digitalizzazione per esempio di uno o più esami di laboratorio e la conseguente archiviazione. 2.2.1 Dottore Il dottore è un dei tre attori presenti nel sistema. Esso rappresenta i medici o meglio ancora il personale specializzato che fornirà la diagnosi o una seconda opinione sul paziente. La figura del dottore entra in gioco quando viene aperto un evento e quindi il requester richiede la refertazione: ovviamente ogni medico avrà una o più specializzazioni e quindi sarà interessato ai soli pazienti che rientrano in queste. L’accesso del dottore è effettuato, come del resto per tutte le altre figure presenti nel sistema, tramite un web browser qualunque e l’opportuna autenticazione nel portale. La porzione di software che da 12 Capitolo 2 – Il progetto MedTel qui il dottore è in grado di utilizzare è composta da interfacce utenti semplici e create appositamente per essere utilizzate nella maniera più intuitiva possibile: sono state concepite per essere utilizzate anche senza nessuna conoscenza informatica rilevante. La schermata iniziale comprende una guida su cosa il dottore può fare e su come farlo e una pagina con tutti gli eventi su cui il dottore può refertare. Quando il dottore accede a tale evento ovviamente compare anche la cartella clinica del paziente con tutti i suoi dati, dal profilo all’anamnesi fino ai vari problemi clinici riscontrati: è ovviamente stata implementata anche la possibilità di poter scaricare il dato ottenuto dall’esame di laboratorio. Il dottore può anche dare una seconda opinione, se richiesta, riguardo un problema clinico. È stata aggiunta inoltre la possibilità di leggere anche i referti degli altri medici. Con tale meccanismo si effettua uno scambio ancora maggiore di informazioni, si aumenta la qualità del servizio e si effettua anche formazione medica. 2.2.2 Requester Il requester è la figura del sistema MedTel che rappresenta qualsiasi struttura che necessita la refertazione di un esame: può essere sia un laboratorio di analisi, sia un ospedale nonché un privato cittadino con necessarie conoscenze cliniche. Il termine requester infatti è stato opportunamente utilizzato per definire un ente “richiedente”, di qualsiasi tipologia, che voglia farsi refertare un esame. La figura del requester è anche quella che genera gli eventi. Dopo aver effettuato l’esame, deve provvedere alla digitalizzazione e all’inserimento dello stesso evento nel sistema. Come per il dottore, il 13 Capitolo 2 – Il progetto MedTel requester accede tramite un web browser al portale MedTel, si autentica e successivamente ha di fronte un’interfaccia simile a quella del dottore. Sara presente inizialmente una descrizione delle azioni che il requester può compiere, come l’inserimento di nuovi pazienti o la gestione di quelli esistenti. Inoltre avrà la solita schermata di ricapitolazione dei problemi clinici inseriti. Attenzione a non confondere la presenza di un paziente con un problema clinico: non è detto che un paziente presente nel sistema debba necessariamente avere un problema clinico, può essere ancora in fase di aggiornamento, in quanto il sistema è asincrono e non real time. Delle organizzazioni potrebbero anche solo mantenere un elenco di tutti i pazienti, senza necessariamente avere dei problemi da refertare, per la semplice volontà di avere un elenco informatico compatto e di rapida consultazione. Infatti oltre ai problemi da refertare, il requester deve inserire anche i dati che il paziente come ad esempio le informazioni sull’anamnesi. Ritornando alla generazione dell’evento, una volta effettuata, viene scritto il dato con le varie informazioni nel database centrale. Da qui il requester attende la refertazione o osserva le varie refertazioni finché non si potrà ritenere soddisfatto. Sono quattro le fasi della refertazione che il requester monitorizza, anche se non è detto che debbano necessariamente essere tutte svolte: la generazione dell’evento e lo stato dello stesso impostato a “open”; la refertazione da parte del dottore e lo stato impostato a “reported”; la volontà del requester di avere una successiva refertazione con il conseguente stato impostato a “request another”; e la chiusura del report nel quale il requester si ritiene soddisfatto della refertazione ottenuta, con stato impostato a “close”. 14 Capitolo 2 – Il progetto MedTel 2.2.3 Struttura Dati La struttura dati è dove vengono memorizzati tutti i dati inseriti dal requester e quindi prelevati dal dottore. Questa struttura non è nient’altro che un database relazionale costituito da tabelle che contengono le informazioni, divise per argomenti. Passiamo ora ad analizzare le tabelle contenute nel database, che come già detto sono relazionate tra loro. Infatti ogni tabella è legata ad un’altra tabella tramite chiave esterna, che permette di rendere il tutto più flessibile e dinamico in caso di aggiornamento e manutenzione. Questo avviene soprattutto nelle tabelle che rispecchiano elenchi di nomi selezionabili nella cartella clinica. Si pensi al caso della tabella contenente le informazioni sulle allergie: i nomi delle varie allergie sono contenute in una tabella separata rispetto a quelle delle allergie di quel determinato paziente, così facendo quando si dovrà inserire una nuova allergia o modificarne una vecchia, non si dovrà correggere il codice sorgente della pagina web in questione, ma solamente modificare una campo del database. La gestione dei referti effettuati e dei messaggi spediti è anch’essa gestita tramite delle tabelle. Anche le informazioni relative al paziente sono memorizzate in una tabella apposita, come del resto i dati di ogni singolo utente registrato al progetto MedTel. Per quanto riguarda i file scaricati nel server dai requester si è scelto di adoperare la seguente soluzione: per ogni file scaricato, viene salvato il percorso attuale in un record di una tabella, nel quale sarà presente oltre a tale percorso, anche il nome del file scelto. Il download quindi avviene attraverso la lettura di questi due campi. Come già detto sopra la visualizzazione di questi “meta-dati” verrà resa possibile grazie alla cartella clinica. Proprio 15 Capitolo 2 – Il progetto MedTel l’importanza dei dati e delle informazioni qui presenti saranno oggetto del capito successivo, completamente dedicato alla cartella clinica. 2.3.3 Utenti e Privilegi In questa sezione sarà presentata l’organizzazione dei privilegi assegnati agli utenti nel progetto MedTel. Le seguenti scelte sono state fatte in quanto, oltre a dover differenziare dottore e requester, servivano delle figure “collaborative”, necessarie per la gestione e manutenzione del progetto. Ogni privilegio è gestito all’interno della struttura dati in cui è presente una tabella utenti con questo genere di informazioni: un campo ne contraddistingue il “tipo” di utente. Due tipologie di utenti, come si potrà ben capire, non sono nient’altro che le due figure sopra citate, cioè il dottore ed il requester. Questi sono differenziati a livello di database e ovviamente l’accesso consente differenti visualizzazioni e interfacce. Oltre a queste due tipologie di utenti ne sono state create altre due, necessarie per l’amministrazione. La prima presa in esame è il super-amministratore, cioè colui che ha tutti i privilegi sul database: il suo compito non sarà quello di creare dottori o requester, ma di creare i manager. È prevista anche la cancellazione e quindi il riassegnamento a qualche altro manager degli utenti creati. I manager, che è appunto la seconda tipologia creata, sono coloro che hanno il compito di creare i dottori e i requester, ma sono impossibilitati a creare altri manager. Tale figura è anche responsabile della cancellazione di dottori e requester ed in questo ultimo caso, anche alla riassegnazione dei pazienti gestiti. 16 Capitolo 2 – Il progetto MedTel Tale gerarchia a piramide, ci è sembrata la più solida e la più flessibile di fronte ad una possibile espansione, in quanto il lavoro è suddiviso per gradi e con il meccanismo di rassegnazione non vi è possibilità di perdere parzialmente dati o peggio ancora pazienti. 2.3 Soluzioni tecniche adottate In questa sezione introdurrò le soluzioni tecniche che abbiamo utilizzato per svolgere il progetto. Innanzitutto ricordo che avevamo bisogno di costruire un prodotto innovativo e flessibile, ma anche molto user-friendly, in quanto diretto ad un pubblico non necessariamente preparato dal punto di vista informatico. Serviva costruire il portale utilizzando delle soluzioni che fossero sia complete e funzionali, ma senza trascurare la “leggibilità” del software creato: vorrei precisare che il progetto oltre che essere pienamente funzionale dal punto di vista informatico, doveva quindi esserlo, con la stessa importanza, graficamente. Inoltre dovevano essere utilizzati ovviamente prodotti open source. Ci siamo dovuti immedesimare in coloro che avrebbero dovuto utilizzare questo software e capire quali soluzioni potessero andare meglio. Per il server, la scelta è ricaduta su Kubuntu Server, che offre affidabilità e sicurezza, senza tralasciare le prestazioni e la stabilità. Tale server ovviamente è stato collocato presso un laboratorio del D.E.I.T. al polo di Ingegneria. 17 Capitolo 2 – Il progetto MedTel Apache Tomcat Il web server utilizzato invece è stato Apache Tomcat 6.0. Si tratta di un prodotto con licenza Apache, cioè con la possibilità di utilizzo anche per progetti non open source e senza l’obbligo di rilascio dei sorgenti modificati: è una licenza per software libero. Si discuteranno ora brevemente alcune delle caratteristiche principali di questo web server. Innanzitutto il nostro progetto, che è un’applicazione web, ha bisogno di uno schema gerarchico prestabilito di file e directory. Tutto questo in Tomcat avviene attraverso la directory radice /webapp. Questa directory rappresenta il context path dei servlet che si creeranno. Tale directory conterrà tutti gli elementi che compongono le applicazioni web e quindi sarà il luogo in cui metteremo anche il nostro progetto: in questa directory o nelle eventuali sottodirectory quindi andranno messi i file e le directory come per un sito web statico. Per la directory /webapp o le eventuali sottodirectory contenenti l’applicazione web andranno comunque presi degli accorgimenti particolari e sarà necessario la creazioni di altre directory particolari. All’interno della directory root della nostra applicazione si troverà una directory speciale, chiamata WEB-INF. In questa directory sono presenti i file o directory riservati, come quelli di configurazione. Uno dei file contenuti in questa cartella è il file web.xml. Questo file è il descrittore della nostra applicazione web e contiene la configurazione necessaria per il funzionamento dei servlet: vengono definiti gli alias e mappati i percorsi dei servlet. Sempre all’interno della cartella WEB-INF sono contenute le directory classes e lib. La prima contiene le classi java compilate, la 18 Capitolo 2 – Il progetto MedTel seconda invece è destinata a contenere gli archivi Jar necessari per il funzionamento dell’applicazione web (librerie esterne). Nel nostro caso particolare, siamo un po’ usciti da questa regola, in quanto il deploy module automatico di Eclipse invece di copiare le classi compilate nella directory classes, crea un archivio Jar e lo posiziona nella cartella lib, insieme a quelle esterne. Passiamo ora ad elencare come si effettua il deployment dell’applicazione web stessa in Tomcat. L’applicazione web creata viene compressa in un archivio WAR (Web Application Archive), molto simile ad un pacchetto Jar. Questi archivi hanno la funzione di contenere tutta l’applicazione in un unico archivio, ma anche di rendere operativa l’applicazione web: infatti, in pratica, basta copiare il suddetto file WAR nella cartella /webapp di Tomcat e lo stesso web server provvederà allo “scompattamento” in una directory con lo stesso nome del file WAR. La struttura dati e le interfacce La struttura dati è stata implementata su un database MySQL, in quanto è in primis un prodotto open source, è veloce ed infine è semplice nel suo utilizzo. La compatibilità è un altro punto molto importante: MySQL è compatibile sia con i sistemi basati su Unix che su MS Windows. Verrà ora presentato un elenco di tutte le tabelle presenti nel database, con una sommaria decrizione di esse. 19 Capitolo 2 – Il progetto MedTel Ecco l’elenco delle tabelle: • Allergies: elenco delle allergie che è possibile attribuire ad un paziente; • Allergy_categories: le categorie in cui le allergie sono suddivise; • Anamnesis_allergologies: allergie di ogni relativo paziente; • Anamnesis_familiars: informazioni relative ai genitori dei pazienti; • Anamnesis_family_pathologies: patologie avute dai parenti dei pazienti; • Anamnesis_pathologicals: informazioni sulle patologie passate diagnosticate ai pazienti; • Anamnesis_physiologicals: problemi fisiologici generali dei pazienti; • Events: informazioni sugli eventi; • Exams: elenco degli esami memorizzabili; • Exams_files: mappatura dei file degli esami di laboratorio; • Exams_users: relazione tra utente ed evento associato; • Pathologies: elenco delle patologie memorizzabili; • Patients: tutti i dati di carattere generico (non medico) dei pazienti; • Reports: informazioni riguardo ai referti e ai messaggi; • Users: informazioni sugli utenti registrati, tra cui i privilegi; • Visits: dati memorizzati riguardanti i problemi clinici, non relativi all’anamnesi; Per la creazione vera e propria del portale, cioè per tutte quelle iterazioni lato client e server, e per la creazione delle interfacce grafiche, abbiamo scelto il framework open source fornito da Google: il Google 20 Capitolo 2 – Il progetto MedTel Web Toolkit (GWT). Questa soluzione è stata presa di comune accordo con la ASUR di Ancona, in quanto rappresentava, oltre che ad una soluzione open source, anche un nuovo modo di creare portali sul web ed in più risolveva molto i problemi legati al creare un portale graficamente semplice e funzionalmente intuitivo. Una dettagliata presentazione di questo framework verrà fatta nel capitolo 4. 21 CAPITOLO 3 - LA CARTELLA CLINICA La cartella clinica è la figura centrale del sistema MedTel. Essa contiene tutte le informazioni relative al paziente ed in più rappresenta il “luogo” in cui avviene lo scambio di informazioni tra dottore e requester. Infatti la cartella clinica non è nient’altro che la soluzione grafica di tutti i dati relativi al paziente presenti nel database centrale: tutti gli eventi e le informazioni vengono salvate in questo database e visualizzate tramite opportune interfacce agli utenti, siano essi appartenenti alla categoria dottore o requester. Una cosa importante da dire è che ovviamente la cartella clinica sarà presentata ugualmente sia per il dottore che per il requester, con l’unica importante differenza, che quest’ultimo è l’unico che può e deve inserirvi e manipolare i dati all’interno: solo la figura del requester, che è colei che gestisce il paziente, è a contatto diretto con lo stesso. Verrà quindi prima descritta in modo generico con una panoramica di funzioni generali, per poi passare nello specifico di ogni sezione della cartella clinica. Non viene in questo capitolo tuttavia menzionata nessuna delle soluzioni tecnologiche utilizzate, in quanto la descrizione dettagliata di queste è contenuta nel capitolo 5. 22 Capitolo 3 – La cartella clinica 3.1 Comʼè costituita La creazione della cartella clinica è un passo molto importante. Le caratteristiche che infatti doveva avere la nostra cartella clinica sono le seguenti: avere a disposizione i dati anamnestici e del paziente in generale, fornire con una certa rapidità le informazioni desiderate, essere flessibile e duratura nel tempo. Innanzitutto bisogna dire che in ambito medico esistono due tipi di cartelle cliniche, quelle utilizzata in medicina generale e quella utilizzata in ambito ospedaliero. In ospedale sarà necessario focalizzare l’attenzione sugli eventi prossimi che portano a un certo iter diagnostico verso una determinata patologia. In medicina generale invece deve essere utilizzata una cartella clinica orientata ai problemi, in cui tutti i dati ruotino attorno al problema per cui è richiesta la visita. Si distinguono quindi tra problemi attivi, cioè quei problemi che non hanno ancora trovato soluzione, e problemi inattivi, i quali sono già stati risolti. La nostra cartella clinica contiene i dati di base generici del paziente, dalle allergie ai problemi familiari; una lista dei problemi, siano essi attivi che inattivi, utilizzati per una buona diagnosi; un “diario clinico” del paziente che comprende i dati oggettivi, quali la pressione, il polso e simili, e dati soggettivi, come i sintomi manifestati. Tutto questo è corredato dalla possibilità di visualizzare un eventuale esame di laboratorio, prima digitalizzato e memorizzato dalla struttura che gestisce il paziente. Un’ultima osservazione sulla costituzione della cartella clinica va fatta riguardo alla terminologia utilizzata. Infatti nell’inserimento dei dati sarebbe meglio utilizzare una terminologia universale, atta 23 Capitolo 3 – La cartella clinica soprattutto a non creare possibili diversità di interpretazione tra i vari medici, cosa molto importante nel campo della telemedicina. 3.2 Il profilo del paziente Passiamo ora ad analizzare la struttura vera e propria della cartella clinica. Inizialmente è presente il profilo del paziente con tutti i suoi dati anagrafici: questo profilo serve al requester per poter riconoscere il proprio paziente in caso di gestione di una grande mole di pazienti; inoltre serve anche al dottore per sapere con che paziente ha a che fare, in quanto magari la provenienza potrebbe anche essere un fattore da considerare in caso di diagnosi. I dati presenti nel profilo sono la data di nascita e la città di nascita, informazioni generali sulla residenza attuale, la struttura in cui è ospitato, più altre informazioni generali quali il sesso, il grado di istruzione, lo stato civile e la professione svolta, senza trascurare che sono anche presenti informazioni per contattare direttamente il paziente come il numero di telefono e l’e-mail. Inoltre è anche stata implementata la possibilità di mantenere in archivio pazienti deceduti, perciò come dato verrà anche fornita la data di morte. Questo è stato fatto per il semplice motivo di avere una più ampia visione d’insieme della situazione locale: si pensi che, per esempio, in caso di un’epidemia sarebbe utile sapere il tempo in cui si trasmette e dopo quanto avviene l’eventuale decesso. I dati utili per contattare il paziente direttamente sono stati inseriti per essere utilizzati come “rubrica” da parte del requester e far aumentare la comunicazione e quindi la qualità del servizio offerto. 24 Capitolo 3 – La cartella clinica Ricordo inoltre che gli stessi dati sono visibili sia dal requester che dal dottore: tali dati non dovrebbero essere utilizzati dal dottore per contattare direttamente il paziente, tranne che per emergenza. Non devono essere quindi utilizzati come modo per scavalcare la figura del requester, che deve e dovrà fare da tramite tra il sistema e il paziente. Infatti spesso il paziente non ha conoscenze mediche adatte a fornire, ne a capire, diagnosi o altre generalità mediche. Se si sfruttassero quindi tali dati per creare un legame diretto dottore-paziente non si rischierebbe nient’altro che peggiorare la situazione. 3.3 Lʼanamnesi Successivamente è presente la sezione riguardante l’anamnesi del paziente con varie sottocategorie così dal rendere tutto più immediato ma allo stesso tempo avere una visione completa e dettagliata di dati relativi al paziente. La sezione anamnesi è stata creata con l’intento di avere sempre tutte le informazioni davanti al dottore: tutte le informazione, che verranno sotto descritte, rimangono sempre in primo piano e sempre quindi visibili. Ogni sottosezione è prima presentata da un riepilogo della stessa, con un elenco delle patologie e problematiche generali; successivamente si può anche accedere nel dettaglio di queste, avendo informazioni sempre più specifiche. Abbiamo deciso di operare con questo genere di struttura per dare un’ampia ma completa visuale del paziente, cosicché al primo impatto già il dottore può avere un’idea generale sul da farsi. 25 Capitolo 3 – La cartella clinica Le sezioni di seguito sono identiche dal punto di vista del dottore e del requester: a questo proposito quindi bisogna ricordare che le informazioni citate sono inserite dal requester, tramite un’apposita form di inserimento, e recepite dal dottore. Nel formare questa sezioni ci siamo basati sulla considerazione che è meglio fornire più informazioni possibile, anche magari in surplus, piuttosto che fornirne meno, in quanto questo può causare una mancata diagnosi corretta, tutto a discapito del paziente. Allergie La prima sottosezione che si incontra è quella relativa alle allergie del paziente. La prima scheda riassuntiva fornisce informazioni sul numero di allergie salvate nella struttura dati e relativi a quel paziente, e naturalmente quali allergie questo paziente ha o ha avuto. Quando si entra nel dettaglio vengono forniti informazioni in dettaglio dell’allergia in questione: la data di inizio dell’allergia e l’eventuale data di fine se questa non è più presente. Come ultimo, sono presenti anche delle note generiche sull’allergia riscontrata che possano essere utili per una migliore diagnosi o lettura stessa della cartella clinica: si è cercato di rendere il più dettagliato possibile la rilevazione dell’allergia. Informazioni generali sui genitori La sottosezione successiva è quella comprendente i dati della famiglia. Le informazioni qui presenti sono riguardanti il padre e la madre del paziente. 26 Capitolo 3 – La cartella clinica Qui non è presente come nella scheda precedente una parte riassuntiva, in quanto i dati sono immediati. Dal punto di vista del dottore, qui vengono prelevate le seguenti informazioni: viene reso noto se sono ancora vivi i genitori e qual è il loro gruppo sanguigno. Naturalmente anche qui viene data la possibilità a chi gestisce il paziente di inserire delle note aggiuntive riguardanti la famiglia, ovviamente inerenti allo stato clinico e propense ad ottenere una migliore diagnosi. Patologie familiari Nella prossima sottosezione vengono invece presentate le informazioni sulle patologie di tutta la famiglia, cioè quelle persone con legami di sangue con il paziente. È qui presente una scheda riassuntiva del numero e di quali patologie i familiari abbiano avuto. Per il dettaglio, invece, è anche presente proprio un elenco di quali siano stati i membri della famiglia ad avere avuto quella specifica patologia. Questa sottosezione è molto importante soprattutto per stabilire se ci siano possibilità di malattie genetiche e quindi ereditabili dal paziente, o comunque connessioni fra malattie familiari e del paziente: non solo collegamenti genetici ma magari anche di carattere ambientale in cui vive il paziente. 27 Capitolo 3 – La cartella clinica Patologie del paziente Nella sottosezione successiva invece si ha un resoconto completo e dettagliato delle patologie che ha riscontrato precedentemente il paziente. Anche qui, come nelle patologie familiari, è presente il riassunto che fornisce quali patologie sono state riscontrate nel paziente. Se si entra nel dettaglio, oltre alla patologia, come informazioni utili vengono fornite l’età a cui è stata diagnosticata e, nel caso che ce ne sia stato bisogno, il dettaglio del ricovero in ospedale. Infatti vengono forniti i dati del ricovero ospedaliero: viene resa nota la data di entrata e quella di uscita dall’ospedale, nonché informazioni su quest’ultimo. Saranno presenti inoltre la diagnosi ricevuta dal paziente e le note con cui è stato dimesso, per poter confrontare con una eventuale nuova diagnosi. In questa sottosezione comunque non devono per forza di cosa essere inserite le patologie che hanno causato una permanenza in ospedale, ma tutte quelle che il paziente ha avuto nell’arco della sua vita. Problemi fisiologici Nell’ultima sottosezione dell’anamnesi sono presenti informazioni mediche generali riguardanti il paziente. Infatti, vengono visualizzate qui le note riguardo i problemi fisiologici del paziente quali la digestione o il sonno, ma anche informazioni riguardo il gruppo sanguigno del paziente e quello dell’eventuale partner, con la possibilità di note aggiuntive riguardo proprio il gruppo sanguigno. 28 Capitolo 3 – La cartella clinica Ogni nota dovrebbe essere ben esaustiva, inquadrare subito il problema e fornite anche più dettagli possibili: si deve ricordare che il dottore non è a contatto diretto e quindi non può visitare di persona il paziente. 3.4 Problemi clinici del paziente Ora passiamo all’ultima parte da cui è composta la cartella clinica e che rappresenta anche il vero punto in cui avviene lo scambio tra l’esame digitalizzato dal requester, prelevato (tramite download) e successivamente refertato dal dottore. In questa sezione è presente un elenco di tutti i problemi clinici del paziente in esame, con l’aggiunta di informazioni come la data, il numero di esami svolti su ognuno e ovviamente lo stato di ogni evento. Bisogna ora differenziare il discorso tra dottore e requester. Il dottore ha la possibilità, una volta selezionato il problema clinico da prendere in esame, diverse opzioni. Prima di iniziare a spiegare tali opzioni è utile ricordare che il dottore può osservare tutti gli esami svolti, ma può refertare solo i problemi riguardanti la sua specializzazione. Come prima cose deve selezionare l’esame svolto che vuole refertare o comunque controllare: infatti è possibile che siano stati fatti differenti esami per lo stesso problema clinico. I dati che emergono da tale selezione sono quelli relativi al paziente, quali peso, altezza, 29 Capitolo 3 – La cartella clinica informazioni sulla pressione sanguigna, sulla frequenza cardiaca, l’indice di massa corporea e infine delle conclusioni generali. Successivamente può inserire un nuovo messaggio, con cui magari può richiedere ulteriori informazioni in merito all’esame svolto; scaricare sul proprio computer l’esame digitalizzato; refertare l’esame selezionato compilando un form apposito. Questo form, uguale sia per l’inserimento dei referti che dei messaggi, permette di inserire informazioni a campo libero, tenendo pero a debita considerazione che ogni referto e messaggio è associato al dottore che lo ha scritto. Sono disponibili, inoltre, le opzioni per la visualizzazione di altri messaggi e referti, nel caso si voglia tenere in considerazione una seconda opinione. Attenzione a non dimenticare i vari stati di ogni singolo problema clinico: dove il problema è classificato come “close” o “reported”, il dottore ovviamente non potrà effettuare la refertazione o aggiungere messaggi, infatti tale operazione è possibile solo per lo stato “open” o “request another”. L’interfaccia che si pone di fronte al requester è molto simile a quella del dottore. Il requester da questa sezione può inserire i problemi clinici relativi a quel particolare paziente. Alla creazione del problema clinico vengono richieste alcune informazioni. Bisogna scegliere innanzitutto come classificare il problema, così da poter coinvolgere il giusto dottore specializzato: si sceglierà la categoria dell’esame in base alle specializzazioni mediche così da avere un semplice legame medico specializzato-problema clinico. A questo punto il requester deve inserire i sintomi che tale problema ha mostrato e anche alcuni dati caratteristici del paziente come l’altezza, il peso, la pressione sanguigna e la 30 Capitolo 3 – La cartella clinica frequenza cardiaca, l’indice di massa corporea, delle considerazioni fisiche e una conclusione esaustiva sul tutto. Ovviamente è possibile aggiungere un esame di laboratorio effettuato per tale problema e fare l’upload sul server del file corrispondente. Per quanto riguarda i problemi clinici già presenti, è possibile inserire nuovi esami di laboratorio, così da dettagliare il problema. La procedura è identica a quella per l’inserimento di un nuovo problema clinico, anche se qui non si andrà a creare un problema clinico ma solo ad aggiungere un esame a quello esistente, vengono comunque chiesti i dati del paziente sopra citati in quanto magari possono essere cambiati. Il requester può anche leggere i messaggi e soprattutto i referti dei dottori che hanno risposto: ogni referto o messaggio contiene le informazioni anche relative all’ora, alla data e soprattutto da quale dottore questo è stato emesso. Come sopra annunciato il requester può qui monitorare l’evento decidendone la chiusura se è soddisfatto della refertazione o richiedere una nuova opinione cambiando lo stato da “reported” in “request another”. La possibilità di richiedere un’ulteriore analisi è a discrezione del requester e dalla sua soddisfazione nel leggere le altre diagnosi: non è prevista comunque nessuna limitazione in tal senso. Essendo nel campo della telemedicina asincrona inoltre dallo stato di “close” è anche possibile passare allo stato “request another”, in quanto magari il problema non si è effettivamente risolto: questo può capitare per via dell’inesperienza del requester, che si avvale di conoscenze mediche non sempre all’altezza del problema che gli si pone e quindi potrebbe anche non riconoscere eventuali sintomi presenti, considerando quindi il problema risolto. 31 Capitolo 3 – La cartella clinica Come sopra prima accennato è qui che avviene il vero scambio di informazioni tra requester, e quindi indirettamente tra il paziente, ed il dottore. Tutto il progetto MedTel si basa sullo scambio di tali informazioni e finalizza il suo intento in una diagnosi più precisa e accurata possibile: il tempo speso nel costruire la miglior cartella clinica possibile è quindi tempo ben utilizzato per poter avere un risultato più corretto e rapido, senza continue perdite di tempo dovute alla comunicazione asincrona tra le due figure in gioco. 32 CAPITOLO 4 - GOOGLE WEB TOOLKIT IL Google Web Toolkit (GWT) è un frame work di sviluppo per applicazioni AJAX rilasciato con licenza open source Apache 2.0. Con il GWT lo sviluppatore può creare applicazione AJAX in Java: ci si porta quindi ad un livello di astrazione superiore di quello nativo dell’AJAX, cioè senza scrivere codice HTML o JavaScript. Attraverso l’uso di GWT è possibile quindi utilizzare i soliti IDE disponibili per la creazione di progetti Java: nel nostro caso è stato utilizzato Eclipse in primis perché open source, per via della sua semplicità nel gestire progetti di medie-grandi dimensioni come il nostro, nonché per la sua integrazione naturale con il GWT. La pagina prodotta sarà comunque una pagina HTML contenente codice JavaScript: il tutto viene eseguito da un compilatore Java-toJavaScript presente nel GWT. GWT mette a disposizione dello sviluppatore delle librerie grafiche, simili alle Swing, che permettono di creare applicazioni dalla grafica avanzata: questi componenti sono chiamati widget. L’applicazione creata non avrà nulla da invidiare alle altre applicazioni stand-alone desktop, infatti l’obbiettivo principale di questo tool è quello di fornire un’interfaccia immediata, sempre attuale e moderna. Sono messi a disposizione una serie di widget nativi molto interessanti come alberi, tabs ecc. Inoltre nel nostro progetto abbiamo inserito anche delle librerie che appunto mirano ad aumentare l’effetto grafico e a rendere il tutto più coinvolgente. Le GwtExt infatti fondono il GWT con le librerie Ext, 33 Capitolo 4 – Google Web Toolkit ovvero delle librerie JavaScript che appunto creano nuovi effetti grafici. Anche l’utilizzo di quest’ultime librerie è ovviamente open source. Ora passiamo a descrivere le motivazioni per cui uno sviluppatore dovrebbe utilizzare GWT rispetto allo scrivere codice JavaScript. La motivazione principale sta tutta nel fatto che la tecnologia Java offre diversi vantaggi nello sviluppo rispetto al Javascipt. Java verifica staticamente i tipi di dato riducendo notevolmente gli errori a runtime ed inoltre un’applicazione Java è più semplice da comprendere e soprattutto gestire rispetto ad una JavaScript. Da non dimenticare poi che per Java è presente un insieme molto più ricco di tool di sviluppo come lo stesso Eclipse utilizzato da noi. Un altro pregio è che con l’utilizzo del GWT e quindi del suo compilatore, gli errori JavaScript non vengono commessi, in quanto il compilatore di google non dovrebbe sbagliare nel passaggio da Java a JavaScript. Con questo strumento quindi anche l’analisi e il debug è molto più efficace in quanto possibile riga per riga e classe dopo classe, come per una normale applicazione Java. Un altro aspetto importantissimo è che l’utilizzo del GWT ci permette di avere la completa compatibilità, almeno teorica, con i browser più noti, come IE, Firefox, Mozilla, Safari ed Opera, senza dover differenziare il codice sorgente: la dicitura “teorica” è dovuta al fatto che alcuni comportamenti non sono esattamente gli stessi, ma se ne parlerà meglio nel capitolo 6. 34 Capitolo 4 – Google Web Toolkit 4.1 Caratteristiche Un aspetto caratterizzante di GWT è la presenza del compilatore che converte il codice Java in JavaScript: generalmente se l’applicazione è compilata ed eseguita correttamente in hosted mode, il codice Javascipt generato funzionerà ugualmente anche nel browser scelto. Come sopra detto il GWT si integra facilmente con Eclipse: con una delle opzioni disponibili da riga di comando è addirittura possibile creare un progetto con i file necessari per il funzionamento sotto Eclipse. Una delle principali caratteristiche del GWT resta comunque la dinamicità e la possibilità di riutilizzare le componenti grafiche. I programmatori infatti posso utilizzare classi già create e con queste creare ottime e soprattutto sofisticate applicazioni grafiche: l’interscambio di classi è uno dei punti di forza nell’utilizzo di GWT come del resto anche del Java stesso e un po’ di tutti i linguaggi orientati agli oggetti. È facilmente possibile integrare librerie esterne, oltre a quelle di GWT. Infatti anche le nostre GwtExt sono state integrate senza problemi. Inoltre anche il codice JavaScript puro può essere inserito direttamente nel codice sorgente Java: questo è possibile grazie alle JavaScript Native Interface (JSNI). Naturalmente è possibile integrare anche le numerose librerie esterne fornite dallo stesso Google, come le API di Google Earth. Un’altra importante caratteristica è la possibilità di una semplice gestione della cronologia della navigazione. Un problema infatti delle 35 Capitolo 4 – Google Web Toolkit applicazioni AJAX è infatti quello dell’interazione con il browser. Nella gestione della cronologia, l’applicazione deve “aiutare” il browser a ricordare quale era lo stato precedente a cui, per esempio deve tornare il browser: tutto questo sorge perché spesso in AJAX e quindi anche in GWT l’applicazione è formata da una sola pagina, non da molte come per un sito statico. Il GWT gestisce tale problematica con un meccanismo che tiene traccia dello stato precedente, mediante l’utilizzo di token, nella parte finale dell’url: viene segnalato lo stato in quel momento e se successivamente basterà solo invocare tale stato tramite un opportuno listener. Come sopra detto, una delle caratteristiche principali è anche la possibilità di scegliere il proprio ambiente di sviluppo. GWT non impone niente in tal senso, infatti lo sviluppatore può utilizzare l’IDE che meglio crede o quello con cui è più abituato a gestire i propri progetti Java. Un’altra caratteristica è la continua evoluzione. Essendo un software open source, molte persone nel mondo stanno collaborando alla creazione di nuovi ed interessanti componenti, nonché alla risoluzione di problemi con le vecchie interfacce. Soprattutto per un prodotto che punta molto sull’aspetto grafico, la continua evoluzione grafica che ci offre questo tool è molto importante, soprattutto per avere sempre un prodotto aggiornato. Riguardo alle caratteristiche di funzionamento, le applicazioni GWT possono essere eseguite in due modalità: Hosted mode oppure Web mode. Passiamo ora alla descrizione di entrambe. 36 Capitolo 4 – Google Web Toolkit Hosted Mode Nella Hosted mode l’applicazione viene fatta girare dalla Java Virtual Machine (JVM): il tutto viene eseguito nel browser interno di GWT. Questa modalità è utilizzata per effettuare il debug o comunque per testare l’applicazione prima della pubblicazione: Eclipse permette un’interfacciamento completo con l’avvio automatico durante i test del browser interno di GWT. Molto del tempo nello sviluppare la nostra applicazione è stato passato utilizzando questa modalità: infatti è possibile effettuare il debug e testare sia il lato client sia il lato server dell’applicazione, così da rendersi conto del corretto o scorretto funzionamento. Quando si avvia la modalità host ci si presenta davanti una shell di sviluppo da cui si possono ricavare interessanti ed importanti informazioni. Figura 4.1 In primo luogo è presente una dicitura che ci conferma la partenza della sessione HTTP con la relativa porta: viene lanciata ogni volta una 37 Capitolo 4 – Google Web Toolkit nuova istanza di Tomcat per permettere il corretto funzionamento delle parti lato server. È anche possibile visualizzare messaggi personalizzati, magari generati ad un preciso evento, effettuando una chiamata a GWT.log(). Tale strategia può essere utile per segnalare le azioni compiute dall’utente in fase di debug dell’applicazione. Anche la gestione delle eccezioni è visualizzata all’interno di questa shell: le eccezioni, sia catturate che non catturate, sono evidenziate in rosso. Una finestra di log inoltre fornirà le informazioni sull’eccezione generata, ovvero il tipo, la classe e la riga di codice in cui è accaduta e lo stack di successione (fig 4.1). È anche possibile utilizzare tale shell come strumento di debug diretto, ovvero facendo stampare a video tutti i vari passi che vengono compiuti in modalità host. Noi, nel nostro sviluppo, ci siamo comunque affidati al debug “classico” offerto da Eclipse, anche se l’uno non esclude l’altro. Il punto da cui viene lanciato il browser necessario per l’hosted mode è proprio la shell sopra citata. Il browser lanciato permette il corretto funzionamento dell’applicazione e offre anche gli stessi comandi di un comune web browser. Inoltre è anche presente, proprio tra questi comandi, la possibilità di effettuare la compilazione del codice Java lato client in codice JavaScript e avviare il browser predefinito. 38 Capitolo 4 – Google Web Toolkit Web Mode Nella Web mode l’applicazione è costituita da codice Html e JavaScript veri e propri: il compilatore produce tale codice che poi viene caricato sul browser preferito. Questa sarà la vera e propria applicazione che andrà ad essere inserita nel nostro web server, cioè in Tomcat. Per passare dalla modalità host alla modalità web basta utilizzare uno dei comandi principali del web browser di GWT (qui cerchiato in rosso, fig. 4.2). Figura 4.2 Quando si preme il pulsante la modalità host verrà interrotta e il compilatore tradurrà il codice Java, le librerie GWT e le eventuali librerie di terze parti, in codice JavaScript. L’utilizzo di questa modalità è necessario per vedere come si comporta la nostra applicazione con le varie tipologie di browser, senza dover effettuare realmente il deploy module su un web server. Infatti soprattutto con la presenza di fogli di stile è necessario testare il loro funzionamento sui differenti browser. Lo 39 Capitolo 4 – Google Web Toolkit stesso discorso vale per l’uso di librerie JavaScript di terze parti, che non garantiscono lo stesso funzionamento su tutti i browser. Inoltre l’utilizzo di questa modalità è anche un ottimo test per valutare le prestazioni dell’applicazione: nella modalità host infatti il tutto risulta essere ovviamente più lento perché non compilato in JavaScript. Perciò soprattutto in presenza di molti widget e componenti grafiche sarebbe opportuno utilizzare tale modalità prima della definitiva pubblicazione. 4.2 Architettura Un progetto GWT è costituito da tre package: il package principale che è quello che conterrà il vero e proprio progetto, una directory /www in cui il compilatore inserisce i file generati e la directory /tomcat creata per il tomcat interno dell’hosted mode. L’architettura vera e propria del progetto quindi si distingue in tre directory principali contenute nella root del progetto: /public, /client e /server. Oltre a queste tre directory è presente il file di configurazione xml contenenti gli alias dei servlets, necessari per la mappatura su Tomcat. Qui è definita anche la classe che implementa l’EntryPoint. Un altro file xml è generato automaticamente quando si effettua il deployment su Tomcat: è utilizzato come modulo di base comune. La struttura di questi tipi di file verrà presentata nei capitoli successivi quando si introdurrà il vero e proprio codice. Nel package public sono inserite tutte le risorse statiche accessibili dall’esterno. È qui presente la nostra pagina HTML su cui si svilupperà il progetto: questa è anche la pagina HTML più importante. In tale 40 Capitolo 4 – Google Web Toolkit pagina devo anche essere inseriti i vari tag necessari per il funzionamento corretto delle librerie esterne, come GwtExt. Inoltre in questo package è anche possibile inserire eventuali pagine per collegamenti interni al sito o anche delle immagini da visualizzare. Nel package client sono presenti tutte quelle classi che fungono da visualizzazione e da interfaccia grafica. Tutte queste classi sono dei composite, eccetto una, quella che è l’implementazione di Entry Point e che come detto farà solamente da contenitore del RootPanel per lo scambio dei Widget da visualizzare. In queste classi sono presenti le eventuali chiamate ai servlet, le RPC, che effettuano la chiamata alle classi presenti nel package server. Nel package server invece sono presenti i servlet necessari a risolvere la dinamicità del portale. Tutte le interrogazioni a database sono effettuate da queste classi, anche se non direttamente, ma previa l’attivazione della procedura remota lato client. Anche la raccolta di dati da file xlm avviene tramite queste classi, nella stessa maniera che per un database. 4.3 Lato client e lato server GWT si differenzia molto nella stesura delle pagine, soprattutto per quanto riguarda il lato client ed lato server. La concezione di azioni effettuate lato client e lato server si discostano dalle altre tecnologie conosciute come ad esempio il PHP. Per poter quindi effettuare accessi a database o reperire dati, bisogna effettuare delle chiamate remote, dette RPC. Introduciamo gli elementi necessari per comprendere come sia costituita la nostra applicazione, prima lato client e poi lato server. 41 Capitolo 4 – Google Web Toolkit I Widget Le interfacce grafiche che vengono create con GWT sono molto simili a quelle create dalle librerie Swing di Java. Nel JavaScript tradizionale, la creazione di queste interfacce è fatta con la manipolazione del DOM: utilizzando GWT questo avviene tramite delle classi DOM, le quali vi accedono direttamente. La creazione delle pagine lato client è ovviamente effettuata tramite i widget, che non sono nient’altro che dei componenti grafici di alto livello contenuti in dei particolari pannelli. Sono presenti i classici componenti HTML, come i bottoni, i pulsanti di scelta multipla e scelta esclusiva, le caselle di testo, e così via. In più vengono messi a disposizione i widget particolari del GWT: particolari tipi di pulsanti, come il Toggle Button, una serie di strutture atte alla creazione di menu a tendina, le classiche strutture ad albero, passando anche per la struttura a tabs. Questi widget definiscono quindi il modo in cui l’utente esegue azioni di input e di output, senza trascurare gli importanti effetti grafici ottenuti. I pannelli sono i contenitori di questi widget, nonché di altri pannelli. Sono utilizzati per definire la struttura dell’interfaccia grafica all’interno del browser. Vi sono numerose tipologie di pannelli: strutturato a zone (nord, sud, ovest, est), per fasce orizzontali o verticali, oppure con la classica struttura a tab (contenitore a sua volta di altri pannelli). Un particolare pannello è il RootPanel. Questo pannello è in cima alla gerarchia dei pannelli ed è quindi quello che li conterrà tutti: rappresenta infatti il <body> del documento HTML. Questo particolare 42 Capitolo 4 – Google Web Toolkit pannello è il luogo in cui vengono scambiate le varie “pagine” del nostro portale, effettuando un’azione di rimozione e di aggiunta ogni volta. La gestione degli eventi in GWT è simile a quella delle Swing, ovvero viene utilizzato il concetto di “listener”. Questo listener non è nient’altro che un’interfaccia che definisce uno o più metodi che il widget chiama quando si verifica l’evento. Ogni widget ha una sua lista personalizzata di eventi chiamabili, cioè che si possono verificare. Un classico esempio è il click del bottone. Il listener associato al bottone è il ClickListener, il quale al suo interno avrà un metodo onClick(): esso si attiverà al click sul bottone e se giustamente implementato eseguirà l’azione voluta dall’utente. Ovviamente tale evento necessita di parametri per essere eseguito correttamente, perciò si deve fornire il widget stesso come parametro. Questo e molti altri esempi saranno presenti nella sezione relativa alla stesura del codice, in quanto, ogni azione possibile dell’utente è gestita tramite i listener. I widget di GWT, essendo in fondo dei componenti HTML, possono anche essere integrati con i classici fogli di stile (CSS). Ogni componente e quindi classe, di default, ha una sua regola di stile così chiamata: gwt-<nomeclasse>. Per esempio il widget Button, che rappresenta il bottone, avrà gwt-Button. Le regole utilizzabili per manipolare i widget sono quelle tradizionali utilizzate per i componenti HTML. Si possono assegnare stili anche utilizzando alcuni metodi disponibili per la classe widget di interesse, come setStyleName(). Una cosa importante da tenere presente è che le regole di stile o il foglio di stile esterno devono essere collocati nella parte statica del progetto, ovvero nella directory /public, ed eventualmente inseriti tramite 43 Capitolo 4 – Google Web Toolkit l’opportuno codice nella pagina principale (tag <link>) in caso di CSS esterno. L’assemblaggio di questi widget in pannelli permette la creazione di una nuova struttura, detta composite. Il composite non rappresenta nient’altro come verranno mostrati i widget: ogni composite, è quindi, per così dire, una “pagina” del nostro portale, anche se questo non è propriamente vero. Infatti un composite può anche essere la base di più pagine, in quanto, essendo il sito dinamico, esso può cambiare aspetto a seconda delle informazioni richieste e ricevute in quel momento. Inoltre, come più volte detto, il concetto di pagina web in realtà viene un po’ meno, perché la pagina in cui si sviluppa tutto il portale è sempre la stessa, l’unica cosa che cambiano sono i composite caricati sul RootPanel. Per scrivere un modulo di un’applicazione in GWT, inizialmente bisogna creare una sottoclasse di EntryPoint, una classe particolare che rappresenta anche l’unica pagina del nostro portale. Questa sottoclasse sarà quella che avvierà il primo composite e che apparirà come schermata iniziale del portale (la finestra di login). Un’importante caratteristica di queste classi, è che in esse deve essere solo inserito codice traducibile dal compilatore GWT, altrimenti lo stesso compilatore ci fornisce errore: il codice contenuto in questi composite, o comunque all’interno del costruttore degli stessi, deve essere GWT compatibile, cioè contenti nelle apposite librerie. Tutto questo è dovuto al fatto che il codice verrà tradotto il JavaScript e quindi, come logicamente ci si può immaginare, non tutto il codice Java si presta bene a questa traduzione. Si possono comunque creare dei 44 Capitolo 4 – Google Web Toolkit metodi che contengono codice Java “classico”, senza incorrere in nessun problema. Le Remote procedure Call (RPC) Ogni volta che il browser ha bisogno di interagire con il server, o per memorizzare dati o per raccoglierne, viene attivato il codice lato server tramite una procedura remota, detta Remote Procedure Call (RPC). Essa non è nient’altro che una richiesta effettuata tramite HTTP. GWT effettua questo meccanismo di RPC tramite l’uso dei Java Servlet, i quali accedono alle risorse del server. Le classi presenti dal lato server infatti sono classi Java che non verranno tradotte dal compilatore GWT ed è proprio con queste classi che viene effettuata la connessione al database MySQL e anche tutte le interrogazioni per reperire i dati. Passiamo ora ad analizzare più vicino questo meccanismo di interazione client-server. Una fondamentale differenza tra le applicazioni AJAX e le tradizionali applicazioni HTML è che le applicazioni AJAX non necessitano di caricare una nuova pagina per il loro funzionamento. Comunque anche le applicazioni AJAX hanno la possibilità di raccogliere dati dal server, proprio tramite le sopracitate RPC. Il meccanismo delle RPC è molto semplice e permette di passare oggetti Java avanti e indietro tramite il protocollo HTTP. Tale meccanismo può sembrare a prima vista complicato, ma ci si rende conto che porta a notevoli vantaggi, quali la riduzione della banda utilizzata ed in generale l’aumento di tutte le prestazioni. Il codice lato server che viene invocato dal lato client è detto anche servizio. 45 Capitolo 4 – Google Web Toolkit Ora passiamo alla struttura vera e propria della nostra procedura remota, enunciando le principali classi ed interfacce presenti. Alcune di queste classi ed interfacce sono fornite dal GWT, altre devono essere create dall’utente e altre ancora generate automaticamente, come la classe service proxy. Ecco qui di seguito un diagramma che illustra quanto detto. Figura 4.3 Per creare un servizio sono quindi necessari i seguenti passi: 1. Definire un’interfaccia del tuo servizio che estenda l’interfaccia RemoteService, nella quale vi siano presenti tutti i metodi necessari per la RPC. 2. Definire una classe che contenga tutto il codice lato server necessario, che estenda la classe RemoteServiceServlet e che implementi l’interfaccia creata al passo precedente. Questa classe sarà il nostro servlet vero e proprio. 3. Definire un’interfaccia asincrona che verrà chiamata dal codice lato client e permetterà il corretto funzionamento. 46 Capitolo 4 – Google Web Toolkit Introduciamo ora il concetto di interfaccia sincrona e asincorna. La prima interfaccia creata è un’interfaccia sincrona e viene inserita nel codice lato client. Tale interfaccia rappresenta la definizione del servizio che si sta creando ed il servizio lato server dovrà implementare questa interfaccia. Per funzionare, però, oltre all’interfaccia precedente è necessario anche di un’interfaccia asincrona, perché non è infatti possibile chiamare la RPC direttamente dal lato client. Questa interfaccia è basata sull’interfaccia precedente e richiede che oltre ai precedenti parametri di ogni metodo, sia presente un oggetto AsyncCallback, il quale conterrà le informazioni desiderate provenienti dal server. I metodi di questa interfaccia sono chiaramente di tipo void in quanto non necessitano niente come dato di ritorno. Una cosa importante da ricordare è la collocazione ed i nomi delle interfacce sopra citate. Entrambe devono stare nello stesso package, ovviamente nel lato client. Per quanto riguarda la denominazione, devono avere lo stesso nome, con il suffisso Async per l’interfaccia asincrona. Inoltre ogni metodo presente nell’interfaccia sincrona deve essere presente nell’interfaccia asincrona e come lista dei parametri deve avere la stessa con l’aggiunta, per ultimo, del parametro di tipo AsyncCallback. Vediamo ora come è costituita la classe lato server. Tale classe è basata sull’architettura dei servlet Java, deve implementare la RemoteServiceServlet e l’interfaccia sincrona. Attenzione a non implementare l’interfaccia asincrona. L’unica differenza con un normale servlet sta nel fatto che invece di estendere HttpServlet, essa estende RemoteServiceServlet. RemoteServiceServlet 47 si occupa della Capitolo 4 – Google Web Toolkit serializzazione e deserializzazione dei dati tra il client ed il server: è proprio questa classe ad estendere HttpServlet. Ora un piccolo accenno va fatto riguardo all’uso di file xml per la configurazione e mappatura dei servlet. All’interno del modulo xml viene utilizzato il tag <servlet> per identificare e mappare tali servlet. Vengono aggiunti due importanti parametri: il parametro path e il parametro class. Il parametro path serve per assegnare lo specifico URL che il servlet avrà quando sarà instanziato. Il parametro class invece serve proprio ad identificare quale sarà la classe riferita a quel servlet, cioè in quale package il servlet sia presente. Passiamo ora ad illustrare il meccanismo vero e proprio con cui tale RPC viene eseguita. Il processo con cui il nostro servizio è invocato è sempre lo stesso. 1. Istanziare il servizio usando il metodo GWT.create(). 2. Viene creato un oggetto AsyncCallback che conterrà l’informazione del completamento della RPC. 3. Viene effettuata la chiamata. L’oggetto AsyncCallback, creato al punto 2, sarà il fulcro delle operazioni di ricezione dei dati richiesti tramite la RPC. Infatti tale oggetto conterrà due metodi: il medoto onSuccess() e il metodo onFailure(). Il primo metodo, come si deduce dal nome, è invocato quando la chiamata remota ha esito positivo, cioè il collegamento con il servlet è riuscito. L’altro metodo invece viene invocato quando la connessione con il servlet fallisce. Le possibilità che tale connessione fallisca sono dovute ad un’errata stesura del file di mappatura dei servlet, o alla mancanza, per qualunque motivo, del file sevlet stesso. Attenzione a non confondere 48 questo fatto con la riuscita Capitolo 4 – Google Web Toolkit dell’interrogazione a database: infatti se dovesse avvenire un’eccezione lato server o comunque avvenire qualche errore sempre nel servlet, viene eseguito il metodo onSuccess(). Questi due metodi forniscono all’utente anche la possibilità di utilizzare dei parametri. Per il metodo onSuccess() è presente il parametro result di tipo Object, che rappresenta il risultato della RPC. Questo è ovviamente di tipo Object perché l’utente può far tornare qualunque tipo serializzato dalla RPC: il cast quindi è sempre safe per i tipi serializzati. Il parametro disponibile per il metodo onFailure() è invece caught di tipo Throwable, cioè è possibile utilizzare tale parametro per prelevare informazioni riguardo l’eccezione o l’errore creatosi. I dati che vengono passati tra client e server non sono dati qualunque. GWT, infatti, supporta il concetto di serializzazione. Tale concetto permette l’estrapolazione del contenuto di dati da una parte di codice in esecuzione, la quale può essere o trasmessa ad un’altra applicazione memorizzata e utilizzata successivamente. Quindi sia i parametri che i dati di ritorno dei metodi utilizzati nelle RPC devono essere serializzabili, rispettare cioè particolari restrizioni. Non bisogna confondere i concetti di serializzazione del GWT con quelli dell’interfaccia Java Serializable, in quanto sono simili ma non uguali. Un tipo è serializzabile secondo il GWT se rispetta uno dei seguenti punti: • Il tipo di dato è una primitiva, come char, byte, int, long, boolean, float o double. 49 Capitolo 4 – Google Web Toolkit • Il tipo di dato è un’istanza di String o di Date, o un tipo wrapper come Character, Byte, Short, Integer, Long, Boolean, Float o Double. • Il tipo è un’enumerazione, tramite la parola chiave enum. • Il tipo è un array di tipi serializzabili. • Il tipo è definito dall’utente come serializzabile, tramite Serializable o IsSerializable. • Il tipo ha almeno una sottoclasse serializzabile. In conclusione si può dire che l’utilizzo delle chiamate server, non è poi così complicato, anche se magari si discosta un po’ dalle altre tecnologie come PHP o ASP. Il tutto comunque punta ad aumentare le prestazioni in quanto con le applicazioni AJAX si dovrebbe evitare di ricaricare tutta la pagina e quindi velocizzare il tutto. Confronti con le altre tecnologie sono lasciati al capitolo riguardante le conclusioni. 50 CAPITOLO 5 - SOLUZIONI TECNICHE CARTELLA CLINICA Vengono presentate ora le soluzioni tecnologiche utilizzate per la realizzazione della cartella clinica, seguendo l’ordine di presentazione del capitolo 3. Una considerazione che vorrei fare sugli argomenti trattati successivamente è che verrà utilizzato impropriamente il termine pagina nei paragrafi successivi. Questo termine non è rappresenta la realtà se si considera che con la tecnologia GWT la pagina è sempre la stessa, ma non perde neanche di significato se tale termine è utilizzato per esprimere le varie “viste” disponibili di fronte all’utente: con il termine pagina verrà quindi identificato ogni Composite visualizzabile dall’utente e sarà tratto come una pagina a sé. 5.1 Comʼè fatta La cartella clinica è stata concepita cercando di renderla più semplice possibile da utilizzare. A questo proposito abbiamo deciso di basarci su modelli già conosciuti, così da non introdurre ulteriori difficoltà di comprensione. Come base è stata scelta la modalità con la quale il sistema operativo MS Windows propone la risoluzione nell’esplorazione delle cartelle o dei file. Infatti ci è sembrata la soluzione più ovvia dato che, come bene noto, a oggigiorno, i sistemi operativi Microsoft sono i più diffusi, e quindi anche quelli che, di conseguenza, la gente è abituata ad utilizzare. 51 Capitolo 5 – Soluzioni Tecniche Cartella Clinica La struttura è formata, quindi, da due aree differenti: una laterale sinistra ed una maggiore sulla destra che ricopre il resto della cartella. La parte laterale sinistra è formata con una struttura ad albero. Con questa soluzione sono possibili due agevolazioni importanti: la prima riguarda il fatto che l’utente ha sempre sotto controllo la posizione in cui sono i dati, la seconda e più importante è che in questo modo, nel lato sinistro della cartella clinica si ha sempre una panoramica generale dei dati clinici del paziente. Ovviamente la struttura ad albero è generica per tutti i pazienti, ma a seconda di quali informazioni cliniche vengono inserite, tale struttura si modifica dinamicamente: ogni nodo sarà un dato clinico sensibile del paziente ed è con esso che l’utente interagisce. Per rendere il tutto più coinvolgente abbiamo anche utilizzato delle icone che per noi rappresentavano al meglio ogni ramo dell’albero: un esempio può essere visto in figura 5.1. Figura 5.1 La restante parte della cartella clinica ovviamente è dove vengono visualizzati le informazioni non più sotto forma di elenco, ma nel loro dettaglio. La struttura utilizzata è una struttura a tab, i quali permettono 52 Capitolo 5 – Soluzioni Tecniche Cartella Clinica di avere una più rapida manipolazione dei dati, una memoria delle schede aperte in precedenza. Questa soluzione, utilizzata anche da molti browser, rende la cartella clinica più simile alla realtà, infatti permette di avere delle schede per ogni dato clinico del paziente, come le schede di un raccoglitore che potrebbe utilizzare nella realtà il medico. L’organizzazione in tab permette di sfruttare tutte le comodità che da questa derivano, come la possibilità di avere più schede aperte contemporaneamente (anche se visualizzate una per volta). In questa area le soluzioni di visualizzazione sono differenti per ogni sottosezione della cartella clinica e verranno dettagliatamente affrontate. L’accesso alle singole schede è effettuato in maniera semplice grazie alla selezione di un nodo nell’albero laterale: al click su uno di questi, viene aperto un tab che visualizza le informazioni ricercate. Abbiamo utilizzato anche una semplice colorazione per rendere il tutto più user-friendly, ma non solo. Il colore di contorno della cartella clinica, come quello della barra del titolo, sono differenziati per ogni tipologia di utente: il requester avrà un colore ed il dottore un altro. Oltre a queste due aree sono disponibile alcuni strumenti per interagire con i dati, utili soprattutto per il requester. Sono infatti disponibili dei menu a tendina che permetto, dove possibile ed implementato, di modificare, inserire o cancellare i dati corrispondenti alla pagina visualizzata. Oltre a questi strumenti sono presenti i classici pulsanti di chiusura, di minimizzazione o massimizzazione della cartella clinica, ed anche un pulsante di help con la guida in linea sul suo utilizzo. Questa guida è stata necessaria perché il software creato può essere utilizzato da chiunque e quindi la sua diffusione deve essere accompagnata da una guida semplice ed esaustiva. 53 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Tutto questo è stato effettuato utilizzando la tecnologia del GWT. Qui di seguito verranno analizzate le singole pagine, così da permettere la comprensione e l’utilizzo di questa tecnologia: si passerà ad analizzare ogni pagina partendo dalla struttura base, cioè dai pannelli, passando per i dettagli grafici, come i vari widgets, concludendo con le RPC presenti. Bisogna ricordare inoltre che ogni cartella clinica è personalizzata per ogni paziente e non fornisce alcuna informazione sugli altri presenti nel database. Il funzionamento con cui ogni cartella clinica è generata è molto semplice. Infatti nella sezione requester o dottore compare un elenco dei vari pazienti disponibili da quell’utente, il quale selezionando lo stesso, avvia la procedura di apertura e visualizzazione della cartella clinica. La cartella che qui si apre è quindi in realtà un pop-up, così da poter mostrare, in sfondo, l’elenco di partenza. Con questo metodo si è voluto far avere all’utente una memoria delle azioni da lui stesso compiute: anche se il pop-up, per chi è abituato all’utilizzo di internet, può sembrare poco risolutivo, nella comprensione di un nuovo software invece resta una soluzione valida, perché non “nasconde” l’azione precedente. Le finestre utilizzate per questo pop-up, oltretutto, sono graficamente efficienti, con trasparenze molto efficaci, che rendono il prodotto molto evoluto ed innovativo. Quest’utilizzo di pop-up quindi ha reso inutile la creazione del meccanismo di cronologia citat nel capitolo di presentazione del GWT, in quanto l’utente ha sempre di fronte la pagina precedentemente utilizzata. 54 Capitolo 5 – Soluzioni Tecniche Cartella Clinica 5.2 ClinicalFolder Come primo argomento verrà affrontata proprio la struttura ed il codice che compone la cartella clinica. Innanzitutto bisogna ben spiegare cosa s’intende effettuare con questa classe. La classe cartella clinica funziona da contenitore e da visualizzatore delle varie sezioni. In questa classe sono presenti innumerevoli meccanismi necessari a far funzionare le altre classi e soprattutto a far interagire queste fra di loro. Oltre a funzionare da tramite tra le classi delle varie sezioni, è qui presente la costruzione dell’albero laterale. Anche la parte puramente grafica della struttura della cartella clinica è curata in questa classe. Non sono invece presenti i contenuti degli eventuali tab aperti in fase di visualizzazione o le eventuali finestre di pop-up: queste sono separate e vengono trattate nelle sezioni successive di questo capitolo. L’attivazione della cartella clinica, avviene quando un dottore o un requester, tramite la propria interfaccia grafica, seleziona un paziente nonché un problema clinico. Nel primo caso come tab iniziale viene visualizzato il profilo del paziente, mentre nel secondo l’attenzione è puntata sul problema scelto. La pagina viene aperta su di una finestra, ovvero grazie al widget Window delle librerie Gwt-Ext. In questa finestra vengono aggiunte le varie strutture grafiche, quali l’albero ed il TabPanel che conterrà i tab. Molte ed importanti sono le proprietà che questa finestra possiede: dalla posizione dei pannelli tramite a degli appositi Layout, passando per la gestione delle icone di massimizzazione e minimizzazione, nonché dell’help in linea. Analizziamo ora l’albero laterale presente in questa finestra. Alla base vi è presente un pannello particolare, il TreePanel. Questo pannello 55 Capitolo 5 – Soluzioni Tecniche Cartella Clinica è necessario per l’inserimento della struttura ad albero, in quanto permette di interagire con diverse proprietà. Oltre alle proprietà generali, è qui che bisogna impostare il nodo radice dell’albero. La costituzione dell’albero infatti è su scala gerarchica, ovvero permette un solo nodo principale e successivamente vi sono tutti i nodi figli, che a loro volta ne possono contenere altri. Nel nostro caso particolare abbiamo dovuto dividere l’albero in due parti: una parte statica ed una asincrona (dinamica). La parte statica rappresenta tutti quei nodi che non modificano la loro struttura, ovvero lasciano inalterata la parte grafica ma anche le varie proprietà. La parte asincrona invece è necessaria per tutti quei nodi che invece possono variare in struttura, quindi magari variando il proprio titolo di visualizzazione nonché alcune proprietà. È quindi stata utilizzata per l’anamnesi e tutte le varie sottosezioni. Figura 5.2 La parte sincrona è creata essenzialmente tutta allo stesso modo. Vengono come prima cosa impostate le proprietà necessarie, come l’identificativo nell’albero. Successivamente viene impostato un evento che descrive l’azione da compiere quando viene selezionato il nodo in questione: si dovrà aprire il tab rispettivo nel pannello dei tab, 56 Capitolo 5 – Soluzioni Tecniche Cartella Clinica controllando ovviamente che tale già non vi sia presente, altrimenti portarlo in primo piano. Avviene quindi in tale metodo la costruzione del tab con la scelta dell’effettiva vista da mostrare e l’aggiunta della toolbar dove necessaria. Il modo di avvalersi dell’evento è sfruttato ugualmente anche per la parte asincrona, anche se in modo leggermente differente. Si analizza ora la parte asincrona. È necessario come prima cosa impostare correttamente l’oggetto che carica i dati che formeranno tale albero: l’XMLTreeLoader. Come si intuisce dal nome, questo loader preleva i dati in formato XML e li analizza in base ai tag, costruendo quindi vari nodi degli alberi. Il codice XML è costruito su una classe a parte, che non fa altro che effettuare una serie di query alle tabelle contenente i dati dell’anamnesi. Il risultato di ogni query viene utilizzato per costruire una stringa: tale stringa contenente codice XML, viene inviata tramite il metodo doPost() delle HttpServlet al richiedente, cioè all’XMLTreeLoader. Questa stringa viene generata ogni volta che si invoca il loader, ovvero quando si richiede di rigenerare l’albero. Il nodo dell’albero asincrono avrà un costruttore differente rispetto a quello precedente: viene generato tramite l’oggetto AsyncTreeNode che metterà a disposizione il metodo reload() fondamentale per l’aggiornamento. Il meccanismo che genera la creazione del tab alla selezione del nodo asincrono, è simile ma non uguale a quello prima descritto. Infatti la differenzia sostanziale, sta nel fatto che non si sa a priori che tab aprire. Si è quindi risolta questa situazione impostando, già in fase di generazione del codice XML per il loader, di dotare ogni nodo di un proprio identificatore dinamico, ovvero prelevato da database. In base alla corrispondenza con questo id viene aperto il tag rispettivo. 57 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Tramite una serie di metodi richiamati si evidenziano due possibili scenari. Il primo coinvolge la creazione del tab necessario alla visualizzazione delle pagine di riepilogo, ovvero i nodi asincroni di primo livello appena sotto anamnesi. Qui l’id non è nient’altro che il nome della tabella da cui vengono prelevati. L’altro scenario invece si presta quando è necessaria la creazione di “foglie”, ovvero di nodi di secondo livello, sottostanti ai precedenti. In questi nodi l’id invece è dato da una sottostringa dell’id del genitore (una sottostringa dell’id prima descritto) con in più il valore prelevato database. Quest’ultimo valore non è nient’altro che l’id del record di dati nella tabella corrispondente. Infatti tale valore viene anche settato nella sessione corrente, così da permettere una più semplice esecuzione delle query di recupero informazioni necessarie per le altre classi. Si vuole avere quindi sempre la traccia univoca di quale record attuale si sta trattando. Tutta questa procedura è effettuata tramite una RPC, che nel servlet non fa nient’altro che impostare il valore in sessione. Al buon fine della chiamata, il controllo ritorna al client che tramite l’apposito metodo seleziona ed aggiunge il tab corretto al TabPanel. Viene presa in esame quindi la gestione del TabPanel. Quest’ultimo, come dice lo stesso nome conterrà i vari tab atti alla visualizzazione dei dati richiesti. L’aggiunta di un tab è molto semplice. Tra le varie proprietà importanti da citare è che ogni tab è riconosciuto all’interno del pannello tramite un id. Sono inoltre presenti due tipi di tab, quelli attivi cioè in primo piano e quelli inattivi, prelevabili tramite un opportuno metodo del TabPanel. Queste proprietà saranno molto importanti quando avverrà l’aggiornamento dei tabs. 58 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Per quanto riguarda i tab iniziali, essi vengono scelti in base a quale schermata sorgente attivi la cartella clinica. Questa scelta viene operata tramite l’interazione di alcuni metodi contenuti in questa classe e nella classe sorgente, ovvero tramite il passaggio di determinati parametri. Un’altra importante procedura che viene affrontata in questa pagina, è quella del prelevamento di alcune fondamentali informazioni per migliorarne l’aspetto grafico e funzionale. Queste coinvolgono soprattutto la selezione del composite da visualizzare in alcuni tab e la creazione della toolbar. Il problema si pone solamente per le sezioni riguardante i dati dei genitori e quella relativa ai problemi fisiologici. Questo avviene perché tali sezioni sono descritte da una sola pagina e non permettono ulteriori dettagli, quindi non hanno nodi figli al loro seguito. È necessario che si abbia la conoscenza se tali dati sono stati inseriti oppure no al fine di modificare la struttura grafica. Infatti nel caso di assenza di dati, verrà visualizzato un messaggio che notifica tale situazione all’utente. Viceversa, in caso di presenza, viene attivata la classe necessaria per la visualizzazione dei dati. La stessa procedura per la toolbar: quando non vi sono informazioni ovviamente verrà mostrato il pulsante di inserimento, altrimenti quello di modifica e cancellazione. Come detto questo non avviene per le altre sezioni perché esiste sempre una pagina riassuntiva della sezione ed il messaggio da mostrare è gestito in tale pagina. Lo stesso per la toolbar, in quanto il pulsante che permette il nuovo inserimento sarà sempre presente nella pagina appena citata. Naturalmente il recupero di tali informazioni è effettuato tramite una RPC, la quale non fa altro che verificare se sono appunto presenti righe con il record di informazioni richieste e quindi costruire la pagina di conseguenza. 59 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Una parola in più va spesa riguardo alla toolbar. Questa struttura è stata progettata per contenere le varie opzioni disponibili. La scelta è caduta su questo widget in quanto ci permette di avere la possibilità, in futuro, di ampliare con quante opzioni si desideri il nostro portale. La toolbar infatti è creata mediante l’aggiunta interna di ulteriori widget, chiamati Item. Questi oggetti possono effettuare qualunque tipo di operazione associata al proprio listener: nel nostro caso alla selezione di esso vengono avviate sia procedure interne a questa classe, sia aperte delle nuove classi. Inoltre ogni menu che si viene a creare non interferisce con la creazione o la struttura del tab. Proprio da qui nasce la possibilità tendenzialmente infinita di incrementare tali opzioni. Passiamo ora ad analizzare le altre procedure presenti in questa classe. Una delle procedure più interessanti e che viene richiamata dalle altre pagine aperte dalla toolbar è quella riguardante l’aggiornamento dei tab. L’operazione svolta è molto semplice quanto essenziale per avere una vista sempre aggiornata. Vengono passati come parametri due tab, il primo da togliere e l’altro da aggiungere. In realtà tali tab sono gli stessi, a parte le modifiche aggiornate sul secondo. Come prima cosa viene impostata quella proprietà di cui deve godere il secondo tab per essere riconosciuto all’interno della cartella clinica, ovvero l’id. Successivamente non si fa altro che rimuovere il vecchio tab, aggiungere il nuovo e renderlo attivo (selezionato). Considerando la velocità di tali operazioni, l’effetto ottenuto è quello di aver aggiornato il primo tab, non di averne creato uno identico sostituto a questo. Una serie di importanti procedure sono quelle riguardanti la creazione, modifica e cancellazione di un dato clinico. Se la creazione e la modifica non fanno altro che avviare in differenti modalità la classe specifica della loro sezione, la cancellazione avviene interamente in 60 Capitolo 5 – Soluzioni Tecniche Cartella Clinica questa classe. La procedura remota viene effettuata al click sul pulsante di cancellazione. Nel metodo del servlet non viene fatto altro che eseguire una query di cancellazione, differente a seconda di che dato si stia considerando. Infatti tramite l’identificatore del nodo di ogni ramo e quindi di ogni dato clinico, viene dapprima riconosciuta e selezionata la giusta tabella del database sulla quale verrà effettuata la query. Successivamente si preleva dalla sessione l’id del paziente in esame e si avvia la query. Ritornato il controllo nel lato client, non si deve far altro che rimuovere il tab sottostante e aggiornare l’albero (per aggiornamento si intende la sola parte asincrona). In questa classe sono presenti anche le operazioni di controllo sul decidere quale classe deve essere aperta della sezione relativa ai problemi clinici: viene infatti controllato se l’utente loggato è un requester o un dottore. Tale controllo serve ovviamente anche nella decisione di visualizzare la toolbar, in quanto questa non è visualizzata se si è un dottore. La creazione dell’help in linea è stato qui implementato, ma solo parzialmente. Infatti viene solamente creata la maschera iniziale dell’help (fig. 5.3), non le successive pagine, che sono invece pagine HTML pure. La maschera non è nient’altro che una pagina HTML, che tramite dei tag ancora <a> richiamerà le varie pagine più dettagliate dell’help. Anche tale struttura è soggetta a controlli di tipo condizionale in base all’utente loggato e quindi verranno visualizzati alcuni link piuttosto che altri. 61 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Figura 5.3 Nel complesso la creazione di questa classe ha creato diversi problemi, in quanto interagisce con altre classi, non tutte uguali. Per cercare di progettare una struttura solida ci siamo avvalsi dell’aiuto delle sessioni per il passaggio degli identificatori principali. Dal punto di vista del codice, sono stati creati il più possibile dei metodi autonomi così da suddividere l’informazione in ognuno di essi: la modifica parziale ma anche totale di una procedura è così più semplice ed immediata perché coinvolge solo quel metodo. Un elenco completo di tutti questi metodi può essere osservato dal codice sorgente presente nell’appendice finale di questa tesi. 5.3 Patient Profile Le pagine che qui andremo a descrivere riguardano i dati non di tipo medico presenti nella cartella clinica: verrà mostrata la struttura delle pagine necessarie a creare il profilo del paziente. La prima pagina o classe di cui parleremo è la PatientProfile. . Lo scopo di questa pagina è quello di visualizzare tutte le informazioni riguardanti il profilo del paziente, come la data di nascita, informazioni 62 Capitolo 5 – Soluzioni Tecniche Cartella Clinica sull’indirizzo, ecc. Ovviamente, come detto, ogni classe e quindi anche questa, estende la superclasse Composite, utili per aggregare i numero widget presenti. La pagina sarà composta da varie etichette, o label, che visualizzeranno i dati richiesti da database. Come pannello di base abbiamo utilizzato un AbsolutePanel, ovvero un pannello che consente di posizionare le varie etichette tramite delle coordinate cartesiane, così da essere precisi negli allineamenti. La tecnica utilizzata per la visualizzazione dei record da database è molto semplice e intuitiva. Si è pensato di utilizzare delle label per indicare quale dato veniva visualizzato e delle label differenti per visualizzare i dati effettivi: queste label all’inizio sono ovviamente vuote, cioè senza niente scritto, ma al caricamento della pagina vengono riempite con i dati provenienti dalla RPC. Le label utilizzate sono ovviamente quelle del Gwt-Ext, in quanto forniscono una grafica migliore e dei metodi aggiuntivi rispetto alle classiche di GWT. La distribuzione di queste label è stata effettuata su due colonne, così da poter essere visualizzato tutto si di una pagina. Alla prima tipologia di label è stato dato uno stile uguale per tutte cioè un carattere grassetto, così da evidenziare meglio ogni campo. Alla seconda tipologia di label, invece, è stato dato uno stile differente, impostando solo la dimensione del carattere, uguale alle precedenti. Per ognuna di queste label, ma soprattutto per quelle atte a visualizzare i dati provenienti da database, è necessario un nome rappresentativo: tramite tale nome la label sarà riconosciuta all’interno del composite. Analizziamo ora il meccanismo della chiamata asincrona con cui reperiremo i dati dal database. Viene come prima cosa istanziato il servizio, poi creato l’oggetto AsyncCallback. Nel metodo onSuccess() verranno eseguite le operazioni di riempimento delle label: i dati provenienti dal servlet sono ovviamente dati serializzati, perciò si è 63 Capitolo 5 – Soluzioni Tecniche Cartella Clinica deciso di utilizzare l’oggetto ArrayList come dato di ritorno, il quale conterrà tutti i risultati della query da database. Viene creata una variabile text di tipo ArrayList effettuando proprio un cast a result. Tramite il metodo setText() delle label, viene impostato il testo da visualizzare: bisogna però tuttavia far notare che i dati proveniente da database potrebbero non essere completi, perciò si è deciso di visualizzare solo i dati non nulli, tramite un’opportuna struttura condizionale. Nel metodo onFailure() invece viene mostrato all’utente un errore, con la motivazione del fallimento della connessione al servlet: più che altro tale metodo serve in fase di diagnostica del sito da parte di noi sviluppatori, in quanto una volta creato e mappatto correttamente il sevlet tale errore non dovrebbe più essere mostrato. Dopo la creazione dell’oggetto AsyncCallback finalmente viene effettuata la chiamata. Il metodo necessario viene evocato tramite l’utilizzo della variabile patient, la quale è del tipo dell’interfaccia asincrona: sarà presente come parametro solo callback, di tipo AsyncCallback, il quale eseguirà appunto i metodi sopra citati. Questo era quanto presente nel codice lato client, ora passiamo a trattare invece il codice lato server. Qui troveremo il metodo view(), che come parametro di ritorno ha, come sopra detto, un ArrayList. Innanzitutto è necessario recuperare i dati dalla sessione per poter avere l’id del paziente, ovvero il riconoscimento univoco nel database. Viene quindi effettuata la connessione al database e impostata la query: vogliono essere visualizzati tutti i dati disponibili per quel determinato paziente. La connessione al database è stata effettuata tramite i driver appositi di MySQL e tramite la creazione di una classe servlet 64 Capitolo 5 – Soluzioni Tecniche Cartella Clinica particolare, chiamata Database, la quale viene sempre utilizzata per istanziare oggetti nei servlet utilizzati nelle RPC. Come salvataggio temporaneo di tali dati vengono utilizzate delle variabili di tipo String, una per ogni dato, cioè colonna del database. Si è scelto di utilizzare variabili di tipo String per il semplice fatto che sono semplici nella modellizzazione: quindi anche per dati come le date di nascita, invece di utilizzare il tipo Date, si è scelto di operare sempre con il tipo String. Nel blocco try viene effettuata la query e il prelevamento e memorizzazione dei dati nelle variabili. Questa memorizzazione viene effettuata previa opportuna condizione che il paziente esista, altrimenti tutti i metodi di prelevamento dei dati genereranno eccezione. Le eccezioni generate, vengono gestite dal blocco catch. Questo non vuol dire che il metodo lato server che verrà evocato sarà onFailure() perché come sopra detto, il problema non è stato nella connessione al servlet. Continuando nel blocco try, si ha la costruzione dell’ArrayList, al quale vengono aggiunte tutte le variabili, ora contenenti i dati voluti. Molto importante è l’ordine con cui tali dati vengono memorizzati in questo ArrayList, in quanto, nello stesso ordine devono essere recuperati dal lato cliente nella variabile text, risultata dal cast su result. In conclusione a questa pagina lato server, è presente anche la chiusura del database, ovviamente nella clausola finally. Passiamo ora a descrivere la pagina con cui i dati sopra vengono inseriti ed anche modificati. Per effettuare queste due operazioni abbiamo utilizzato la stessa pagina senza dover rifare graficamente due pagine uguali: le operazioni lato server ovviamente saranno differenti. Tale classe è stata chiamata Patient. Questa pagina è fatta diversamente 65 Capitolo 5 – Soluzioni Tecniche Cartella Clinica dalla precedente. Sono presenti delle label ed in più sono presenti dei campi di inserimento testo, in cui l’utente (requester) inserisce i dati, ma anche dove gli stessi dati vengono visualizzati e pronti per essere modificati. Nel costruttore sono presenti diversi parametri utili: vengono passati la finestra in cui viene aperta questa pagina, la pagina della cartella clinica, un parametro booleano e il pannello di tabs adibito alla visualizzazione della pagina precedente. Molto importante è soprattutto il parametro booleano, che a seconda se vero o falso, permette di attivare le procedure di visualizzazione e modifica o inserimento. Come pannello di base anche qui è stato utilizzato un AbsolutePanel: la struttura grafica è la stessa della pagina precedente, ovvero organizzata su due colonne per avere la pagina con un’altezza più piccola possibile. Le label hanno tutte lo stile delle precedenti, cioè un carattere grassetto. Per le caselle di testo il widget utilizzato è il TextField, mentre per i pulsanti di scelta esclusiva si è utilizzato il RadioButton. Per quanto riguarda l’inserimento delle date, è presente un widget apposito nelle librerie Gwt-Ext, il DateField, il quale tramite un pulsate, permette la visualizzazione di un calendario con la possibilità di scegliere la data che si desidera. Affrontiamo il discorso sulla modalità di inserimento dei dati. Questa pagina viene utilizzata in questa modalità nella sezione dedicata al requester, presentandosi in uno dei tab. Come prima cosa si è dovuto prendere degli accorgimenti di tipo logico. Anche se questi accorgimenti possono sembrare banali, un lavoro ben fatto deve sempre effettuare controlli e prevedere ogni casualità. Alcuni campi ovviamente 66 Capitolo 5 – Soluzioni Tecniche Cartella Clinica non possono essere vuoti e altri ancora devo rispettare alcune regole. Il nome, il cognome e la data di nascita, non possono essere vuoti, perciò tramite un apposito metodo setAllowBlank(false) abbiamo reso questo impossibile. La data di morte deve essere selezionabile, e quindi visualizzabile, solo se è il paziente è morto effettivamente, cioè se è spuntata la voce corrispondente. Oltre a questo la data di morte non deve essere precedente a quella di nascita. In questa modalità non vengono visualizzati dati al caricamento della pagina, quindi si può passare subito a parlare della parte lato server. Una volta inseriti i dati voluti, il requester preme il pulsante di memorizzazione dei dati e avvia la RPC corrispondente. Qui come prima cosa si analizza la giustezza dei dati inseriti, ovvero viene controllato che l’eventuale data di morte sia posteriore alla data di nascita: se questo non avviene, il fatto viene segnalato all’utente con la marcatura in rosso della data di morte. Come al solito ora viene istanziato il servizio e creato l’oggetto AsyncCallback ed effettuata la chiamata lato server. In questa chiamata vengono inseriti i dati digitati dall’utente e passati al servlet tramite i parametri del metodo specifico. Nel servlet viene recuperato dalla sessione l’id dell’utente che sta inserendo il paziente: questo avviene per avere traccia dei pazienti del requester e sapere a chi appartengono. Dopo l’inserimento viene anche recuperato l’id, assegnato automaticamente, del paziente e impostata la sessione per questo paziente. Questo id è necessario in quanto terminata l’azione del servlet, cioè dopo l’effettivo inserimento del paziente, viene aperta automaticamente la cartella clinica. 67 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Passiamo ora alla modalità di modifica dei dati. La pagina è aperta in questa modalità tramite il menù di modifica disponibile nella toolbar del tab di visualizzazione dei dati del paziente all’interno della cartella clinica. In questa modalità la stessa pagina deve compiere due RPC, una per la visualizzazione dei dati da modificare, l’altra per la modifica degli stessi. La prima RPC avrà luogo automaticamente al caricamento della pagina. Questa procedura avviene in modo simile alla pagina di visualizzazione dei dati descritta nella prima parte di questa sezione: i dati vengono memorizzati in un ArrayList come dati uscenti dal servlet e tramite un opportuno metodo, visualizzati nelle caselle di testo, nei campi data ecc. Questo avviene sempre sotto condizione che tali dati non siano nulli. Il servlet non fa nient’altro che prelevare tutti i dati disponibili per il paziente identificato dalla sessione attuale e caricarli nell’ArrayList di output. Anche qui ricordo che l’ordine è essenziale e deve essere rispettato lato cliente e lato server. Per quanto riguarda invece la procedura di modifica, questa avviene alla premuta del pulsante di memorizzazione. Come per l’inserimento, anche qui vengono effettuati controlli relativi alla giustezza delle date prima di proseguire. La particolarità che abbiamo in questa procedura è che nel caso di successo della chiamata RPC vengono eseguite diverse operazioni.Viene innanzitutto aggiornato il tab che visualizzava i dati del profilo del paziente: questo avviene costruendone uno uguale al precedente e sostituendo quest’ultimo al vecchio, settando ovviamente tutti le proprietà giuste quali il titolo del tab, la grandezza ecc. Inoltre viene anche aggiornato il nome del paziente nell’albero laterale della cartella clinica. Al termine di tutto viene anche chiusa la finestra pop-up che si è aperta. Il risultato alla chiusura della finestra è un aggiornamento automatico di tutti i dati visualizzati dal paziente. 68 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Nel servlet la procedura di modifica è molto semplice e viene eseguita effettuando la query di modifica in base all’id attuale del paziente, anche qui prelevato dalla sessione. Gli unici problemi riscontrati i queste pagine sono stati relativi alle date e al loro formato, soprattutto in fase di memorizzazione nel database. Infatti si è dovuto utilizzare un particolare tipo di oggetto, il SimpleDateFormat, il quale formatta la data in modo consono al database MySQL. 5.4 Anamnesis In questa sezione saranno presentate le soluzioni tecnologiche utilizzate per creare le strutture riguardanti l’anamnesi del paziente. Come prima cosa verrà evidenziata la pagina in cui viene effettuato un elenco riassuntivo, cioè quella che viene visualizzata sul tab quando si seleziona un nodo primario dell’anamnesi, ovvero un ramo diretto del nodo “anamnesis” dell’albero. Questa è stata strutturata come una normale pagina html, ovvero senza particolari accorgimenti relativi ai widget: è presente un AbsolutePanel di base con una sola label, dove vengono visualizzate tutte le informazioni, cioè l’elenco dei problemi o delle patologie relative alla sezione dell’anamnesi. La visualizzazione di tali informazioni è effettuata tramite un particolare metodo delle label, il metodo setHtml(), che permette di impostare del codice HTML all’interno della stessa. La nostra idea infatti è quella di visualizzare una sorta di pagina web, con un elenco puntato di tutte i problemi, il tutto tramite i consueti tag HTML. 69 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Dal punto di vista del server, invece, le RPC sono abbastanza complicate. Innanzitutto è stato necessario utilizzare una chiamata remota per reperire dalla sessione corrente il tipo di utente, requester o dottore. Questo è stato fatto per un motivo di semplice contorno, ma pur sempre utile. Infatti è presente un messaggio che invita il requester, e solamente questo, ad interagire con la barra del menù per aggiungere nuovi problemi. Si capisce quindi che, anche se è solo una scritta, tale non deve essere visualizzata per il dottore, per non creare confusione, in quanto la barra citata dalla scritta non risulterebbe disponibile per il dottore. Al successo di questa RPC, il tipo di utente viene memorizzato in una variabile e utilizzato come parametro sulla struttura condizionale relativa alla visualizzazione del messaggio. L’altra RPC presente è più complessa e serve a reperire effettivamente l’elenco riassuntivo dei problemi. Come parametro prende l’id del nodo selezionato, così da riconoscere di che sezione dell’anamnesi si tratta: da questo quindi è possibile eseguire la query esatta sul database e prelevare i dati con il solito ArrayList. Al successo di questa chiamata non viene fatto altro che costruire il messaggio, ovvero personalizzare la scritta, come per esempio, “ci sono 3 tipi di allergie memorizzate”, anziché di problemi familiari; ma anche prelevare i dati dall’ArrayList e creare l’elenco puntato. L’id sopracitato viene prelevato quando l’utente seleziona un nodo dell’albero della cartella clinica: tale identificativo infatti non sarà altro che il nome del nodo selezionato, già differenziato per costruzione dell’albero stesso. In queste pagina di visione generale, sono stati utilizzati i classici tag HTML perché si voleva anche testare la flessibilità del GWT e tentare di mescolare anche del puro codice HTML. Inoltre questo 70 Capitolo 5 – Soluzioni Tecniche Cartella Clinica metodo ci sembrava il più semplice e rapido per avere un elenco creato dinamicamente. 5.5 Allergology La prima sezione dell’anamnesi che si incontra è quella relative alle allergie. Vi sono qui due classi, una necessaria per la visualizzazione in dettaglio dei dati dell’allergia e un’altra per l’inserimento o la modifica. Partiamo con la classe che effettua la visualizzazione dei dettagli riguardanti la singola allergia. Come prima cosa ricordo che i dati mostrati in questa pagina sono il nome dell’allergia, la sua data di inizio ed eventualmente quella di fine e alcune note aggiuntive. La struttura di questa pagina, dal lato client, risulta quindi molto semplice: una serie di label disposte su di un AbsolutePanel. Tali label sono differenziate come per la visualizzazione del profilo del paziente. Le label che fungono da marcatura dell’informazione sono come al solito evidenziate con uno stile grassetto, mentre le altre label inizialmente sono vuote e saranno riempite dal risultato della RPC. La chiamata al server verrà effettuata naturalmente al caricamento della pagina. Una precisazione particolare va fatta per la visualizzazione delle note. Avendo la possibilità di contenere diverse righe, si è scelto di adoperare una diversa soluzione, cioè utilizzare un pannello per la visualizzazione. Un pannello infatti ha la possibilità di avere lo scroll orizzontale e verticale e quindi faceva esattamente al nostro caso. Analizziamo ora il servlet che effettua la query per reperire i giusti dati dal database. Questa query si basa sul concetto fondamentale di poter conoscere l’id dell’allergia, cioè l’identificativo assoluto di quel 71 Capitolo 5 – Soluzioni Tecniche Cartella Clinica record all’interno della tabella. Questo id è ricavato dalla sessione, opportunamente settata quando l’utente seleziona il nodo dell’allergia. Attenzione a non confondere tale id con l’id dell’allergia: il primo è effettivamente l’identificativo del problema allergico del paziente, mentre il secondo è l’id del tipo di allergia. Recuperato questo id, l’interrogazione a database è quindi molto semplice. Tramite un join si risale al nome dell’allergia e tramite l’id si riducono i vari record ad uno singolo. Ogni dato viene salvato in un ArrayList e ritornato al lato client. Al successo della RPC, i dati vengono prelevati all’ArrayList ed utilizzati per assegnare il valore alle singole label ed al pannello delle note. Per riempire tale pannello, abbiamo trattato il dato dell’ArrayList come del testo HTML e quindi l’apposito metodo setHtml per riempirlo. Ora affrontiamo invece il discorso relativo all’altra pagina, ovvero quella che permette le operazioni di inserimento e modifica delle allergie. Si è scelto ovviamente di sviluppare queste funzionalità sulla stessa pagina e, come per il profilo del paziente, di utilizzare un parametro booleano per identificare in quale modalità tale pagina stia, in quel momento, lavorando. Ricordo che ogni manipolazione di dati può essere effettuata solamente dal requester. Questa pagina infatti viene visualizzata tramite una finestra che viene aperta dalla barra degli strumenti: per l’inserimento tale opzione comparirà nella pagina di riepilogo delle allergie, mentre per la modifica la barra in questione è visualizzata nella pagina di dettaglio descritta precedentemente. La struttura grafica anche qui si basa su di un AbsolutePanel. I widget inseriti sono una ComboBox necessaria per la scelta dell’allergia, una TextArea utilizzata per l’inserimento delle note 72 Capitolo 5 – Soluzioni Tecniche Cartella Clinica aggiuntive ed un paio di DataField utilizzati per l’inserimento delle date di inizio e fine allergia. Come parametri del costruttore grafico sono passati la finestra di pop-up dove viene aperta la pagina, la cartella clinica stessa ed il parametro che identifica la modalità di esecuzione. Una procedura comune a tutte e due le operazioni è il caricamento dei dati presenti sulla ComboBox: tali dati vengono prelevati dal database tramite una normale query e memorizzati nell’oggetto Store, che funzionerà da sorgente per la ComboBox. Tutto questo avviene al completamento della RPC. Infatti i nomi vengono prelevati dalla tabella delle allergie e memorizzati sull’ArrayList in uscita. Affrontiamo per prima la situazione in cui il requester necessita di inserite una nuova allergia. Dopo aver inserito tutti i dati voluti, viene avviata la RPC necessaria per l’inserimento. Il servlet acquisisce tali dati tramite i parametri, ottiene l’id del paziente tramite la sessione ed effettua una normale query di inserimento. Questa procedura ovviamente viene bloccata in partenza se i valori delle date non sono logicamente validi, ovvero se la data di inizio è posteriore a quella di fine: in tal caso, alla premuta del pulsante, la data di fine allergia viene marcata in rosso dell’inserimento e l’utente vengono avvisato effettuate dell’errore. delle Alla operazioni fine necessarie all’aggiornamento della struttura della cartella clinica: viene infatti ricaricato l’albero per visualizzare il nodo della nuova allergia ed in più viene anche ricaricata la pagina riassuntiva dell’allergia in modo da poter visualizzare il completo elenco aggiornato. Nella modalità di modifica dei dati, è come prima cosa necessaria una RPC che carichi i dati da modificare. La chiamata che esegue tale compito viene effettuata nello stesso metodo del servlet che carica i dati 73 Capitolo 5 – Soluzioni Tecniche Cartella Clinica relativi alla ComboBox. Ovviamente qui verranno caricati anche i dati relativi all’allergia, sempre identificata dall’id contenuto nella sessione impostata alla selezione del nodo. Alla fine di tale query, i dati vengono inseriti nell’ArrayList. Lato client questa volta verranno recuperanti anche i dati necessari a riempire le caselle di testo, cosa non fatta in modalità di inserimento. La procedura effettiva che modifica i dati viene eseguita alla premuta del pulsante di memorizzazione: la RPC viene avviata solo se i dati rispettano le sopracitate regole riguardanti le date. Nel servlet viene, come prima, cosa reperito l’id dell’allergia tramite il nome della stessa, in quanto nella tabella contenente le informazioni dell’allergia del paziente non è presente il nome dell’allergia, ma solo l’id identificativo. Questo id è necessario se viene modificato il nome dell’allergia. Dalla sessione viene recuperato l’id del problema allergico da modificare e quindi effettuata una query di aggiornamento in base a tale dato. Lato client, al termine della chiamata, viene come prima cosa ricreato il pannello di visualizzazione da sostituire a quello vecchio (prima pagina descritta in questa sezione) ed infine ricaricato l’albero della cartella clinica. In queste pagine, non abbiamo riscontrato particolari problemi in quanto ci siamo aiutati parecchio con l’utilizzo delle sessioni e dei parametri. Soprattutto l’utilizzo dei parametri al costruttore rende queste pagine molto dinamiche e con la possibilità di avviare procedure molto complesse. Si pensi che già il fatto di dover interagire con l’aggiornamento dell’albero o della cartella clinica stessa, senza la possibilità di passare questi parametri, sarebbe stato estremamente difficile e macchinoso. Una delle caratteristiche più importanti del GWT infatti sta nello sfruttare queste tecniche derivanti dal Java, senza dover 74 Capitolo 5 – Soluzioni Tecniche Cartella Clinica necessariamente passare dal DOM come per il JavaScript: ricordo infatti che come parametri è possibile passare anche un oggetto, proprio come in Java. L’unica cosa non troppo semplice è stato il dover rendere dinamica la ComboBox con i nomi delle allergie, anche se con l’utilizzo dello Store abbiamo risolto ogni problema. 5.6 Familiars Nella cartella clinica è presente una sezione riguardante i dati medici dei genitori del paziente. Anche qui sono presenti due classi, una per la visualizzazione ed una per l’inserimento e la modifica. Questa parte della cartella clinica non prevede la presenza della pagina riassuntiva delle problematiche del paziente, in quanto non necessaria: la pagina di visualizzazione quindi viene attivata direttamente con la selezione del nodo sull’albero, senza quindi essere previsto nessun ulteriore nodo secondario. Se comunque non ci sono dati inseriti viene mostrata all’utente una pagine che appunta informa che tali dati non sono disponibili. Alla base della pagina di visualizzazione è presente un AbsolutePanel. Con il solito meccanismo una serie di label vengono utilizzate per la visualizzazione dei dati provenienti dal server. Anche in questa pagina sono presenti delle note aggiuntive inserite dal requester, quindi abbiamo deciso di operare nella stessa maniera delle pagine precedenti, ovvero utilizzando un pannello invece di una label. Per questo pannello, oltre che ad uno stile apposito di formattazione del testo, è stato impostato anche lo scroll automatico, interno alla pagina, così da ottenere un effetto simile all’iframe dell’HTML. 75 Capitolo 5 – Soluzioni Tecniche Cartella Clinica I dati necessari al riempimento delle label vengono effettuati tramite la consueta chiamata lato server. Nel servlet è presente un solo metodo che svolge tale compito. Come prima cosa viene recuperato l’identificativo del paziente dalla sessione aperta. Tramite questo id viene effettuata la query, che risulta molto semplice, in quanto ogni record di dati nella tabella coinvolta è univoco per ogni paziente. Come al solito i dati desiderati vengono memorizzati sull’ArrayList e ritornati al client. Il client al successo della chiamata preleva tali dati dal result del metodo onSuccess() dell’oggetto AsyncCallback effettuando il consueto cast ad ArrayList, ma non li utilizza direttamente. Infatti i dati prelevati dal server non sono nel giusto formato e quindi pronti per essere visualizzati sulle label. Oltre al solito controllo se i dati non sono nulli, è necessaria un’ulteriore analisi. L’informazione sulla morte dei genitori, infatti, è trattata in modo binario nel database, perciò a livello grafico, non è fattibile visualizzare per esempio 0 per indicare che il paziente è morto, sarebbe incomprensibile. Quindi si è scelto di adoperare una struttura condizionale, lato client, che controlla i dati prelevati da database e visualizza il giusto testo all’interno della label specifica. Questo ovviamente è fatto per entrambi i genitori. Per impostare il testo delle note ricordo che la procedura è leggermente diversa rispetto alle label: è necessario trattare i dati come normale testo HTML ed utilizzare il metodo specifico dei pannelli setHtml(). Passiamo ora alla pagina e alle classi lato server e client che permettono di inserire e modificare i dati relativi ai genitori del paziente. Bisogna innanzitutto far capire il meccanismo con cui tale pagina viene caricata. La modalità di inserimento dei dati viene attivata solo se non vi sono già dati presenti, in quanto come detto ogni record in tabella è univoco per ogni paziente. La modalità di modifica invece è 76 Capitolo 5 – Soluzioni Tecniche Cartella Clinica disponibile solo se ci sono già dei dati. Tutto questo viene deciso nella classe che rappresenta la cartella clinica nel suo insieme: non viene fatto altro che impostare la variabile booleana di flag necessaria a distinguere le due modalità. Nel costruttore grafico vengono quindi passati la finestra pop-up in cui tale pagina viene visualizzata, la cartella clinica stessa e appunto il parametro booleano. Graficamente la pagina è basata sul solito AbsolutePanel che contiene diversi widget. Come prima cosa è presente un widget non ancora trattato, ma che risulta essere graficamente molto efficace, il FieldSet. Tale widget si comporta come un pannello ed è utilizzato per raggruppare altri widget con lo stesso nesso logico. I widget contenuti in questo pannello sono contornati e quindi raggruppati, così da fornire un effetto grafico che, apparentemente non sembra importante, ma che in realtà per chi utilizzerà il portale risulterà molto significativo. Tale widget è stato utilizzato molto spesso nelle diverse classi. Gli altri widget presenti sono ComboBox, CheckBox e una TextArea. Le checkbox in questa pagina non sono caricate con dati provenienti da database, infatti, rappresentando i vari gruppi sanguigni, non sono previste possibilità di ampliamento o modifica. I gruppi sanguigni sono caricati tramite un Store e l’associato oggetto SimpleStore, che preleva appunto il testo necessario da un metodo contenuto in questa classe: i dati sono passati trami array multidimensionale, in quanto tale oggetto acquisisce solo questa tipologia di variabili. Sempre riguardanti le ComboBox, sono interessanti le varie proprietà impostate: oltre allo store, è stato necessario settare due proprietà, setForceSelection(true) e setSelectOnFocus(true). La prima per restringere la scelta del testo alla 77 Capitolo 5 – Soluzioni Tecniche Cartella Clinica lista degli elementi nella combobox e non permettere all’utente di inserire “nuovi gruppi sanguigni”. La seconda è stata utilizzata per permettere un’automatica selezione di uno delle voci in elenco quando si comincia a scrivere nella combobox (autocompletamento). Procediamo con l’analisi della pagina in modalità inserimento. In questa modalità il valore del flag booleano sarà false. L’inserimento quindi non prevede nessun tipo di interazione con il server al caricamento della pagina. La procedura di attivazione della RPC avviene tramite la premuta di pulsante di memorizzazione: a differenza di altre pagine, qui i dati inseriti dal requester non sono soggetti a nessun controllo logico in quanto si tratta di dati non dipendenti. Prima dell’attivazione della chiamata vera e propria, i dati prelevati dai vari widget devono essere correttamente formattati. Infatti, come detto sopra, i dati nel database non sono memorizzati come vengono inseriti dall’utente. Una struttura condizionale ha il compito di “capire” se la checkbox relativa alla morte dei genitori è selezionata o meno e quindi memorizzare a 0 oppure 1 (0=morta, 1=viva) la variabile che verrà passata come parametro nella RPC. Nel servlet, acquisiti i dati dal client, avviene la query di inserimento: prelevato l’identificativo del paziente dalla sessione, tutti i dati sono ora nel giusto formato e quindi memorizzabili correttamente. La pagina in modalità modifica funziona in modo differente. Al caricamento vengono visualizzati i dati che l’utente ha richiesto di modificare. Tale procedura viene effettuata solamente in questa modalità. Effettuata la chiamata al servlet, non sono necessari parametri e viene eseguita una query sull’id del paziente ottenuto dalla sessione. I dati vengono quindi prelevati dal database, senza alcuna formattazione, 78 Capitolo 5 – Soluzioni Tecniche Cartella Clinica e memorizzati nell’ArrayList. Solamente quando il controllo ritorna al lato client i dati vengono formattati correttamente così da poter essere visualizzati: le checkbox vengono selezionate solo se dal database il dato sarà 1, ovvero genitore ancora vivo. Quando i dati vengono modificati dal requester e viene premuto il pulsante di memorizzazione, viene avviata la chiamata lato server relativa alla modifica. La formattazione corretta dei dati avviene lato client: i dati pronti da inserire nel database sono quindi passati come parametri dello specifico metodo del servlet. Qui la query di aggiornamento viene eseguita sull’identificativo del paziente prelevato dalla sessione, in quanto esso è univoco per ogni record. Quando termina l’azione del servlet e si passa al metodo onSuccess() del lato client, sono necessarie diverse operazioni di aggiornamento della struttura della cartella clinica. Viene innanzitutto ricreato un tab identico a quello in cui venivano visualizzati i dati, cioè con le stesse proprietà e caratteristiche. Nella cartella clinica inoltre è necessario aggiornare i due tab togliendo quello vecchio e sostituendovi quello appena creato, nonché ricaricare l’albero dinamico. Queste operazioni avvengono grazie alla cartella clinica che è stata passata come parametro al costruttore della classe: vengono attivati due metodi della cartella clinica, uno per lo scambio dei tab, l’altro per l’aggiornamento dell’albero. Non ci sono stati particolari problemi nel realizzare questa sezione, in quanto anche la formattazione dei dati necessaria per la visualizzazione è stata relativamente semplice. 79 Capitolo 5 – Soluzioni Tecniche Cartella Clinica 5.7 Family Pathologies In questa sezione verranno prese sotto esame le soluzioni tecnologiche utilizzate per gestire le patologie dei parenti. Sono qui presenti due pagine, una per la visualizzazione dei dettagli ed una con il compito di fornire la possibilità di inserire o modificare i dati. La pagina di visualizzazione viene mostrata quando si accede al nodo della patologia. Le informazioni presenti sono il nome della patologia, seguita da un elenco dei parenti in cui questa è stata riscontrata. Come pannello di base è stato utilizzato il solito AbsolutePanel. Il fatto che sia stato utilizzato quasi sempre l’AbsolutePanel è dovuto al fatto che la struttura grafica delle pagine nella cartella clinica sono molto simili. Inoltre si è cercato di dare una sorta di conformità tra le varie pagine. Il nome della patologia viene visualizzato su di una label, mentre l’elenco dei parenti su di un pannello, sempre per permettere lo scorrimento. Le proprietà delle label sono sempre le stesse, cioè carattere grassetto per le etichette di marcatura e normale per quelle di visualizzazione dei dati dinamici. Il pannello ovviamente ha impostata la proprietà dello scorrimento automatico in caso di presenza nell’elenco di molti parenti. La chiamata RPC viene eseguita al caricamento della pagina stessa. La query che viene effettuata dal servlet necessita di due cose: un identificativo del record da mostrare e ovviamente, come per le allergie, di reperire il nome della patologia, essendo in un'altra tabella. L’id viene prelevato come al solito dalla sessione corrente: quando viene selezionato il nodo della corrispondente patologia familiare, viene anche settato il giusto id nella sessione, tutto nella cartella clinica. Per quanto riguarda il nome della patologia, basta effettuare un join tra tabelle ed 80 Capitolo 5 – Soluzioni Tecniche Cartella Clinica otteniamo il risultato voluto. I dati vengono caricati nell’ArrayList e pronti per essere utilizzati lato client. Bisogna però ricordare che i dati presenti nel database e quindi ora memorizzati nell’ArrayList sono in formato binario, ovvero se è certificato il riscontro della patologia per quel parente nel database sarà presente 1, in caso contrario 0. Come si potrà capire tali dati dovranno essere formattati a dovere lato client. Ritornando lato client, quando la RPC ha avuto successo, i dati cominciano ad essere prelevati dall’ArrayList e formattati. Come visualizzazione si è scelto di utilizzare un elenco puntato, creato con i classici tag HTML. Questo è stato possibile grazie all’utilizzo del pannello e del suo metodo setHTML(). Con tale metodo è possibile costruire anche un’intera pagina HTML, passando come parametro la stringa contenente il codice HTML desiderato. Per ogni dato contenuto nell’ArrayList verrà notificata in una variabile di appoggio la presenza o meno di quel parente: in realtà, questa variabile conterrà il codice HTML necessario per tale metodo e quindi in essa verrà accodato il messaggio uscente, direttamente con il tag <li> impostato per ogni parente. Il tutto è effettuato da varie strutture condizionali che oltre a controllare la presenza o meno del dato nell’ArrayList, controllano anche il numero binario contenuto in esso e quindi decidono se accodare il messaggio alla variabile di appoggio oppure no. Per quanto riguarda la visualizzazione vera e propria le varie proprietà quali il padding e gli stili sono quindi forniti direttamente dal codice HTML stesso. 81 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Passiamo ora a parlare della pagina di inserimento e modifica dei dati. Anche qui, come negli altri casi, si è cercato di raggruppare queste due funzioni, così da ottenere lo stesso risultato da un’unica pagina o classe. Ovviamente sarà presente un parametro che notifica quale funzione deve essere attivata: per la precisione abbiamo utilizzato un parametro booleano passato al costruttore della classe, il quale assume valore vero in caso di modifica e falso in caso di inserimento di nuovi dati. Questo parametro, viene passato al costruttore grafico e quindi impostato all’atto della creazione dell’oggetto e quindi della pagina. Oltre a questo parametro, nel costruttore sono passati anche la finestra di pop-up dove vengono aperte le pagine e la cartella clinica stessa. L’attivazione delle due modalità avviene tramite la toolbar presente nei tabs di visualizzazione: la pagina verrà impostata per l’inserimento se il requester accederà alla barra degli strumenti dalla pagina di riepilogo, mentre sarà attivata la modifica solamente dalla barra presente nel dettaglio della patologia desiderata. Graficamente tale pagina è semplicemente costituita da una ComboBox, che permette la selezione della patologia; e da un elenco di tutti i parenti, selezionabili tramite dei pulsanti di scelta multipla. Entrambe appoggiano su di un AbsolutePanel. I dati nella ComboBox sono reperiti da database, sia nella modalità di inserimento che in quella di modifica. Questo è stato fatto perché si vuole rendere dinamica il più possibile la pagina: così facendo, la modifica o aggiunta di una patologia è molto più semplice ed avviene senza dover modificare direttamente il codice della classe. Questa procedura remota avviene tramite una RPC al caricamento della pagina. Dal servlet vengono reperiti i nomi delle patologie, disposte in ordine 82 Capitolo 5 – Soluzioni Tecniche Cartella Clinica alfabetico ed infine memorizzate sull’ArrayList. Quando il controllo ritorna lato client, avviene al vera e propria costruzione, non solo della ComboBox, ma anche dell’intera pagina. I dati appena prelevati dal servlet vengono memorizzati nello store, il quale servirà a caricarli nella ComboBox. Come detto, dopo aver caricato i dati sulla ComboBox, avviene anche la vera e propria creazione della pagina, tutto all’interno del metodo onSuccess(). La creazione del pannello contenente l’elenco dei parenti è stato effettuato con una procedura abbastanza complessa. In realtà, infatti, questo pannello non è presente direttamente in questa classe, ma è costruito all’interno di una seconda classe. Oltre alla semplice creazione della pagina grafica tramite un AbsolutePanel ed una griglia con la possibilità di scelta multipla, vi sono presenti due metodi per la memorizzazione e il recupero dei dati da questa griglia. Questa classe è stata necessaria per prima cosa perché la struttura della griglia in questione è abbastanza complessa e poi in quanto permetteva una maggiore separazione delle due modalità: infatti basta attivare dalla pagina genitrice il metodo desiderato e possiamo o memorizzare o ottenere i valori richiesti. Ritornando alla classe principale, per ottenere la griglia sopra citata, non basta far altro che creare un oggetto della seconda classe, avendo quindi a disposizione tutti i suoi metodi. Fatto questo si aggiunge l’oggetto appena creato ad un Panel. Dopo la creazione di questo pannello, a seconda in quale modalità la pagina sta operando, vengono create due strutture differenti: in entrambe è presente un pulsante per la memorizzazione, mentre per la modalità di modifica, è stato introdotto anche un pulsante per reimpostare i dati iniziali. Analizziamo come primo caso l’intervento della pagina in modalità inserimento. Oltre al caricamento dei dati nella ComboBox, al 83 Capitolo 5 – Soluzioni Tecniche Cartella Clinica caricamento della pagina non vengono effettuare altre chiamate remote ai servlet. Il requester prima di effettuare la memorizzazione dei dati, deve rispettare alcune regole impostate. In questa fase, la selezione di una patologia è necessariamente obbligatoria e non è possibile inserire tramite questa form un’eventuale nuova patologia: il requester deve quindi selezionare una delle patologie dall’elenco presente nella ComboBox. Inoltre è necessario, come ovvio, anche la selezione di uno dei parenti, per dare un senso all’inserimento stesso. Il non rispettare tali regole, invalida la procedura di inserimento, segnalando il problema all’utente. Inseriti correttamente i dati, si attiva la procedura remota premendo sul pulsante di memorizzazione. La prima cosa che viene effettuata è quella di prelevare i dati dalla griglia ed inviarli come parametro al servlet. Questi dati sono passati tramite un array di interi, così sono già ben formattati per il database. Oltre a questo array è passato ovviamente anche il nome della patologia. Segnalo inoltre che, anche se non sempre enunciato, come ultimo parametro di ogni chiamata remota è necessario l’oggetto AsyncCallback. Per effettuare l’inserimento, oltre alla forma binaria dei parenti che notifica la presenza o assenza della patologia, è necessario reperire l’id del paziente e della patologia stessa. L’identificativo del paziente viene recuperato come al solito dalla sessione corrente. Successivamente viene reperito l’identificativo del nome della patologia dalla tabella da cui venivano anche reperiti i dati da caricare nella ComboBox. Ottenuti i dati necessari si procede con la query di inserimento. Nel client, completata l’azione del servlet, vengono utilizzati i parametri passati al costruttore. Con la cartella clinica viene prima 84 Capitolo 5 – Soluzioni Tecniche Cartella Clinica ricaricato l’albero così da avere la nuova patologia inserita. Successivamente si procede con la ricarica della pagina di riepilogo, così da far comparire il nome del problema inserito. Infine con il parametro della finestra non viene fatto altro che invocare il metodo di chiusura della stessa. Affrontiamo ora un discorso analogo per la procedura di modifica. Qui al caricamento, oltre alla chiamata remota necessaria per la visualizzazione dell’elenco di patologie, è effettuata anche la RPC per reperire i dati da modificare. Questa procedura non farà nient’altro che selezionare la giusta patologia dalla ComboBox e selezionare i relativi parenti che ne sono affetti. Nel metodo del servlet disposto per tale operazione come prima cosa viene reperito l’identificativo univoco del record da visualizzare: questo è possibile in quanto tale parametro è impostato dalla cartella clinica alla selezione del nodo desiderato ed è quindi recuperabile dalla sessione corrente. Il nome della patologia, che non è inserito nella stessa tabella dei dati del paziente, viene prelevato tramite un semplice join. I dati relativi alla presenza dei parenti vengono caricati su di un ArrayList, ed uniti in un secondo ArrayList già contenente il nome della patologia prima prelevato. Lato client le informazioni del servlet vengono interpretate da una struttura iterativa: il ciclo scorre l’ArrayList, separando il nome dalle presenze dei parenti. Successivamente i dati sui parenti vengono caricati e visualizzati sulla griglia. Questa procedura è richiamata anche dal pulsante, sopra citato, che permette il recupero delle informazioni iniziali dove infatti non viene fatto altro che ricaricare virtualmente la pagina. 85 Capitolo 5 – Soluzioni Tecniche Cartella Clinica La modifica dei dati appena visualizzati avviene anche qui tramite il tasto di memorizzazione. Prima di avviare la RPC tramite l’apposito metodo, vengono prelevati i parenti selezionati ed inseriti nell’array di interi così da avere già una forma binaria (come nell’inserimento). La RPC viene quindi eseguita e come parametri vengono passati proprio il nome della patologia e questo array di interi. Ovviamente anche qui viene effettuato un controllo sui dati inseriti, proprio gli stessi controlli che avvenivano in fase di inserimento; in caso contrario i la RPC non viene eseguita. Lato servlet vengono reperiti i dati ulteriori necessari alla query: dalla sessione viene prelevato l’identificativo del record di dati da modificare, mentre tramite un’ulteriore query viene ricavato l’id della patologia. La query di modifica è eseguita sulla base dell’id del record, in quanto chiave univoca nella tabella. Quando si ritorna al lato client, vengono eseguite le consuete operazioni di routine: aggiornamento dell’albero e del tab di visualizzazione corrente. Per l’albero basta utilizzare il consueto metodo prelevato dalla cartella clinica, che era stata opportunamente passata al come parametro nel costruttore del Composite. Per l’aggiornamento del tab invece si è operato come per gli altri casi simili: si è dapprima ricreato un tab identico, per proprietà e caratteristiche, a quello visualizzante i vecchi dati modificati, e successivamente si è utilizzato il metodo di scambio tabs della cartella clinica. Infine è presente anche l’operazione di chiusa automatica della finestra alla fine dell’operazione di modifica. 86 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Nello sviluppo e realizzazione di queste classi non abbiamo riscontrato parecchi problemi, se non nel dover creare una classe in più per la manipolazione della griglia dei parenti. Abbiamo comunque scelto questa soluzione perché ci è sembrata la più semplice e ordinata da un punto dal punto di vista del riutilizzo, anche se a prima vista può sembrare il contrario. Anche in questo caso, tramite il passaggio dei parametri, non è stato necessario accedere direttamente al DOM. Per il resto la risoluzione è stata molto simile che per la sezione delle allergie. 5.8 Pathologicals In questo paragrafo verranno presentate le soluzione utilizzate in quella sezione della cartella clinica che riguarda le patologie contratte dal paziente in esame. Vi è la presenza anche qui di due pagine, una atta a presentare i dettagli della patologia selezionata, l’altra a fornire funzionalità manipolative quali l’inserimento o la modifica di queste informazioni. Per accedere alla pagina dei dettagli bisogna selezionare la patologia dall’albero laterale presente nella cartella clinica. La struttura grafica che accompagna questa pagina è simile a quelle precedenti, ovvero ha come base un AbsolutePanel e le solite soluzione di stile da applicare alle label. Le informazioni presenti sono molteplici e comprendono oltre che alla patologia, anche informazioni riguardo l’eventuale ospedale dove è avvenuto il ricovero, visualizzando anche la diagnosi e le note relative alle dimissioni. Proprio riguardo alla visualizzazione di questi due ultime informazioni, è stato necessario inserire nella pagina due pannelli, così da dotare le singole note di uno scorrimento interno quando vi è bisogno. Le pagine infatti contenute 87 Capitolo 5 – Soluzioni Tecniche Cartella Clinica nella cartella clinica non devono essere troppo lunghe, in quanto come ricordo la cartella clinica è aperta su di una finestra di pop-up e quindi la sua dimensione è relativamente ridotta. Per questo pur dotando i tab di scroll automatico, si è deciso di non appesantire la struttura creando pagine troppo grandi. Il riempimento delle label con i dati viene effettuato tramite una RPC al caricamento della pagina. Questa sarà anche l’unica chiamata remota in questa pagina. Lato server è necessaria una query che reperisca tutte le informazioni necessarie, come il nome della patologia, l’età, l’eventuale ricovero e le date di entrata e uscita dall’ospedale, nonché la diagnosi e la lettera di dimissioni, ecc.. Per ottenere tali dati come prima cosa è stato necessario ricavare l’identificativo del record di dati. Questo parametro può essere prelevato dalla sessione corrente: selezionato il nodo dell’albero da cui si accede alla pagina, si imposta automaticamente tale valore. Il nome della patologia viene prelevato tramite un join tra tabelle del database ed effettuata la query i dati vengono memorizzati nel solito ArrayList di ritorno al lato client. Ricordo che, parlando di diversi dati, bisogna prestare particolare attenzione all’ordine in cui gli stessi sono memorizzati, così da essere bene recuperati dal client. Il client non fa altro che elaborare tali dati e dove necessario formattarli correttamente per la visualizzazione. Innanzitutto vengono prelevati solamente i dati presenti, ovvero viene effettuato un controllo sull’effettiva presenza: in caso di assenza di un dato si passa al dato successivo lasciando la label vuota. La formattazione è necessaria per il solo campo riguardante il ricovero in ospedale. Infatti tale dato è memorizzato in forma binaria (necessità di ricovero oppure no) e perciò è stato sufficiente introdurre una struttura condizionale che, controllato 88 Capitolo 5 – Soluzioni Tecniche Cartella Clinica il valore binario, impostasse in formato “umano” il dato nella label: viene semplicemente visualizzata la scritta “yes” o “no” a seconda se è avvenuto o meno il ricovero. Ricordo inoltre che per i pannelli, i dati non vengono inseriti come per le labe, ma vengono trattati come dati HTML puri. Passiamo ora ad affrontare la pagina che permette al requester di manipolare i dati sopra elencati, ovvero di effettuare operazioni di inserimento e di modifica. La scelta della modalità in cui la pagina deve operare è scelta in base al valore di un parametro booleano, passato direttamente quando viene creato il composite: anche qui quando il valore sarà vero la pagina opererà in modifica, altrimenti in inserimento. Oltre a questo parametro, sono presenti al solito la cartella clinica e la finestra di pop-up. La modalità di inserimento come al solito è attivabile dalla pagina riassuntiva della sezione delle patologie, mediante la toolbar e l’opportuno menu. Rispettivamente la modalità di modifica è attivabile dalla pagina dettaglio descritta precedentemente in questo paragrafo. Graficamente la pagina si sviluppa su di un AbsolutePanel contenete altri due particolari pannelli, già incontrati: i FieldSet. Il primo FieldSet contiene una ComboBox per la visualizzazione della patologia ed un campo di inserimento numerico per l’inserimento del’età di riscontro della stessa. Nel secondo FieldSet invece sono presenti le caselle di inserimento dei dati riguardanti il ricovero ospedaliero, quali le date di entrata e uscita dall’ospedale, la diagnosi, la lettera di dimissioni ed ulteriori dati sull’ospedale. Quest’ultimo FieldSet ha una particolarità: è presente infatti una checkbox laterale, 89 Capitolo 5 – Soluzioni Tecniche Cartella Clinica che se selezionata, permette di visualizzare i sui widgets interni, altrimenti rimangono invisibili. Si ottiene quindi un effetto a comparsa che mira a far inserire i dati relativi al ricovero, solo se questo è effettivamente avvenuto. Come per la classe delle allergie, anche qui il caricamento dei dati della ComboBox è comune a tutte e due le modalità e viene effettuata al caricamento della pagina. Nel servlet vengono reperiti i nomi da una semplice query e successivamente inseriti nell’ArrayList di ritorno al client. Nel client, tramite l’oggetto SimpleStore, vengono caricati nella ComboBox. L’unica accortezza che si è dovuto prendere in questo caso è quella del dover, in caso di inserimento, prelevare solamente tale informazione dall’ArrayList: infatti lo stesso ArrayList è utilizzato per memorizzare i dati necessari alla fase di mofica. Questo problema è stato semplicemente affrontato con una struttura condizionale che riconosce la modalità operativa della pagina. Parliamo ora della pagina predisposta per la funzionalità di inserimento dei dati. Anche qui sono presenti degli accorgimenti logici che l’utente deve rispettare per attivare la procedura di inserimento. La prima regola riguarda la necessaria presenza di una patologia inserita e selezionabile solamente da quelle in elenco. L’altro accorgimento riguarda l’inserimento della data di uscita dall’ospedale in caso di ricovero: tale data ovviamente deve essere posteriore a quella di ingresso. Prima dell’inserimento inoltre vengono anche reperiti i dati relativi al ricovero solamente se questo è selezionato dal FieldSet, altrimenti tali dati vengono passati come nulli. Rispettando tali regole viene quindi effettuata la chiamata. 90 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Nel servlet vengono dapprima ricavati gli ulteriori dati necessari ad un corretto inserimento e dalla sessione viene prelevato l’id del paziente. Tramite un query viene prelevato l’id della patologia dalla rispettiva tabella. I dati relativi alle date vengono debitamente formattati e quindi può avere luogo la query di inserimento. Quando l’azione torna lato client, avvengono le varie operazioni di aggiornamento della cartella clinica, ovvero la ricarica dell’albero laterale e della pagina di riepilogo della sezione, oltre che la chiusura della finestra di pop-up. Passiamo ora all’operazione di modifica che può essere effettuata da questa pagina. Come accennato precedentemente, qui è necessario anche un recupero, tramite la stessa RPC di caricamento della ComboBox, dei dati da modificare. I dati prelevati sono presenti infatti sullo stesso ArrayList e vengono visualizzati sui rispettivi widgets. Modificati tali dati, il requester attiva la proceduta di modifica e al relativa chiamata remota tramite il pulsante di memorizzazione. Prima di essere passati al servlet i dati vengono opportunamente controllati, con gli stessi criteri che per l’inserimento. La query lato server necessita, oltre che dei dati passati tramite parametri, anche dell’id del record da modificare e dell’id della patologia. Quest’ultimo è ricavato, come per l’inserimento, tramite una query sul nome nell’opportuna tabella, mentre l’identificativo del record è prelevato dalla sessione corrente. Una volta disponibili tutti i dati è semplice effettuare la query di aggiornamento tramite l’identificativo record, che è ovviamente attributo chiave. 91 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Nel procede allo sviluppo e alla realizzazione di questa sezione, non sono stati riscontrati particolari problemi. Le soluzioni utilizzate sono pertanto risultate molto simili che per le sezioni illustrate precedentemente. 5.9 Physiologicals In questo paragrafo verrà presa in esame la sezione relativa ai problemi fisiologici del paziente. Prima sarà descritta la pagina di visualizzazione riassuntiva, successivamente quella che premette l’inserimento e la modifica dei dati. In questa sezione infatti non sono previste pagine per dettagliare ulteriormente il problema clinico, in quanto saranno solamente visualizzate delle note generiche. La pagina di visualizzazione è basata su di un AbsolutePanel. La pagina che si presenta davanti all’utente è una struttura molto semplice, con delle etichette e dei pannelli affiancati a queste. La decisione riguardo ai pannelli è stata necessaria, in quanto le informazioni visualizzabili sono poste sotto forma di note, senza ulteriore formattazione, quindi la loro lunghezza è a discrezione del requester. Con l’utilizzo di pannelli infatti possiamo sfruttare la proprietà di scorrimento, non disponibile nelle label. In questa pagina è presente una sola chiamata remota, necessaria al caricamento dei dati sui pannelli. Nel servlet viene effettuata una semplice query sull’id prelevato del paziente prelevato da sessione. Le informazioni vengono quindi inserite in un ArrayList e ritornate al client, senza nessun tipo di formattazione. 92 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Lato client, i dati vengono ripresi dall’ArrayList ed utilizzati, tramite una struttura condizionale ed interativa, per riempire i vari pannelli. Il metodo utilizzato imposta tali dati come dati HTML, non come normali stringhe. Questo comunque non ha complicato la visualizzazione. Ogni pannello viene ovviamente riempito solamente in caso di dato rispettivamente trovato nel database. Analizziamo ora la pagina che permette l’inserimento e la modifica dei dati visualizzati. La modalità di inserimento viene attivata tramite la toolbar presente nel tab solamente nel caso in cui nel database non vi siano già tali informazioni per il paziente in esame. In caso contrario è attivata la pagina in modalità di modifica. Il controllo viene effettuato a livello di cartella clinica generale, non in questa pagina, in quanto il menù della toolbar è creato in quella classe. La modalità viene decisa anche qui dal valore assegnato ad una variabile booleana, presente tra i parametri del costruttore. Oltre a questo parametro, sono passati anche la cartella clinica e la finestra di pop-up. Come widget sono stati utilizzati delle TextArea e due ComboBox, disposti su di un AbsolutePanel. Le ComboBox non necessitano di reperire dati dal database, in quanto visualizzano i gruppi sanguigni: si è utilizzato uno Store che preleva i gruppi da un metodo della classe. Partiamo con la pagina in modalità inserimento. Non vi è alcun controllo sui dati inseriti dal requester, in quanto non sono correlati tra loro. Ovviamente la contraddizione logica di alcune informazioni contenuti su questa pagina, non può essere affrontata a livello di programmazione. 93 Capitolo 5 – Soluzioni Tecniche Cartella Clinica Inseriti i dati quindi si avvia la procedura remota, che richiede la presenza del servlet. In questo servlet, una volta acquisiti i valori per passaggio dei parametri, viene eseguita una query di inserimento in base all’identificativo del paziente, prelevato dalla sessione. Nel ritorno al lato client, vengono eseguite le solite operazioni di aggiornamento della cartella clinica: ricarica dell’albero e della pagina nel tab sottostante al pop-up. Nella modalità di modifica, invece, la prima cosa che viene effettuata è la RPC necessaria a reperire i dati interessati. Al caricamento viene richiamato il servlet, che tramite una semplice query sull’id del paziente, preleva le informazioni. Nel client tali dati non subiscono formattazione e vengono quindi prelevati dall’ArrayList e visualizzati direttamente nelle TextArea. La vera procedura di modifica viene richiamata quando il requester necessita di memorizzare i dati e, alla premuta del pulsante, la chiamata remota avvia il servlet. La query qui presente, prelevato l’id del paziente, si appresta ad eseguire un aggiornamento con i dati passati come parametri al metodo. Lato client le operazioni di aggiornamento della cartella clinica sono le stesse che per l’inserimento. Quest’ultima situazione avviene perché le due modalità vengono attivate dal medesimo tab e differiscono solo in base alla presenza/assenza di informazioni nel database. Nelle altre pagine ogni modalità veniva attivata da un tab differente. Nell’affrontare questa sezione non ci sono stati problemi di alcun tipo, in quanto sia lato client che lato server si avevano diverse soluzioni. Si è scelto di operare nella maniera sopra descritta perché 94 Capitolo 5 – Soluzioni Tecniche Cartella Clinica rappresenta la più semplice ed immediata, non complicando inutilmente la realizzazione. 5.10 Patient Clinical Problem In questa sezione della cartella clinica vengono analizzati i problemi clinici del paziente. A differenza delle parti sopra descritte qui è non è stato possibile utilizzare la stessa classe per il dottore e per il requester. Si è adottata comunque una solida conformità grafica per abituare così l’utente ad avere la stessa interfaccia. Le due soluzioni differiscono notevolmente nelle funzionalità. Le operazioni del dottore sono relative alla messaggistica ed alla refertazione del problema clinico. Le possibilità del requester invece coinvolgono, oltre alla messaggistica, anche l’inserimento del problema clinico stesso, nonché la conseguente aggiunta dell’esame clinico. L’attivazione della pagina avviene tramite la selezione dell’ultimo nodo dell’albero presente lateralmente: al click viene visualizzata all’interno di un tab. Verranno per prime prese in esame le soluzioni riguardanti la sezione del dottore. Graficamente la pagina si appoggia su di un AbsolutePanel e si sviluppa poi su due pannelli separati. Superiormente è presente una griglia che presenta i vari problemi clinici per quel paziente, nonché informazioni come le date di apertura e chiusura dell’evento e il numero di esami, referti e messaggi presenti. Per una migliore lettura è presente un raggruppamento per stato dell’evento, ovvero “open” ecc. Nella parte inferiore invece sono presenti le schede visualizzanti le informazioni dell’esame selezionato. 95 Capitolo 5 – Soluzioni Tecniche Cartella Clinica La parte inferiore è visualizzata grazie alla creazione di un oggetto di un'altra classe. La classe relativa ai problemi clinici infatti conterrà solamente la citata griglia, mentre le altre sezioni visualizzate sono di altre classi Java. Desta importanza però il metodo che effettua l’aggiornamento della griglia. Questo è necessario in caso di modifica dello stato del problema clinico, nonché dei dati del paziente stesso. Per questo motivo il metodo richiama tramite una procedura remota i dati necessari alla creazione della griglia. Nel servlet quindi, vengono recuperati i nuovi dati dal database, che è stato aggiornato, e nel client tali vengono utilizzati alla rigenerazione della griglia stessa. A separare le due parti è presente una toolbar con le opzioni disponibili per il dottore. Per le opzioni riguardate i referti ed i messaggi, la procedura richiama una classe diversa. Per l’inserimento di messaggi o referti la classe che compie tale operazione visualizza un form di inserimento dati: le informazioni sensibili sono il nome del dottore, le informazioni sull’esame in questione e naturalmente il testo inserito. La chiamata remota lato server effettua semplicemente una query di inserimento prelevando l’id del dottore dalla sessione di login. Importante ricordare che, nel caso di inserimento di un referto, viene anche impostato lo stato a “reported”. Per la visualizzazione il funzionamento coinvolge una classe diversa: viene visualizzata l’informazione reperita, tramite la RPC, nel widget Panel. I dati ovviamente saranno quelli inseriti al passo precedentemente. Viene analizzata ora la parte contenente i dati relativi al problema clinico, ovvero quella che permette la giusta selezione delle schede. Le funzionalità qui descritte sono ricavate dalla creazione di un oggetto di una classe differente. In questa seconda classe avverrà la vera e propria creazione delle schede annunciate. Tramite una ComboBox come prima 96 Capitolo 5 – Soluzioni Tecniche Cartella Clinica cosa viene selezionato l’esame clinico che si desidera osservare. I dati caricati sono quelli prelevati tramite una RPC da database perché vengono visualizzati solamente gli esami realmente fatti. Selezionato un esame viene aperto una scheda (pannello) in cui vengono mostrate ulteriori informazioni inserite dal requester. È qui anche possibile effettuare il download del file rispettivo. Naturalmente i dati presenti vengono tutti prelevati da database ed identificati tramite il parametro passato alla chiamata remota ed ottenuto dalla griglia: il client non fa altro che formattare i vari dati nelle apposite label. Per quanto riguarda il download la procedura è molto semplice, in quanto è necessario solamente effettuare l’apertura del file tramite una nuova finestra del browser. Il resto del compito è svolto dal browser. Verrà ora presentata la parte relativa al requester. Come già annunciato la disposizione grafica degli elementi è simile. Nella parte superiore è presente una griglia con i vari problemi clinici del paziente, mentre al di sotto sono presenti le solite schede. Tra le due vi è la solita toolbar, con le opzioni disponibili per il requester. Tuttavia queste opzioni e le sottostanti schede sono molto differenti rispetto a quelle del dottore, causa per cui è stato necessario sviluppare due pagine separate anziché una singola come negli altri casi. La griglia come al solito è creata a partire da una chiamata remota che preleva i dati necessari dal database. La query effettuata nel servlet è molto semplice in quanto sfrutta l’id del paziente presente nella sessione corrente, così da avere tutti i dati necessari. Le informazioni presenti nella griglia sono lo stato del problema, le date di apertura ed 97 Capitolo 5 – Soluzioni Tecniche Cartella Clinica eventuale chiusura del problema, il numero di referti o messaggi, nonché di esami effettuati. Nella toolbar sono presenti le seguenti opzioni: impostare lo stato del problema a “close” oppure a “request another”, visualizzarne i dettagli oppure aggiungervi un esame, nonché creare da zero un nuovo problema clinico. Tutte queste operazioni sono create nello stesso modo in cui lo erano quelle del dottore. Per le prime due operazioni non è viene fatto altro che attivare una RPC che tramite una query di aggiornamento cambio lo stato del problema clinico. Naturalmente al termine della chiamata remota la griglia viene rigenerata così da includere l’aggiornamento. Tramite le operazioni successive vengono aperte le citate schede. Queste non sono nient’altro che dei Panel in cui l’oggetto Java creato viene aggiunto. Per l’operazione di visualizzazione del dettaglio dell’esame clinico sono state adottate le comuni soluzioni delle label con i dati prelevati da database tramite una semplice RPC. L’operazione di aggiunta di un esame o di un nuovo problema clinico sono in realtà creati in modo speculare. Infatti lo stesso oggetto Java sta alla base di queste due funzionalità: un form di inserimento costituito da una serie di TextArea e con la possibilità finale di aggiungere anche il file dell’esame di laboratorio. Come si può facilmente intuire l’inserimento risulta molto semplice e viene effettuato trami una chiamata remota con una query di inserimento nelle giuste tabelle del database. Anche qui a fine di ogni RPC è necessario rigenerare la griglia. Un particolare passaggio da affrontare invece necessita l’upload del file sul server. Vengono utilizzati due widgets finora mai incontrati, ovvero File Upload e FormPanel. Il primo come si capisce è utilizzato 98 Capitolo 5 – Soluzioni Tecniche Cartella Clinica per aprire una finestra che visualizza il proprio hardisk e permette selezionare il file. Questo widget inoltre ha il compito di prelevare il nome del file. Il form invece è necessario in quanto è da questa struttura che viene inviato il file. Questo widget permette di avviare un servlet senza necessità di una RPC. Infatti il servlet sarà un’estensione dell’HttpServlet e conterrà al suo interno l’implementazione del metodo doPost() utilizzato per memorizzare effettivamente i dati nel server. La soluzione utilizzata è molto simile a quella per costruire i dati necessari alla creazione dell’albero asincrono (in quel caso si creava una stringa XML). 99 CAPITOLO 6 - CONCLUSIONI In questo capito conclusivo della tesi verranno presentate le mie conclusioni personali riguardante il progetto, inteso nel suo complesso, non solamente riguardo la cartella clinica. Verranno analizzati gli obiettivi raggiunti ed eventualmente quelli a cui non è stato possibile arrivare. Sarà fatta un’autocritica riguardo ai problemi che purtroppo possono sorgere nell’utilizzo del software creato ed inoltre saranno presentati alcuni possibili miglioramenti. In ultimo verrà anche proposta un’analisi comparativa tra il vecchio portale Miro realizzato con tecnologia Ruby On Rails e quello attualmente realizzato. 6.1 Obiettivi raggiunti Gli obiettivi che erano stati prefissati quando ci è stato affidato lo sviluppo di questa applicazione sono stati a pieno raggiunti: creare un portale pienamente funzionale atto a svolgere compiti di telemedicina, quindi di interfacciare le figure del requester e del dottore. L’apprendimento della nuova tecnologia GWT è stato sicuramente raggiunto, anche grazie alle svariate documentazioni presenti sul web. C’è anche da dire che a rendere difficile e poco immediato l’utilizzo di questo framework ci si è messa la sua non moltissima diffusione. Cercare un portale completamente costruito tramite il GWT, come del resto lo è questo, non è cosa semplice: spesso le soluzioni trovate includevano altre tecnologie ausiliarie. Un altro fattore che ha reso difficile raggiungere una certa dimestichezza con il GWT, soprattutto per chi come me magari proveniva dall’utilizzo di tecnologie più “classiche” come possono 100 Capitolo 6 – Conclusioni essere il PHP o l’ASP, è stato senza dubbio l’obbligatorio utilizzo delle RPC. Per quanto riguarda invece la stesura e la finalizzazione del portale, posso essere senz’altro soddisfatto del prodotto ottenuto in quanto realizza a pieno le specifiche richieste, sia dal punto di vista tecnologico-universitario, sia dal punto di vista della telemedicina. L’obiettivo universitario è stato precedentemente affrontato, mentre per quanto riguarda quello medico, si può senz’altro dire che il portale rispecchia tutte quelle caratteristiche e quei valori imposti dalla telemedicina: la semplicità e la possibilità di utilizzo da parte di chiunque è stata alla base della creazione di tutte le pagine realizzate. 6.2 Problemi riscontrati Qui vengono ora presentati i problemi incontrati durante la realizzazione del portale. Alcuni sono stati già presentati nelle singole pagine della cartella clinica, quindi qui si cercherà di dare una visione generale di tutti i problemi affrontati dal nostro gruppo di lavoro. I problemi iniziali sono avvenuti soprattutto nel realizzare la comunicazione lato client e server, nonché il prelevamento o la manipolazione dei dati nel database. Infatti, come sopra detto, le RPC inizialmente realizzate non sempre esaudivano le nostre richieste: questo è accaduto soprattutto per l’utilizzo del meccanismo di passaggio del controllo server-client tramite il metodo onSuccess() dell’oggetto AsyncCallback. Altre difficoltà si sono senz’altro incontrate nell’effettuare l’upload di un file nel server. Questo non deriva da problemi legati al GWT, ma 101 Capitolo 6 – Conclusioni alla gestione dei permessi necessari in determinate cartelle e concessi solamente a determinati utenti: ci siamo quindi creati un utente con i giusti permessi necessari alla risoluzione del problema. 6.3 Miglioramenti Possibili Nella creazione di questo portale, purtroppo, essendo alle prime armi nel costruire un portale per la telemedicina e nell’utilizzo del workspace GWT, devo riconoscere che magari alcune parti del portale possono essere migliorate. Naturalmente anche il fatto di lavorare in team ha provato alcuni svantaggi in questo senso, a causa magari di incomprensioni e via dicendo. In questa sezione verranno affrontati i possibili miglioramenti che si potrebbero sviluppare in futuro. Partiamo inizialmente da com’è gestita la parte che permette al dottore ed al requester di interagire con i problemi clinici del paziente. Questa parte a mio parere è stata creata non pensando molto alla sua usabilità. Per molti di noi che utilizzano spesso il computer può rimanere semplice, ma per coloro a cui è destinato tale software e i quali non sempre hanno esperienze in tal senso, magari questa sezione potrebbe risultare abbastanza complicata da utilizzare. Magari era meglio affrontare lo stesso problema come con le altre parti della cartella clinica, ovvero tramite una toolbar con le varie opzioni presenti. Questo sarebbe senz’altro più simile ad altri software, magari conosciuti dall’utilizzatore, ed avrebbe risparmiato il tempo di apprendimento. Un’altra situazione che poteva magari essere meglio gestita è quella relativa alla gestione dei messaggi. Infatti non è stata prevista la risposta 102 Capitolo 6 – Conclusioni da parte del requester. Questo secondo la mia opinione dovrebbe essere possibile, in quanto magari allo stesso requester viene chiesto di fornire informazioni di natura diversa di quelle inseribili: la comunicazione tra le due parti dovrebbe sempre essere la più ampia e semplice possibile. Sempre relativo alla gestione dei messaggi, ma anche per quanto riguarda i referti, nel nostro portale manca la possibilità di cancellare quest’ultimi. Dovrebbe essere creato un cestino che favorisca questo meccanismo, in cui magari vengono posizionati, a discrezione dell’utente, i report non più interessanti. Successivamente si potrebbe impostare una rimozione automatica basata sulla permanenza nel cestino, come del resto accade per molte caselle di posta elettronica. Tutto questo eviterebbe il possibile sovraccarico, con conseguente lentezza, del server. Un altro aspetto importante che non è stato implementato nel portale è stato quello gestire temporalmente tutte le modifiche. Questo significava creare un portale che mappasse temporalmente tutte le modifiche effettuate dal requester. Per ogni modifica verrebbe quindi memorizzata ora e giorno in cui viene effettuata, così da sapere magari se un report è effettuato prima o dopo che la modifica è stata fatta. Anche questo meccanismo fornirebbe una maggior chiarezza e trasparenza nelle informazioni trasmesse. Un altro difetto, ma questo non è stato purtroppo non è dipeso da noi, resta il fatto della compatibilità tra i vari browser. Infatti, anche se questa è data al 100% con tutti i browser, non è del tutto esatto. Il diverso comportamento dei vari browser ci ha costretto alla creazione di diverse procedure in più: per esempio nell’aggiornare l’albero asincrono è stato dovuto creare un metodo apposito per renderlo compatibile con Internet Explorer, altrimenti l’aggiornamento non risultava dinamico. 103 Capitolo 6 – Conclusioni Queste piccole accortezze hanno rallentato di molto il nostro lavoro in fase finale in quanto, per ottenere un buon prodotto, questo deve senza dubbio funzionare su qualunque browser. Un possibile miglioramento, che però stravolgerebbe di non poco la struttura del portale, sarebbe quello di dare la possibilità anche di effettuare telemedicina in real-time, ovvero tramite video conferenze e simili. Questo senz’altro amplierebbe gli orizzonti di utilizzo del nostro portale. Il lavoro per apportare tale modifica risulterebbe senz’altro molto notevole, ma aumenterebbe di gran lunga la possibilità di offrire una telemedicina completa ed adatta ad ogni situazione. Tale miglioramento ovviamente includerebbe anche l’utilizzo di altre tecnologie informatiche e aumenterebbe purtroppo i costi. 6.4 Confronti con altre tecnologie In questa sezione verrà effettuato il confronto tra la tecnologia utilizzata nello sviluppo, ovvero il GWT, ed altre tecnologie da me conosciute come il PHP e il JSP. Partiamo con l’analisi di confronto con il PHP. Il PHP è un linguaggio di scripting interpretato che riprende per molti versi le sintassi del C e del Perl e dalla versione 5 migliora il supporto al paradigma ad oggetti. Dopo questa breve introduzione verranno fatte notare le differenze. Il GWT per la visualizzazione utilizza dei widget che permettono di rendere all’utente di creare interfacce graficamente complesse, in modo semplice e rapido. Il PHP per la visualizzazione lato client non ha particolari accorgimenti, in quanto utilizza i tag HTML così come sono. 104 Capitolo 6 – Conclusioni Per quanto riguarda la gestione del lato server invece a mio avviso il PHP è più semplice ed intuitivo in quanto non necessita di chiamate remote particolari e viene tutto eseguito in quella pagina: l’integrazione con il database, MySQL in particolare, è immediata e più rapida di quanto non lo sia in GWT. Infatti in GWT per reperire un dato dal database, il meccanismo che vi sta dietro è senza dubbio più complicato perché si basa su delle RPC. Soprattutto la gestione dei dati di ritorno e l’utilizzo di essi nel solo metodo onSuccess() del client, rende questa tecnologia forse un po’ troppo macchinosa. Infatti è proprio questa concezione di netta separazione tra client e server presente nel GWT che lo rende molto differente dal PHP. Un altro punto che gioca a favore del PHP è quello della velocità. Infatti, a meno che non si stia usando un browser di ultima generazione ed una connessione ad internet veloce, con il GWT, a causa della presenza di complesse interfacce lato client, il caricamento iniziale della pagina è molto più lento di una pagina in PHP. A favore del GWT c’è però da dire che caricata la pagina, il successivo scambio delle “viste” è molto più rapido, perché in realtà si svolge all’interno della stessa pagina tramite procedure AJAX e JavaScript. Ricapitolando le due tecnologie hanno alcuni punti a loro vantaggio ed altri a loro svantaggio. Si può quasi dire che si completano. In molte guide trovate sul web, una cosa che ho felicemente notato, è che il GWT può tranquillamente essere compatibile con l’utilizzo di altre tecnologie. Infatti l’utilizzo del PHP assieme al GWT è molto utilizzato perché appunto tende a sfruttare solamente i vantaggi delle due: il GWT viene utilizzato per impostare la parte grafica così da avere un’interfaccia senza dubbio molto più innovativa; il PHP viene utilizzato nelle procedure lato server per interfacciarsi al database. 105 Capitolo 6 – Conclusioni C’è da dire che comunque i risultati che si ottengono dall’utilizzo della tecnologia di Google sono molto importanti e vertono verso una nuova concezione di sito web, sempre più simile ad un’applicazione. Sicuramente essa potrà essere una tecnologia innovativa per il futuro e grazie al suo continuo miglioramento potrà senza dubbio anche ampliare la sua diffusione nel presente. Per quanto riguarda il confronto con il JSP posso dire che le due tecnologie non differiscono di molto nei meccanismo che utilizzano. Lato client c’è la stessa differenza che è stata evidenziata con il PHP, ovvero l’enorme potenzialità in più offerta da GWT tramite i suoi widget. Per quanto riguarda il lato server invece le due tecnologie si assomigliano. È vero che non vengono effettuate RPC nella stessa maniera che del GWT, ma i servlet utilizzati nel JSP in realtà sono gli stessi. Solamente la concezione di serializzazione dei dati non è così rigida, ma per il resto le procedure interne del servlet sono esattamente le stesse. Si può quindi affermare che non esiste, a differenza del PHP un reale vantaggio del JSP rispetta al GWT. Da far notare infatti come questa tecnologia sia un po’ in disuso e man mano sia superata da altre tecnologie, quali il GWT. In conclusione possiamo dire che il vero punto di forza del GWT sono la creazione di interfacce molto belle graficamente quanto funzionali. Come punto a suo sfavore giocano invece i suoi “complessi” meccanismi, rispetto ad altre tecnologie, di gestire il lato server. Questi meccanismo però, possono anche essere semplificati dall’utilizzo di 106 Capitolo 6 – Conclusioni altre tecnologie ausiliarie. Il risultato è che il GWT, generando codice JavaScript, si rende compatibile anche ad essere interfacciato con altri linguaggi. 6.5 Confronto con “Miro On Rails” Innanzitutto bisogna spiegare che cosa sia il Miro On Rails. Il Miro è il vecchio portale precedente a questo, creato per offrire il medesimo servizio, ma basato su tecnologia Ruby On Rails: da qui il nome Miro On Rails. Il risultato ottenuto è profondamente differente. Non cercherò di giudicare la differenza tra le due tecnologie utilizzate in quanto non ho le basi necessarie del Ruby On Rails per argomentarle, ma verrà affrontato il discorso basandosi puramente sul risultato finale. Analizzando il prodotto finale quello che salta subito all’occhio è la notevole differenza nelle interfacce grafiche. Come per gli altri linguaggi prima confrontati, anche qui il GWT non ha paragoni e offre senza dubbio un servizio migliore. Anche qui la cartella clinica viene gestita tramite dei tab, ma certamente l’utilizzo non è così immediato come quello del nostro portale. Infatti la grafica qui presente è molto minimale. C’è da dire a favore di questo portale che, essendo così scarno graficamente, sono presenti solamente le cose essenziali ed il sito perciò non risulta appesantito. Al di la delle singole soluzioni utilizzate, nel totale il portale è stato concepito in modo del tutto differente. Infatti nel nostro progetto abbiamo scelto di rendere sempre visibili più informazioni possibili, utilizzando il concetto di Window presente in GWT: tutti i dati vengono aperti in un pop-up e anche se sembra banale, le informazioni sottostanti 107 Capitolo 6 – Conclusioni possono sempre essere controllate. Nel portale Miro invece questo è ovviamente impossibile, in quanto ogni pagina è isolata e necessita di un caricamento a parte. Per quanto riguarda la cartella clinica invece la differenza è ancora più profonda. Entrando nella cartella clinica da noi sviluppata, l’utente loggato si trova subito davanti una struttura semplice ed immediata offerta dall’albero laterale, che lo tiene sempre aggiornato riguardo le patologie presenti. Nel Miro questo colpo d’occhio iniziale viene meno in quanto in realtà non c’è una netta divisione tra cartella clinica e resto dei dati. Un confronto a livello grafico può essere effettuato da queste due immagini (fig. 6.1 e fig 6.2). Figura 6.1 108 Capitolo 6 – Conclusioni Figura 6.2 La tecnologia del Miro risulta senza dubbio più accessibile a chi possiede una connessione di banda limitata o lenta. Nel caso del GWT il lato client è molto appesantito da tutti i widget presenti e solamente grazie alla tecnologia AJAX questo è meno sentito. Con il Ruby On Rails invece questo non accade e l’utente difficilmente ha problemi di banda. Quindi a parte la grafica in realtà nei meccanismi i due portali si equivalgono. Infatti hanno lo stesso database, con applicate alcune modifiche in più dovute alle maggiori opzioni che sono state sviluppate nel nuovo progetto. Sono state apportate modifiche riguardo ai dati dei pazienti e degli utenti: non era, ad esempio, realizzata la possibilità di modifica dei dati del paziente nonché dell’utente. Ci si discosta molto quindi dal rendere questo portale simile ad un’applicazione normale. Un'altra netta differenza è anche quella riguardante la gestione dei permessi: nel Miro è suddivisa su di tre livelli, ovvero amministratore, requester e dottore; nel MedTel invece è suddivisa in quattro livelli gerarchici, ovvero con in più la figura intermediaria del manager e la 109 Capitolo 6 – Conclusioni scelta del super amministratore univoco. Questa scelta amministrativa a mio avviso rende la gestione di gran lunga più efficace ed efficiente, soprattutto in previsione di una possibile diffusione. 6.6 Considerazioni finali sul tirocinio Nel trarre conclusioni sul tirocinio svolto posso ritenermi senz’altro molto soddisfatto della scelta effettuata. Sebbene il lavoro svolto è stato molto impegnativo ritengo che il prodotto creato sia di buon livello. A livello personale inoltre ho rafforzato le mie attitudini al lavoro in team, cosa che ritengo molto importante soprattutto nell’ottica di un futuro lavoro da ingegnere informatico. Inoltre ho potuto imparare a conoscere ed usare una nuova tecnologia come il GWT, approfondendo tra l’altro il Java da me già conosciuto. Mi sono quindi reso conto dell’importanza di internet come fonte infinita di informazioni. Ho imparato anche a reperire meglio le informazioni nel web, in quanto senza di esse non si poteva procedere: la ricerca ininterrotta di informazioni su internet, specialmente se non presenti nella propria lingua, può essere abbastanza problematica e allungare i tempi di lavoro. Trovare i giusti risultati tra le molteplici pagine web ha espanso la mia conoscenza. Tutta questa esperienza ha ampliato senza dubbio le mie esperienze nell’ambito del web designer e nella gestione di software per internet. La possibilità di confrontarsi su temi attuali e soddisfare problemi reali ha reso sicuramente questo tirocinio molto interessante: il confronto con gli ingegneri della ASUR, soprattutto con l’ing. Giampieri, mi ha senza dubbio fornito un giusto metodo di lavoro. Posso quindi concludere 110 Capitolo 6 – Conclusioni dicendo di avere perfezionato, grazie a questa mia esperienza, la mia metodologia nell’affrontare i problemi che mi si pongono di fronte. In particolar modo vorrei citare i tirocinanti Ausili Andrea, Pongetti Francesco, Prencipe Giuseppe e Torelli Luca che hanno collaborato con me alla realizzazione di questo software e con cui ho affrontato e risolto i problemi incontrati. Un ringraziamento va anche al prof. Dragoni che è sempre stato disponibile per ogni nostro dubbio e per ogni nostro chiarimento. In conclusione quindi ringrazio tutte le persone che mi hanno fornito un supporto e hanno contribuito alla mia formazione durante questo tirocinio. 111 APPENDICE – CODICE SORGENTE Vengono qui presentate quattro classi, due relative alla clinical folder e due relative alla sezione dell’allergia. Sono state scelte queste classi perché le prime racchiudono tutti i meccanismi della clinical folder, mentre le seconde sono da esempio per tutte le sezioni interne dell’anamnesi della stessa cartella clinica. Le classi sono complete e contengono tutto il codice Java: si è scelto di inserire tutte le righe del codice sorgente, non solo delle porzioni, in quanto si è voluto dare una visione completa dei meccanismi del GWT. Ovviamente ognuna delle due classi è fornita del codice lato client e lato server. Ecco qui presente il codice sorgente: Clinical Folder – Lato client public class ClinicalFolder extends Composite { /* * * VARIABILI PUBLIC UTILIZZATE ANCHE NEI METODI ESTERNI * */ public String type=new String(); public String idGenitore=new String(); public String idFiglio=new String(); public AsyncTreeNode anamnesis, anamnesis2; public TabPanel tabPanel = new TabPanel(); public Menu rightClickMenu; final TreePanel treePanel; final Window window; final TreeNode root; public TreeNode profile,problems; final TabPanel tabArea; public String id_event, patient, id_patient; /* * COSTRUTTORE GRAFICO */ 112 Appendice – Codice Sorgente public ClinicalFolder(Window window, final TabPanel tabArea) { this.tabArea = tabArea; this.window=window; final AbsolutePanel absolutePanel = new AbsolutePanel(); initWidget(absolutePanel); //propriety tabPanel tabPanel.setResizeTabs(true); tabPanel.setMinTabWidth(100); tabPanel.setTabWidth(130); tabPanel.setEnableTabScroll(true); tabPanel.setWidth(450); tabPanel.setHeight(250); tabPanel.addListener(new TabPanelListenerAdapter() { public void onContextMenu(TabPanel source, Panel tab, EventObject e) { source.activate(tab.getId()); showRightMenu(tab, e); } }); //tree panel treePanel = new TreePanel(); treePanel.setWidth(230); treePanel.setAnimate(true); treePanel.setEnableDD(false); treePanel.setContainerScroll(true); treePanel.setRootVisible(true); treePanel.setCollapsible(true); treePanel.setAutoScroll(true); treePanel.setSelectionModel(new MultiSelectionModel()); treePanel.setTitle(""); treePanel.expandAll(); root = new TreeNode("Clinical Folder"); root.setSingleClickExpand(true); root.setIcon("books.gif"); treePanel.setRootNode(root); //padre di tutti //figli ----------------------------/* * PATIENT PROFILE * */ profile = new TreeNode("Patient Profile"); profile.setTooltip("Patient Profile"); profile.setIcon("medicon1.png"); profile.setId("patientProfile"); profile.addListener(new TreeNodeListenerAdapter(){ public void onClick( Node node, EventObject e) { if (!tabPanel.hasItem("patientProfile")){ //tab del ramo Patient Profile final Panel tabPatientProfile = new Panel(); 113 Appendice – Codice Sorgente tabPatientProfile.setTitle("Patient Profile"); tabPatientProfile.setId("patientProfile"); tabPatientProfile.setBaseCls("gwt-profile"); tabPatientProfile.setAutoScroll(true); tabPatientProfile.setClosable(true); Toolbar toolbar=createPatientToolbar(); if (type.equalsIgnoreCase("requester")) tabPatientProfile.setTopToolbar(toolbar); tabPatientProfile.add(new PatientProfile()); tabPanel.add(tabPatientProfile); //aggiunge il pannello "tab1" al TabPanel "tabPanel" tabPanel.activate("patientProfile"); } else{ tabPanel.activate("patientProfile"); } } }); root.appendChild(profile); //tab del ramo Patient Profile final Panel tabPatientProfile = new Panel(); tabPatientProfile.setTitle("Patient Profile"); tabPatientProfile.setId("patientProfile"); tabPatientProfile.setBaseCls("gwt-profile"); tabPatientProfile.setAutoScroll(true); tabPatientProfile.setClosable(true); Toolbar toolbar=createPatientToolbar(); tabPatientProfile.setTopToolbar(toolbar); tabPatientProfile.add(new PatientProfile()); tabPanel.add(tabPatientProfile); //aggiunge il pannello "tab1" al TabPanel "tabPanel" tabPanel.activate("patientProfile"); /* * ANAMNESIS * * albero dinamico * */ final XMLTreeLoader loader = new XMLTreeLoader(); loader.setDataUrl("XmlAnamnesis"); loader.setMethod(Connection.POST); loader.setRootTag("anamnesis"); loader.setQtipMapping("@qtip"); loader.setIconMapping("@icon"); loader.setFolderIdMapping("@id"); loader.setFolderTitleMapping("@title"); loader.setFolderTag("element"); loader.setLeafIdMapping("@id"); loader.setLeafTitleMapping("@title"); loader.setLeafTag("subelement"); anamnesis = new AsyncTreeNode("Anamnesis", loader); 114 Appendice – Codice Sorgente root.appendChild(anamnesis); anamnesis.setSingleClickExpand(true); anamnesis.setIcon("anamnesis.gif"); /* * refresh dell'albero */ treePanel.addTool(new Tool(Tool.REFRESH, new Function() { public void execute() { // MultiSelectionModel selectionModel = (MultiSelectionModel) treePanel.getSelectionModel(); //TreeNode[] nodes = selectionModel.getSelectedNodes(); treePanel.getEl().mask("Loading", "x-maskloading"); treePanel.expandAll(); anamnesis.reload(); reloadTree(); anamnesis.collapse(true, false); //TreeNode nodeSelected=nodes[0]; Timer timer = new Timer() { public void run() { treePanel.getEl().unmask(); anamnesis.expand(true, true); } }; timer.schedule(1000); // nodeSelected.select(); } }, "Refresh")); /* * * PATIENT CLINICAL PROBLEMS * * */ problems = new TreeNode("Patient Clinical Problems"); problems.setIcon("medicon2.png"); problems.setId("clinicalProblem"); problems.addListener(new TreeNodeListenerAdapter() { public void onClick(Node node, EventObject event) { if (!tabPanel.hasItem("clinicalProblem")){ //tab del ramo Patient Profile final Panel tabClinicalProblem = new Panel(); tabClinicalProblem.setTitle("Patient Clinical Problems"); tabClinicalProblem.setId("clinicalProblem"); tabClinicalProblem.setBaseCls("gwt-problems"); tabClinicalProblem.setAutoScroll(true); tabClinicalProblem.setClosable(true); //controlla il tipo di utente loggato if (type.equalsIgnoreCase("requester")) tabClinicalProblem.add(new PatientClinicalProblem()); 115 Appendice – Codice Sorgente else tabClinicalProblem.add(new ViewVisitsEvent(id_event,patient,id_patient)); tabPanel.add(tabClinicalProblem); //aggiunge il pannello "tab1" al TabPanel "tabPanel" tabPanel.activate("clinicalProblem"); } else{ tabPanel.activate("clinicalProblem"); } } }); root.appendChild(problems); /* * layout per la visualizzazione * */ BorderLayoutData centerData = new BorderLayoutData(RegionPosition.CENTER); centerData.setMargins(3, 0, 3, 3); BorderLayoutData westData = new BorderLayoutData(RegionPosition.WEST); westData.setSplit(true); westData.setMargins(3, 3, 3, 3); westData.setCMargins(0, 2, 0, 0); /* * * Costruzione della finestra * */ //tutta la finestra // window = new Window(); window.setTitle(" Patient Clinical Folder"); window.setClosable(true); window.setWidth(1049); window.setHeight(530); window.setPlain(true); window.setLayout(new BorderLayout()); window.add(tabPanel, centerData); //aggiunge tabPanel che il contenitore di visualizzazione window.add(treePanel, westData); window.setCloseAction(Window.CLOSE); window.setMaximizable(true); window.setIconCls("image"); window.setModal(true); /* * Tool per visualizzare una window per l'help */ window.addTool(new Tool(Tool.HELP, new Function() { public void execute() { Panel windowPanel=new Panel(); windowPanel.setHtml(getHelp()); Window windowHelp = new Window(); windowHelp.setTitle("Help"); 116 Appendice – Codice Sorgente windowHelp.setMaximizable(false); windowPanel.setPaddings(5); windowHelp.setResizable(false); windowHelp.setWidth(500); windowHelp.setModal(true); windowHelp.add(windowPanel); windowHelp.show(); } }, "HELP")); window.show(); window.maximize(); /* * Listener che viene richiamato ad ogni click * su qualsiasi nodo dell'albero * */ treePanel.addListener(new TreePanelListenerAdapter(){ public void onClick(final TreeNode node, EventObject e) { // System.out.println(node.getText()+" with idFiglio= "+node.getId()+" and idGenitore= "+node.getParentNode().getId()); /* * id del padre ------ da utilizzare in presenza di figli per aprire pagine */ idGenitore=node.getParentNode().getId(); /* * id del figlio ------ da utilizzare quando nn ci sono figli */ idFiglio=node.getId(); System.out.println("idFiglio nuovo="+idFiglio); /* * PADRI -- VERIFICA ID TRAMITE "idFiglio" * perche senza genitori con id valido * */ if (idFiglio.equals("familiars")){ addTab(node); //tab.add(new Familiars()); } if (idFiglio.equals("allergologies")){ addTab(node); // tab.add(new Allergology()); } if (idFiglio.equals("family_pathologies")){ addTab(node); //tab.add(new Allergology()); 117 Appendice – Codice Sorgente } if (idFiglio.equals("pathologicals")){ addTab(node); // tab.add(new Pathologicals()); } if (idFiglio.equals("physiologicals")){ addTab(node); //tab.add(new Phisiological()); } /* * * FIGLI --* padre con le info generiche * * */ VERIFICA ID TRAMITE "idGenitore" perche tramite apro la pagina del poi rese specifiche dalla query SQL //VERIFICA ID FIGLI e SETTA L'ID SULLA SESSIONE if (idGenitore.equals("allergologies")){ setIdSelected(node); //addTabChild(node); } if (idGenitore.equals("family_pathologies")){ setIdSelected(node); // addTabChild(node); //selected.setId(idFiglio.substring(1), callback); } if (idGenitore.equals("pathologicals")){ setIdSelected(node); // addTabChild(node); } } }); /* * RPC CHE AVVIENE AL CARICAMENTO * VISUALIZZA IL NOME * */ ClinicalFolderRSAsync view = (ClinicalFolderRSAsync) GWT.create(ClinicalFolderRS.class); //crea il servizo ServiceDefTarget endpoint = (ServiceDefTarget) view; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "ClinicalFolderRS"); AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { ArrayList values=(ArrayList) result; treePanel.setTitle("Patient: "+values.get(0).toString().toUpperCase()); type=values.get(1).toString(); if (!type.equalsIgnoreCase("requester")) 118 Appendice – Codice Sorgente tabPatientProfile.getTopToolbar().destroy(); } public void onFailure(Throwable caught) { System.out.println("errore nella view"); } }; view.view(callback); /* * AGGIORNA LA GRIGLIA */ this.window.addListener(new WindowListenerAdapter() { public void onClose(Panel panel) { String idActive=tabArea.getActiveTab().getId(); tabArea.remove("lastclinical"); ViewEventsRequest env; EventsPatients env1; Panel pan = new Panel(); Panel lastP; pan.setBorder(false); pan.setPaddings(0); pan.setLayout(new HorizontalLayout(0)); if (type.equalsIgnoreCase("requester")){ env = new ViewEventsRequest(tabArea); pan.add(env); lastP = new Panel(); lastP.setTitle("Last Clinical Problem"); } else{ env1=new EventsPatients(tabArea); pan.add(env1); lastP = new Panel(); lastP.setTitle("Clinical Problems"); } lastP.setPaddings(0); lastP.setId("lastclinical"); lastP.setSize(709, 600); lastP.setAutoScroll(true); lastP.add(pan); tabArea.add(lastP); tabArea.setActiveTab(idActive); } }); } /* * * METODI ESTERNI ----------------------------------* 119 Appendice – Codice Sorgente * */ /* * METODO * * * */ PER L'AGGIUNTA DI UN TAB "PADRE" public Panel addTab(final TreeNode node){ if (!tabPanel.hasItem(idFiglio)){ //CONTROLLA SE GIA ESISTE QUEL TAB Panel tab=new Panel(); tab.setBaseCls("gwt-el"); tab.addListener(new PanelListenerAdapter(){ public void onActivate(Panel tab) { //quando attivo il pannello AnamnesisView.setLabel(); node.select(); MultiSelectionModel selectionModel = (MultiSelectionModel) treePanel.getSelectionModel(); TreeNode[] nodes = selectionModel.getSelectedNodes(); idFiglio=nodes[0].getId(); System.out.println("idFiglio nell'addTab="+idFiglio); } }); Toolbar toolbar=createToolbar(); if (type.equalsIgnoreCase("requester")) tab.setTopToolbar(toolbar); //aggiunge un pulsante per il refresh if (idFiglio.equals("allergologies")|| idFiglio.equals("family_pathologies")|| idFiglio.equals("pathologicals")){ Button buttonRefresh=new Button("Refresh"); buttonRefresh.setIcon("refresh.gif"); buttonRefresh.addListener(new ButtonListenerAdapter() { public void onClick(final Button button, final EventObject e) { refreshPage(); } }); tab.addButton(buttonRefresh); } tab.setTitle(node.getText()); tab.setClosable(true); tab.setId(idFiglio); tab.setAutoScroll(true); tabPanel.add(tab); selectCompositeView(idFiglio,tab); 120 Appendice – Codice Sorgente return tab; } else { tabPanel.setActiveItemID(idFiglio); return null; } } /* * METODO PER L'AGGIUNTA DI UN TAB * * "FIGLIO" * */ public Panel addTabChild(final TreeNode node){ if (!tabPanel.hasItem(idGenitore.substring(0, 6)+""+idFiglio)){ Panel tab=new Panel(); tab.setBaseCls("gwt-sub"); tab.addListener(new PanelListenerAdapter(){ public void onActivate(Panel panel) { //quando attivo il pannello node.select(); MultiSelectionModel selectionModel = (MultiSelectionModel) treePanel.getSelectionModel(); TreeNode[] nodes = selectionModel.getSelectedNodes(); idFiglio=nodes[0].getId(); setIdSelected(); System.out.println("idFiglio nell'addTabChild="+idFiglio); } }); Toolbar toolbar=createToolbar(); if (type.equalsIgnoreCase("requester")) tab.setTopToolbar(toolbar); tab.setTitle(node.getText()); tab.setClosable(true); tab.setId(idGenitore.substring(0, 6)+""+idFiglio); selectCompositeChild(idGenitore,tab); tab.setAutoScroll(true); tabPanel.add(tab); tabPanel.activate(idGenitore.substring(0, 6)+""+idFiglio); return tab; } else { tabPanel.setActiveItemID(idGenitore.substring(0, 6)+""+idFiglio); return null; } } /* 121 Appendice – Codice Sorgente * METODO PER LA SCELTA DELLA PAGINA DA VISUALIZZARE * * "PADRE" * */ public void selectCompositeView(final String name,final Panel panel){ if(name.equals("familiars") || name.equals("physiologicals")){ AnamnesisViewRSAsync tab = (AnamnesisViewRSAsync) GWT.create(AnamnesisViewRS.class); //crea il servizo ServiceDefTarget endpoint = (ServiceDefTarget) tab; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "AnamnesisViewRS"); AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { ArrayList rows=(ArrayList) result; if (!rows.get(0).toString().equals("0")){ if (name.equals("familiars")) panel.add(new FamiliarsView(ClinicalFolder.this)); if (name.equals("physiologicals")) panel.add(new PhisiologicalView()); } else panel.add(new AnamnesisView(ClinicalFolder.this)); tabPanel.activate(idFiglio); } public void onFailure(Throwable caught) { MessageBox.alert("ERROR"); } }; tab.view(name, callback); } /*if (name.equals("familiars")){ panel.add(new FamiliarsView(ClinicalFolder.this)); }*/ if (name.equals("allergologies")){ panel.add(new AnamnesisView(ClinicalFolder.this)); tabPanel.activate(idFiglio); } if (name.equals("pathologicals")){ panel.add(new AnamnesisView(ClinicalFolder.this)); tabPanel.activate(idFiglio); } /* if (name.equals("physiologicals")){ panel.add(new PhisiologicalView()); }*/ if (name.equals("family_pathologies")){ panel.add(new AnamnesisView(ClinicalFolder.this)); 122 Appendice – Codice Sorgente tabPanel.activate(idFiglio); } } /* * METODO * * * */ PER LA SCELTA DELLA PAGINA DA VISUALIZZARE "FIGLI" private void selectCompositeChild(String id,Panel panel){ if (id.equals("allergologies")){ panel.add(new AllergologyView(ClinicalFolder.this)); } if (id.equals("family_pathologies")){ panel.add(new Family()); } if (id.equals("pathologicals")){ panel.add(new PathologicalsView()); } } /* * METODO PER L'AGGIORNAMENTO DELLE PAGINE "MODIFICATE" * * */ public void updateTab(Panel tab,Panel newTab){ String id=tab.getId(); //non l'id del db newTab.setId(id); System.out.println("id nell'update="+idFiglio); //idFiglio=id.substring(6,id.length()); tabPanel.remove(tab.getId()); tabPanel.add(newTab); tabPanel.activate(newTab.getId()); tabPanel.setActiveItemID(newTab.getId()); tabPanel.setActiveTab(newTab.getId()); } /* * * METODO PER LA RICARICA DELL'ALBERO ASYNC * */ public void reloadTree(){ //anamnesis.reload(); anamnesis.remove(); anamnesis.destroy(); //root.removeChild(anamnesis); final XMLTreeLoader loader2 = new XMLTreeLoader(); loader2.setDataUrl("XmlAnamnesis"); loader2.setMethod(Connection.POST); loader2.setRootTag("anamnesis"); loader2.setQtipMapping("@qtip"); loader2.setIconMapping("@icon"); loader2.setFolderIdMapping("@id"); loader2.setFolderTitleMapping("@title"); 123 Appendice – Codice Sorgente loader2.setFolderTag("element"); loader2.setLeafIdMapping("@id"); loader2.setLeafTitleMapping("@title"); loader2.setLeafTag("subelement"); anamnesis = new AsyncTreeNode("Anamnesis", loader2); anamnesis.setSingleClickExpand(true); anamnesis.setIcon("anamnesis.gif"); root.insertBefore(anamnesis, problems); treePanel.expandAll(); } /* * * METODO PER L'AGGIUNTA DELLA TOOLBAR * */ public Toolbar createToolbar(){ Toolbar toolbar = new Toolbar(); final Menu menu = new Menu(); menu.setShadow(true); String id=idFiglio; ) { if(id.equals("familiars") || id.equals("physiologicals") //|| id.equals("physiologicals")){ /* * * Procedura con per vedere il numero di righe di familiars e physiologicals * */ AnamnesisViewRSAsync view = (AnamnesisViewRSAsync) GWT.create(AnamnesisViewRS.class); //crea il servizo ServiceDefTarget endpoint = (ServiceDefTarget) view; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "AnamnesisViewRS"); AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { ArrayList rows=(ArrayList) result; if (!rows.get(0).toString().equals("0")){ addModifyMenu(menu); menu.addSeparator(); addDeleteMenu(menu); } else { addNewMenu(menu); } } public void onFailure(Throwable caught) { 124 Appendice – Codice Sorgente MessageBox.alert("ERROR"); } }; view.view(id, callback); } //aggiungo modifica, e delete x i menu figli else if (id.substring(0, 1).equals("1") || id.substring(0, 1).equals("2") || id.substring(0, 1).equals("3")){ addModifyMenu(menu); menu.addSeparator(); addDeleteMenu(menu); } else { addNewMenu(menu); menu.addSeparator(); addRefreshMenu(menu); } ToolbarButton toolbarButton=new ToolbarButton ("Option Menu",menu); ToolTip tip = new ToolTip(); tip.setTitle("Click here<br>to view more options"); tip.setDismissDelay(15000); tip.setTrackMouse(true); tip.applyTo(toolbarButton); toolbarButton.setIcon("menu.gif"); toolbar.addFill(); toolbar.addSeparator(); toolbar.addButton(toolbarButton); toolbar.addSeparator(); toolbar.addSpacer(); return toolbar; } public Toolbar createPatientToolbar(){ Toolbar toolbar = new Toolbar(); final Menu menu = new Menu(); menu.setShadow(true); Item modify = new Item("Modify"); modify.setIcon("modify.gif"); modify.addListener(new BaseItemListenerAdapter(){ public void onClick(BaseItem item, EventObject e){ Window windowModify=new Window(); windowModify.setDraggable(true); windowModify.setPlain(true); windowModify.setModal(true); windowModify.removeAll(); windowModify.setSize("820", "640"); windowModify.add(new Patient(windowModify,ClinicalFolder.this,true,tabArea)); windowModify.show(); 125 Appendice – Codice Sorgente } }); menu.addItem(modify); ToolbarButton toolbarButton=new ToolbarButton ("Option Menu",menu); ToolTip tip = new ToolTip(); tip.setTitle("Click here<br>to view more options"); tip.setDismissDelay(15000); tip.setTrackMouse(true); tip.applyTo(toolbarButton); toolbarButton.setIcon("menu.gif"); toolbar.addFill(); toolbar.addSeparator(); toolbar.addButton(toolbarButton); toolbar.addSeparator(); toolbar.addSpacer(); return toolbar; } /* * L'ID UTILIZZATO QUI SARA: * "idGenitore" ---->PER I FIGLI !!! * "idFiglio" ---->PER I GENITORI !!!! * */ private void selectModifyWindow(Window window){ System.out.println("id nell'modifyWindow="+idFiglio); String id=idFiglio; if (id.substring(0, 1).equals("1")){ window.setSize("700", "300"); window.add(new Allergology(window,ClinicalFolder.this,true)); } if (id.equals("familiars")){ window.add(new Familiars(window,ClinicalFolder.this,true)); } if (id.equals("physiologicals")){ window.add(new Phisiological(window,ClinicalFolder.this,true)); } if (id.substring(0, 1).equals("2")){ window.setSize("700", "300"); window.add(new Pathologies(window,ClinicalFolder.this,true)); } if (id.substring(0, 1).equals("3")){ window.setSize("700", "700"); window.add(new Pathologicals(window,ClinicalFolder.this,true)); } //aggiugere tutte le finestre da aprire } private void selectNewWindow(Window window){ 126 Appendice – Codice Sorgente String id=idFiglio; if (id.equals("allergologies")){ window.add(new Allergology(window,ClinicalFolder.this,false)); } if (id.equals("familiars")){ window.add(new Familiars(window,ClinicalFolder.this,false)); } if (id.equals("family_pathologies")){ window.add(new Pathologies(window,ClinicalFolder.this,false)); } if (id.equals("physiologicals")){ window.add(new Phisiological(window,ClinicalFolder.this,false)); } if (id.equals("pathologicals")){ window.add(new Pathologicals(window,ClinicalFolder.this,false)); } //aggiugere tutte le finestre da aprire } /* * * AGGIUNGE AL MENU IL PULSANTE "NEW" * */ public void addNewMenu(Menu menu){ Item delete = new Item("New"); delete.setIcon("leaf.gif"); delete.addListener(new BaseItemListenerAdapter(){ public void onClick(BaseItem item, EventObject e){ //MessageBox.alert("new test ok"); Window windowNew=new Window(); windowNew.setDraggable(true); windowNew.setPlain(true); windowNew.setModal(true); windowNew.removeAll(); selectNewWindow(windowNew); windowNew.show(); } }); menu.addItem(delete); } /* * * AGGIUNGE AL MENU IL PULSANTE "MODIFY" * */ public void addModifyMenu(Menu menu){ 127 Appendice – Codice Sorgente Item modify = new Item("Modify"); modify.setIcon("modify.gif"); modify.addListener(new BaseItemListenerAdapter(){ public void onClick(BaseItem item, EventObject e){ Window windowModify=new Window(); windowModify.setDraggable(true); windowModify.setPlain(true); windowModify.setModal(true); windowModify.removeAll(); selectModifyWindow(windowModify); windowModify.setResizable(false); windowModify.show(); } }); menu.addItem(modify); } /* * * AGGIUNGE AL MENU IL PULSANTE "DELETE" * */ public void addDeleteMenu(Menu menu){ Item delete = new Item("Delete"); delete.setIcon("delete.gif"); delete.addListener(new BaseItemListenerAdapter(){ public void onClick(BaseItem item, EventObject e){ final ClinicalFolderRSAsync delete = (ClinicalFolderRSAsync) GWT.create(ClinicalFolderRS.class); //crea il servizo ServiceDefTarget endpoint = (ServiceDefTarget) delete; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "ClinicalFolderRS"); final AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { MessageBox.alert(tabPanel.getActiveTab().getTitle()+" deleted !!!!"); String tabIdRemove=tabPanel.getActiveTab().getId(); tabPanel.remove(tabIdRemove); anamnesis.reload(); MessageBox.alert("Deleted"); } public void onFailure(Throwable caught) { System.out.println("errore nel delete"); } 128 Appendice – Codice Sorgente }; String message="You are deleting "+tabPanel.getActiveTab().getTitle()+". Continue ?"; final String idSelected=idFiglio; MessageBox.confirm("Confirm", message, new MessageBox.ConfirmCallback() { public void execute(String btnID) { if (btnID.equals("yes")) delete.delete(idSelected,callback); } }); } }); menu.addItem(delete); } /* * * AGGIUNGE AL MENU IL PULSANTE "REFRESH" * */ public void addRefreshMenu(Menu menu){ Item refresh = new Item("Refresh"); refresh.setIcon("refresh.gif"); refresh.addListener(new BaseItemListenerAdapter(){ public void onClick(BaseItem item, EventObject e){ refreshPage(); reloadTree(); } }); menu.addItem(refresh); } /* * restituisce la pagine di visualizzazione dell'help */ public String getHelp() { String help = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1transitional.dtd\">" + "<html><head><title>HELP: Patient Clinical Folder</title></head>" + "<body><h2 align='center' style='color: red; fontsize:17px;'>WELCOME IN THE PATIENT CLINICAL FOLDER</h2>" + "<br><p>Here you can find 3 different categories: <b>PATIENT PROFILE</b>, <b>ANAMNESIS</b> and <b>PATIENT CLINICAL PROBLEMS</b>.</p><br>" + "<a href=\"tree.html\" 129 Appendice – Codice Sorgente target=\"_blank\"><b>Tree</b></a> categories:<ul>" + "<li><dt style='color: darkblue;'><a href=\"profile.html\" target=\"_blank\"><b>PATIENT PROFILE</b></a></dt><dd>It contains the patient data.</dd></li>" + "<li><dt style='color: darkblue;'><a href=\"anamnesis.html\" target=\"_blank\"><b>ANAMNESIS</b></a></dt><dd>It contains other data linked to the patient and also the patient relatives.</dd></li>" + "<li><dt style='color: darkblue;'><a "; if (type.equals("requester")){ help+= "href=\"clinical.html\" "; } else { help+= "href=\"clinicalD.html\" "; } help+="target=\"_blank\"><b>PATIENT CLINICAL PROBLEMS</b></a></dt><dd>It contains the problems which need to be sent to doctors for a report.</dd></li></ul>" + "<br><p>Do you require explanations of the buttons? <a href=\"buttons.html\" target=\"_blank\"><b>Click here</b></a>.</p>" + "</body></html>"; return help; } /* * * Mostra il menu quando si clicca con il destro sul tab * Puo effettuare la chiusura del tab o di tutti gli altri tab * */ private void showRightMenu(final Panel tab, EventObject e) { if (rightClickMenu == null) { rightClickMenu = new Menu(); Item close = new Item("Close This Tab"); close.setId("close-tab-item"); close.addListener(new BaseItemListenerAdapter() { public void onClick(BaseItem item, EventObject e) { tabPanel.remove(tabPanel.getActiveTab()); } }); rightClickMenu.addItem(close); rightClickMenu.addSeparator(); Item closeOthers = new Item("Close Others Tabs"); closeOthers.setId("close-others-item"); closeOthers.addListener(new BaseItemListenerAdapter() { public void onClick(BaseItem item, EventObject e) { Component[] items = tabPanel.getItems(); for (int i = 0; i < items.length; i++) { Component component = items[i]; if (!component.getId().equals(tabPanel.getActiveTab().getId())) { tabPanel.remove(component); 130 Appendice – Codice Sorgente } } } }); rightClickMenu.addItem(closeOthers); rightClickMenu.addSeparator(); Item closeAll = new Item("Close All tabs"); closeAll.setId("close-all-item"); closeAll.addListener(new BaseItemListenerAdapter() { public void onClick(BaseItem item, EventObject e) { MessageBox.confirm("Confirm", "Close all tabs:<br>are you sure ?", new MessageBox.ConfirmCallback() { public void execute(String btnID) { if (btnID.equals("yes")) tabPanel.removeAll(true); //tabPanel.removeAll(); } }); } }); rightClickMenu.addItem(closeAll); } BaseItem closeOthers = rightClickMenu.getItem("close-others-item"); if (tabPanel.getItems().length == 1) { closeOthers.disable(); } else { closeOthers.enable(); } BaseItem closeAll = rightClickMenu.getItem("close-all-item"); if (tabPanel.getItems().length == 1) { closeAll.disable(); } else { closeAll.enable(); } rightClickMenu.showAt(e.getXY()); } /* * metodo per settare id del nodo/tab selezionato tramite il click sull'albero * */ public void setIdSelected(final TreeNode node){ ClinicalFolderRSAsync selected = (ClinicalFolderRSAsync) GWT.create(ClinicalFolderRS.class); //crea il servizo ServiceDefTarget endpoint = (ServiceDefTarget) selected; 131 Appendice – Codice Sorgente endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "ClinicalFolderRS"); AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { addTabChild(node); //aggiunge un tab figlio } public void onFailure(Throwable caught) { MessageBox.alert("error"); } }; //VERIFICA ID FIGLI e SETTA L'ID SULLA SESSIONE selected.setId(idFiglio.substring(1), callback); } /* * metodo per settare id del nodo/tab selezionato * */ public void setIdSelected(){ ClinicalFolderRSAsync selected = (ClinicalFolderRSAsync) GWT.create(ClinicalFolderRS.class); //crea il servizo ServiceDefTarget endpoint = (ServiceDefTarget) selected; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "ClinicalFolderRS"); AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { //addTabChild(); //aggiunge un tab figlio } public void onFailure(Throwable caught) { MessageBox.alert("error"); } }; //VERIFICA ID FIGLI e SETTA L'ID SULLA SESSIONE selected.setId(idFiglio.substring(1), callback); } /* * Ricarica la pagina AnamnesisView tramite un metodo contenuto in tale pagina */ public void refreshPage(){ AnamnesisView.setLabel(); window.getEl().mask("Loading", "x-mask-loading"); Timer timer = new Timer() { public void run() { 132 Appendice – Codice Sorgente window.getEl().unmask(); } }; timer.schedule(1000); } /* * SETTA L'EVENTO DA VISUALIZZARE */ public void setProblem(String type){ if (!tabPanel.hasItem("clinicalProblem")){ //tab del ramo Patient Profile final Panel tabPatientProfile = new Panel(); tabPatientProfile.setTitle("Patient Problem"); tabPatientProfile.setId("clinicalProblem"); tabPatientProfile.setBaseCls("gwt-problems"); tabPatientProfile.setAutoScroll(true); tabPatientProfile.setClosable(true); if (type.equalsIgnoreCase("requester")) tabPatientProfile.add(new PatientClinicalProblem()); else tabPatientProfile.add(new ViewVisitsEvent(id_event,patient,id_patient)); tabPanel.add(tabPatientProfile); //aggiunge il pannello "tab1" al TabPanel "tabPanel" tabPanel.activate("clinicalProblem"); } else{ tabPanel.activate("clinicalProblem"); } } /* * * SETTA L'EVENTO * */ public void setDataDoc(String id_event1, String patient1, String id_patient1) { id_event=id_event1; patient=patient1; id_patient=id_patient1; } } 133 Appendice – Codice Sorgente Clinical Folder – Lato Server public class ClinicalFolderRSImpl extends RemoteServiceServlet implements ClinicalFolderRS { public ArrayList view(){ HttpServletRequest request = this.getThreadLocalRequest(); HttpSession session = request.getSession(); PatientSession currentPatient=(PatientSession) session.getAttribute("ps"); UserSession currentUser=(UserSession)session.getAttribute("us"); String name=currentPatient.getName()+" "+currentPatient.getSurname(); String type=currentUser.getType(); ArrayList result=new ArrayList(); result.add(name); result.add(type); return result; } public void setId(String id){ HttpServletRequest request = this.getThreadLocalRequest(); HttpSession session = request.getSession(); session.setAttribute("id", id); // System.out.println("id="+session.getAttribute("id").toString()); } public void delete(String idSelected){ HttpServletRequest request = this.getThreadLocalRequest(); HttpSession session = request.getSession(); PatientSession currentPatient=(PatientSession) session.getAttribute("ps"); int patientId=currentPatient.getId(); String id=(String) session.getAttribute("id"); //System.out.println("nel delete:"+id); String table=new String(); if (idSelected.substring(0, 1).equals("1")) table="anamnesis_allergologies"; if (idSelected.substring(0, 1).equals("2")) table="anamnesis_family_pathologies"; if (idSelected.substring(0, 1).equals("3")) table="anamnesis_pathologicals"; String query="delete from "+table+" where id="+id; if (idSelected.equals("familiars")) query="delete from anamnesis_familiars where patient_id="+patientId; if (idSelected.equals("physiologicals")) query="delete from anamnesis_physiologicals where patient_id="+patientId; Database conn = new Database("mironjava","mironjava","abppt_5"); conn.connetti(); System.out.println(query); try{ 134 Appendice – Codice Sorgente conn.delete(query); } catch (Exception e) { System.out.println("errore nel delete: "+e.getMessage()+e.getCause()); } finally{ conn.closeConn(); } } } Allergology – Lato Client public class Allergology extends Composite { public ArrayList names=new ArrayList(); public int k=0; public String allergies[][]; public TextArea note; public ComboBox typology; public DateField bDate, eDate; final AbsolutePanel absolutePanel; public Allergology(final Window window, final ClinicalFolder clinicalFolder,final boolean modifyStatus) { window.setSize("700", "350"); absolutePanel = new AbsolutePanel(); initWidget(absolutePanel); absolutePanel.setSize("700", "350"); absolutePanel.setStylePrimaryName("gwt-box1"); absolutePanel.setVisible(true); final FieldSet fieldSet = new FieldSet("Allergy description"); fieldSet.setSize("641px", "100px"); fieldSet.setPaddings(10); typology = new ComboBox("Typology"); typology.setDisplayField("typology"); typology.setMode(ComboBox.LOCAL); typology.setTriggerAction(ComboBox.ALL); typology.setTypeAhead(true); typology.setSelectOnFocus(true); typology.setWidth(150); typology.setAllowBlank(false); //non puo essere vuoto typology.setForceSelection(true); 135 Appendice – Codice Sorgente fieldSet.add(typology); note = new TextArea("Note"); note.setSize("465", "100"); fieldSet.add(note); bDate = new DateField("Beginning date", "bdate", 150); bDate.setFormat("Y-m-d"); fieldSet.add(bDate); eDate = new DateField("Ending date", "edate", 150); eDate.setFormat("Y-m-d"); fieldSet.add(eDate); absolutePanel.add(fieldSet, 20, 30); final Button saveButton = new Button("Save"); absolutePanel.add(saveButton, 320, 292); saveButton.addListener(new ButtonListenerAdapter() { public void onClick(final Button button, final EventObject e) { if(modifyStatus) startModify(window,clinicalFolder); else startNew(window,clinicalFolder); } }); saveButton.setSize("100px", "20px"); startView(modifyStatus); } /* * METODI ESTERNI */ /* * MODIFICA */ public void startModify(final Window window, final ClinicalFolder clinicalFolder){ boolean dateStatus=true; if (!eDate.getValueAsString().equals("") && (eDate.getValue()).before(bDate.getValue())){ eDate.markInvalid("Date invalid:<br>ending date is before beginning date"); 136 Appendice – Codice Sorgente dateStatus=false; } final AllergologyRSAsync modify = (AllergologyRSAsync) GWT.create(AllergologyRS.class); //crea il servizo ServiceDefTarget endpoint = (ServiceDefTarget) modify; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "AllergologyRS"); final AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { Panel tab=clinicalFolder.tabPanel.getActiveTab(); Panel newTab = new Panel(); newTab.setClosable(true); newTab.setAutoScroll(true); newTab.setBaseCls("gwt-sub"); newTab.setTitle(typology.getText()); newTab.add(new AllergologyView(clinicalFolder)); Toolbar toolbar=clinicalFolder.createToolbar(); newTab.setTopToolbar(toolbar); String id=tab.getId(); clinicalFolder.idFiglio=id.substring(6,id.length()); MessageBox.alert("Changes saved successfully"); clinicalFolder.reloadTree(); //carica l'albero clinicalFolder.updateTab(tab,newTab); //aggiorna i tab window.close(); } public void onFailure(Throwable caught) { MessageBox.alert(caught.getMessage()+caught.getCause()+caught.getStack Trace()); } }; if (typology.isValid() && bDate.isValid() && eDate.isValid() && dateStatus){ MessageBox.confirm("Confirm", "Confirm changes ?", new MessageBox.ConfirmCallback() { public void execute(String btnID) { if (btnID.equals("yes")) modify.modify(typology.getText(), note.getText(), bDate.getValue(), eDate.getValue(), callback); } }); 137 Appendice – Codice Sorgente } } /* * CARICAMENTO DATI PAGINA */ public void startView(final boolean modifyStatus){ //al caricamento della pagia viene effettuata la callback AllergologyRSAsync view = (AllergologyRSAsync) GWT.create(AllergologyRS.class); //crea il servizo ServiceDefTarget endpoint = (ServiceDefTarget) view; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "AllergologyRS"); AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { ArrayList text=(ArrayList) result; int i=0; if (modifyStatus){ if (text.get(i)!=null) typology.setValue(text.get(i++).toString()); else i++; if (text.get(i)!=null) note.setValue(text.get(i++).toString()); else i++; if (text.get(i)!=null) bDate.setValue(text.get(i++).toString()); else i++; if (text.get(i)!=null) eDate.setValue(text.get(i++).toString()); else i++; } names=(ArrayList) text.get(4); k=names.size(); allergies=new String[k][1]; for (int j=0;j< k;j++){ allergies[j][0]=names.get(j).toString(); } final Store store = new SimpleStore(new String[]{"typology"}, allergies); store.load(); typology.setStore(store); } public void onFailure(Throwable caught) { MessageBox.alert(caught.getMessage()+caught.getCause()+caught.getStack Trace()); } }; view.view(callback); 138 Appendice – Codice Sorgente } /* * INSERIMENTO */ public void startNew(final Window window,final ClinicalFolder clinicalFolder){ boolean dateStatus=true; if (!eDate.getValueAsString().equals("") && (eDate.getValue()).before(bDate.getValue())){ eDate.markInvalid("Date invalid:<br>ending date is before beginning date"); dateStatus=false; } final AllergologyRSAsync insert = (AllergologyRSAsync) GWT.create(AllergologyRS.class); //crea il servizo ServiceDefTarget endpoint = (ServiceDefTarget) insert; endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "AllergologyRS"); final AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { MessageBox.alert("Inserted"); clinicalFolder.reloadTree(); //carica l'albero clinicalFolder.refreshPage(); window.close(); } public void onFailure(Throwable caught) { MessageBox.alert(caught.getMessage()+caught.getCause()+caught.getStack Trace()); } }; if (typology.isValid() && bDate.isValid() && eDate.isValid() && dateStatus){ MessageBox.confirm("Confirm", "Confirm date ?", new MessageBox.ConfirmCallback() { public void execute(String btnID) { if (btnID.equals("yes")) insert.insert(typology.getText(), note.getText(), bDate.getValue(), eDate.getValue(), callback); } }); } } } 139 Appendice – Codice Sorgente Allergology – Lato Server public class AllergologyRSImpl extends RemoteServiceServlet implements AllergologyRS { public boolean modify(String typology, String note, Date bDate, Date eDate){ HttpServletRequest request = this.getThreadLocalRequest(); HttpSession session = request.getSession(); //UserSession currentPatient=(UserSession) session.getAttribute("us"); String id=(String) session.getAttribute("id"); // deve essere settata la sessione nelle pagina di visualizzazione pazienti Database conn = new Database("mironjava","mironjava","abppt_5"); conn.connetti(); String allergyId=new String(); String query="SELECT id FROM allergies where name=\""+typology+"\";"; try{ conn.query(query); conn.getResult().first(); allergyId=conn.getResult().getString("id"); } catch (Exception e) { System.out.println("Eccezione: "+e.getMessage()); return false; } Calendar bCal=new GregorianCalendar(); Calendar eCal=new GregorianCalendar(); query="update anamnesis_allergologies set "; query+=" notes=\""+note+"\","; if (bDate!=null){ bCal.setTime(bDate); int bY=bCal.get(Calendar.YEAR); int bM=bCal.get(Calendar.MONTH)+1; int bD=bCal.get(Calendar.DATE); query+=" beginning_date=\""+bY+"-"+bM+"-"+bD+"\","; } else query+=" beginning_date=null,"; if (eDate!=null){ eCal.setTime(eDate); int eY=eCal.get(Calendar.YEAR); int eM=eCal.get(Calendar.MONTH)+1; int eD=eCal.get(Calendar.DATE); query+=" ending_date=\""+eY+"-"+eM+"-"+eD+"\","; } else query+=" ending_date=null,"; query+=" allergy_id=\""+allergyId+"\""; query+=" where id="+id+";"; 140 Appendice – Codice Sorgente try { conn.insert(query); } catch (Exception e) { System.out.println("Eccezione: "+e.getMessage()); return false; } finally{ conn.closeConn(); } return true; } public ArrayList view() { //sessione HttpServletRequest request = this.getThreadLocalRequest(); HttpSession session = request.getSession(); //UserSession currentPatient=(UserSession) session.getAttribute("us"); String id=(String) session.getAttribute("id"); Database conn = new Database("mironjava","mironjava","abppt_5"); conn.connetti(); String query="SELECT name FROM allergies"; ArrayList names=new ArrayList(); try { conn.query(query); while(conn.getResult().next()) names.add(conn.getResult().getString("name")); } catch (Exception e) { System.out.println("eeee2222"+e.getMessage()+e.getCause()+e.getS tackTrace()); String mess="errore: "; mess+="\n\nLOG DATABASE: "+conn.getLog(); System.out.println(mess); String [] error={mess}; ArrayList ret_err= new ArrayList(); for (int i=0;i<=error.length;i++) ret_err.add(i, error[i]); return ret_err; } query="SELECT * FROM anamnesis_allergologies, allergies"; query+=" WHERE allergy_id=allergies.id and anamnesis_allergologies.id="+id+";"; String name=new String(); String note=new String(); String bDate=new String(); String eDate=new String(); ArrayList values=new ArrayList(); try { conn.query(query); 141 Appendice – Codice Sorgente if(conn.getResult().first()){ name=conn.getResult().getString("name"); note=conn.getResult().getString("notes"); bDate=conn.getResult().getString("beginning_date"); eDate=conn.getResult().getString("ending_date"); } values.add(name); values.add(note); values.add(bDate); values.add(eDate); values.add(names); //nomi di tutte le allergie **array list** System.out.println("values 0:"+values.get(0).toString()); System.out.println("names:"+names.get(0).toString()); } catch (Exception e) { System.out.println("errore nel view:"+e.getMessage()+e.getCause()+e.getStackTrace()); String mess="errore: "; mess+="\n\nLOG DATABASE: "+conn.getLog(); System.out.println(mess); String [] error={mess}; ArrayList ret_err= new ArrayList(); for (int i=0;i<=error.length;i++) ret_err.add(i, error[i]); return ret_err; } finally{ conn.closeConn(); } return values; } public boolean insert(String typology, String note, Date bDate, Date eDate){ HttpServletRequest request = this.getThreadLocalRequest(); HttpSession session = request.getSession(); PatientSession currentPatient=(PatientSession) session.getAttribute("ps"); int patientId=currentPatient.getId(); Database conn = new Database("mironjava","mironjava","abppt_5"); conn.connetti(); String allergyId=new String(); String query="SELECT id FROM allergies where name=\""+typology+"\";"; try{ conn.query(query); conn.getResult().first(); allergyId=conn.getResult().getString("id"); } catch (Exception e) { 142 Appendice – Codice Sorgente System.out.println("Eccezione: "+e.getMessage()); return false; } query="insert into anamnesis_allergologies values "; query+="(null,"+patientId+","+allergyId+",\""+note+"\","; Calendar bCal=new GregorianCalendar(); Calendar eCal=new GregorianCalendar(); if (bDate!=null){ bCal.setTime(bDate); int bY=bCal.get(Calendar.YEAR); int bM=bCal.get(Calendar.MONTH)+1; int bD=bCal.get(Calendar.DATE); query+=" \""+bY+"-"+bM+"-"+bD+"\","; } else query+=" null,"; if (eDate!=null){ eCal.setTime(eDate); int eY=eCal.get(Calendar.YEAR); int eM=eCal.get(Calendar.MONTH)+1; int eD=eCal.get(Calendar.DATE); query+=" \""+eY+"-"+eM+"-"+eD+"\")"; } else query+=" null)"; System.out.println(query); try{ conn.insert(query); } catch (Exception e) { System.out.println("Eccezione: "+e.getMessage()); return false; } finally{ conn.closeConn(); } return true; } } 143 BIBLIOGRAFIA • Wikipedia, http://www.wikipedia.org : enciclopedia libera scritta dagli utenti del web; • http://code.google.com/webtoolkit/ : sito ufficiale di Google Web Tooolkit; • http://gwt-ext.com/ : sito ufficiale librerie GWT-Ext; • http://tomcat.apache.org/ : sito ufficiale di Apache Tomcat; • http://javascript.html.it/ : sito da cui sono state prelevate informazioni riguardo JavaScript e AJAX; • H.M. Deitel, P.J. Deitel, “Java – Fondamenti di Programmazione”: consultazioni informazioni di base sul Java; • H.M. Deitel, P.J. Deitel, “Java – Tecniche Avanzate di Programmazione”: consultazione informazioni avanzate sul Java e sui Servelts. 144