AIRT Lab - Università Politecnica delle Marche
Transcript
AIRT Lab - Università Politecnica delle Marche
Università Politecnica delle Marche Facoltà di Ingegneria Ingegneria Informatica PROGETTO DI UN MIDDLEWARE IN “RUBY ON RAILS” PER LA TELEREFERTAZIONE MEDICA: REALIZZAZIONE LATO CLIENT Relatore Candidato Prof. Aldo Franco Dragoni Alfredo Flauto Matr. 1022956 Correlatore Prof. Paolo Puliti Anno Accademico 2007/2008 1 INDICE 1. INTRODUZIONE............................................................................................................4 1.1 Telemedicina...................................................................................................4 1.2 Stato dell’arte..................................................................................................6 1.3 Progetto “Miro on Rails”................................................................................8 1.4 Ruby e telemedicina.....................................................................................11 1.4.1“Ruby on Rails”....................................................................................14 1.5 Le immagini DICOM..................................................................................16 1.6 “Scrubbing” dei dati medici riservati..........................................................19 1.7 Miro on Rails e l’Open Source....................................................................21 2. ANALISI DEI REQUISITI............................................................................................23 2.1 Analisi orientata agli scenari........................................................................23 2.2 Schemi di Jacobson......................................................................................25 2.3 Requisiti della cartella clinica......................................................................34 2.3.1 Profilo del paziente..............................................................................35 2.3.2 L’anamnesi..........................................................................................36 3. PROGETTAZIONE.......................................................................................................40 3.1 Architettura...................................................................................................40 3.2 Classi di progettazione.................................................................................42 3.3 Progettazione cartella clinica........................................................................43 3.4 Confronto tra Ruby on Rails e tecnologie alternative..................................46 3.4.1 Rails.....................................................................................................52 4. IMPLEMENTAZIONE LATO CLIENT.......................................................................54 4.1 Realizzazione Database...............................................................................54 4.2 Ambiente di sviluppo e tecnologie software utilizzate................................57 4.2.1 Eclipse con plugin Aptana + RadRails + Ruby...................................57 4.2.2 SSH......................................................................................................60 4.2.3 Prototype..............................................................................................66 2 4.2.4 Ajax......................................................................................................67 4.2.5 Mantis bug tracker................................................................................69 4.2.6 Sistema operativo.................................................................................72 5. STRUTTURA MIRO ON RAILS..................................................................................75 5.1 Autenticazione.............................................................................................75 5.2 Administrator...............................................................................................77 5.3 Requester.....................................................................................................81 5.4 Doctor..........................................................................................................91 5.5 Sezione messaggi.........................................................................................95 6. CONCLUSIONI E SVILUPPI FUTURI.......................................................................98 6.1 Conclusioni...................................................................................................98 6.2 Sviluppi futuri...............................................................................................99 Appendice A: CONTROLLERS.......................................................................................101 Appendice B: MODELS....................................................................................................120 Bibliografia.........................................................................................................................126 3 1. INTRODUZIONE 1.1 Telemedicina Il termine TELEMEDICINA si presta a svariate definizioni, non sempre univoche in letteratura, che spesso focalizzano l’attenzione solo su alcuni aspetti della materia. Si tratta sostanzialmente della trasmissione in tempo reale di informazioni a carattere scientifico tra medico e cittadino o tra addetti ai lavori, attraverso sistemi di comunicazione di tipo telematico/informatico. La definizione più esaustiva del termine è senz’altro quella concordata a livello CEE da una Commissione di esperti, che ha redatto un documento sulle prospettive di sviluppo della telemedicina in Europa (Advanced Informatics in Medicine - AIM 1990) con l’obiettivo di migliorare la qualità dei servizi sanitari, facilitare la formazione professionale di medici e infermieri ed ottimizzare il trasferimento qualificato di dati ed esperienze tra i vari Paesi europei. Secondo la Commissione Europea, organizzatrice tra l’altro dell’EHTO (European Health Telematics Observatory – Osservatorio delle applicazioni mediche della telematica), la telemedicina è “l’integrazione, monitoraggio e gestione dei pazienti, nonché l’educazione dei pazienti e del personale, usando sistemi che consentano un pronto accesso alla consulenza di esperti ed alle informazioni del paziente, indipendentemente da dove il paziente o le informazioni risiedano”. I campi di applicazione della telemedicina sono numerosissimi e in continua evoluzione, dalla cardiologia (trasmissione di tracciati elettrocardiografici) alla radiologia (immagini radiografiche e computerizzate), dalla dermatologia (foto digitali di lesioni cutanee) all’anatomia patologica, dalla ginecologia (monitoraggio in gravidanza) all’odontoiatria e via dicendo; praticamente ogni branca della medicina può avvalersi di questo strumento per migliorare l’esercizio delle attività cliniche, assistenziali e didattiche. Applicare la telematica in ambito medico significa, infatti, rispondere con tempestività alle esigenze diagnostiche (telediagnosi) e terapeutiche (teleassistenza) di cittadini distanti dalle strutture sanitarie o comunque impossibilitati a muoversi da casa; fornire una risposta valida ed efficace in caso di malati cronici o anziani e un supporto indispensabile nelle urgenze (telesoccorso); favorire l’aggiornamento scientifico (teledidattica) e il collegamento interattivo tra medici (videoteleconsulto) con condivisione dinamica di 4 informazioni, cartelle cliniche digitali, tracciati diagnostici, immagini biomediche, che si “muovono” in tempo reale e con la massima definizione. Ne consegue una concreta interrelazione tra le strutture minori o più deboli e quelle maggiori o specialistiche. Oltre ad avere utilità in campo strettamente clinico/didattico, la telemedicina può contribuire all’ottimizzazione della gestione del sistema sanitario, mediante vaste applicazioni di tipo amministrativo. Attraverso la creazione di una rete telematica di strutture sanitarie è possibile, infatti, ottenere informazioni sulla disponibilità dei posti letto, sull’accesso alle liste di prenotazione, troppo spesso caratterizzate da ritardi esagerati, sulla gestione delle cartelle cliniche, con gli adeguati accorgimenti per la tutela della privacy, dei referti medici etc. Questo si traduce in un sensibile miglioramento sia della qualità dei servizi per il cittadino, che si sente più garantito, sia delle condizioni di lavoro del personale, che accede più facilmente alle informazioni. Non ultimo, nell’ottica di una congrua riorganizzazione del Sistema Sanitario, l’utilizzo delle tecnologie informatiche, snellendo le procedure e migliorando i servizi offerti, contribuisce a garantire anche un contenimento della spesa sanitaria. La nostra tesi ha riguardato un ambito ben specifico della telemedicina che può essere riassunto dalle due parole chiave: Telerefertazione Second Opinion Telerefertazione: nell'ambito della diagnostica clinica, è possibile per un medico effettuare la diagnosi su un paziente che non è fisicamente nello stesso posto del medico, attraverso la trasmissione a distanza di dati prodotti da strumenti diagnostici. Second opinion medica: è una delle applicazioni più comuni nell'ambito della telemedicina, essa consiste nel fornire una opinione clinica a distanza supportata da dati acquisiti inviati ad un medico remoto che li analizza e li referta producendo di fatto una seconda valutazione clinica su un paziente. 5 1.2 Stato dell’arte Oggigiorno i sistemi sanitari mondiali stanno facendo passi da gigante nell’utilizzo massiccio della telemedicina, grazie soprattutto allo sviluppo di nuove tecnologie. In ambito nazionale ogni regione si sta attrezzando allo sviluppo di sistemi di informatica medica. Uno dei principali progetti a livello nazionale è rappresentato da As.ter: un sistema informativo per la gestione di tutte le attività del territorio sviluppato dalla USL 11 di Empoli. As.Ter integra, con l’ausilio di una piattaforma tecnologica innovativa, tutti gli applicativi ed i database delle attività sanitarie e sociali svolte sul territorio, consentendo la rilevazione dei bisogni complessivi dei cittadini, l’individuazione delle modalità e dei tempi di risposta ai loro bisogni, la rilevazione dei costi; tutto ciò al fine di pianificare e gestire le attività sociosanitarie sul territorio in un ottica manageriale di costi/benefici. Un altro progetto è stato proposto dal servizio sanitario regionale Emilia-Romagna con il nome di SOLE. Esso è finalizzato a realizzare una rete telematica, basata su una serie di hub (centri di archiviazione) e spoke (centri di acquisizione), di collegamento tra i servizi ospedalieri e i servizi territoriali per agevolare la comunicazione tra operatori sanitari e, di conseguenza, agevolare l'erogazione dei servizi con importanti e positive ricadute sulla continuità assistenziale e sulla semplificazione dell’accesso ai servizi per il cittadino. Un’interessante applicazione sviluppata da un’equipe guidata da Ennio Amori, all’interno di questo progetto è “Neurosurgery Teleconsulting”, un servizio di teleconsultazione tra i centri di neurochirurgia collegati attraverso la rete hub&spoke, nella nuova logica distribuita e digitale, in modo da poter trattare ogni trauma e lesione in modo appropriato sfruttando il livello avanzato che ha raggiunto l’IT. Molti sono i progetti pilota che puntano a creare tra i nostri ospedali e centri d’eccellenza un collegamento con i paesi dell’Europa dell’est, che sono da poco entrati nella comunità europea, per fornire servizi di teleconsulto e telediagnosi, come per esempio tra Padova o Milano e la Romania. Nello sviluppo di questo tipo di progetti è possibile usufruire anche dei finanziamenti della comunità europea che ovviamente danno maggiore spinta alla ricerca e permettono di pagare la consulenza di software house, esterne alle aziende sanitarie, che possono dare un forte supporto tecnico e far ottenere un prodotto software più completo ed evoluto. 6 In tutto questo panorama di progetti non emerge nessun sistema simile a quello sviluppato nella nostra tesi, infatti per gli esempi citati si tratta sempre di servizi ad hoc per applicazioni specifiche, senza una vera ottica di integrazione e supporto globale di servizi sanitari delocalizzati. Un capitolo a parte merita il progetto MedTel, in quanto si propone di realizzare lo stesso servizio di Miro on Rails ma con tecnologie differenti. Il progetto MedTel (Medical Telereporting System) è stato realizzato grazie alla collaborazione tra l’Università Politecnica delle Marche e L’ASUR Marche zona 7 di Ancona e finalizzato alla sperimentazione della telerefertazione. In questo caso l’utilizzo dei sistemi di comunicazione e informatici a favore del servizio sanitario, è stato incentrato per favorire il servizio di refertazione verso le esigenze di tipo diagnostico, per paesi come l’Africa che sono in via di sviluppo. Tramite l’utilizzo di questo sistema, si è in grado di poter effettuare una refertazione delocalizzata, ossia poter effettuare un referto indipendentemente dal posto e dal momento in cui è stato effettuato un determinato esame clinico. Offre quindi un tipo di servizio asincrono. Gli esami svolti, e gli eventuali referti medici allegati, verranno successivamente inseriti e registrati nella memoria del sistema, che ne garantirà la sicurezza e privatezza. 7 1.3 Progetto “Miro on Rails” Il termine “MiRo” è l’arcronimo per Medical Report e l’estensione “on Rails” è stata aggiunta per evidenziare il framework utilizzato per la progettazione. Il progetto Miro on Rails nasce dall’esigenza di poter delocalizzare la refertazione diagnostica. Infatti l’obiettivo del nostro progetto è quello di realizzare un prodotto che implementi un servizio di refertazione asincrono, in grado di separare l’effettuazione dell’esame clinico dalla refertazione medica, da un punto di vista temporale ma anche spaziale. Quindi per asincrono intendiamo che non ci si aspetta che l’esame venga refertato in tempo reale, ma in un tempo accettabile concordato tra i fornitori del servizio e gli utenti. La parola chiave di Miro on Rails è “telerefertazione”: soluzione avanzata di telemedicina. Il progetto rientra infatti nell’ambito applicativo della Telemedicina. Le applicazioni di telemedicina possono essere utilizzate per sopperire ad eventuali carenze di copertura territoriale dei servizi sanitari o nel caso di problematiche legate alle difficoltà dei trasporti o all’impossibilità di facili spostamenti da parte dei cittadini soprattutto se anziani senza assistenza. Pensiamo poi alle popolazioni del terzo mondo, proprio queste popolazioni sono quelle che più soffrono per la difficoltà di collegamenti, per la povertà, per la carenza di servizi medici adeguati. In questi contesti ambientali le possibilità che offre la telemedicina può limitare il disagio puntando a portare il servizio alle persone; sfruttando le reti telematiche e la flessibilità delle attrezzature informatiche si “avvicina” il servizio verso l’uomo e non viceversa. Miro on Rails è pensato soprattutto per aiutare le popolazioni del terzo mondo, quindi l’ambito di utilizzo è a livello mondiale. Il sistema di telerefertazione che andremo a progettare si propone di muovere le informazioni e non i pazienti (Figura 1). Obiettivo specifico del progetto è la realizzazione di un Software sperimentale che offra un servizio di telerefertazione medica: trasmissione di analisi mediche (tracciati ECG, ecc...) e dei relativi referti in formato elettronico attraverso internet. Il Sistema di Telerefertazione ipotizzato è un sistema di uso clinico composto da strumenti di analisi mediche (per esempio elettrocardiografi), da computer connessi in rete, un’unità 8 centrale (“Server” di refertazione) dove vengono archiviati elettronicamente tutti i dati anagrafici e clinici dei pazienti e tutti gli esami acquisiti dagli strumenti, tutto il sistema è gestito dal software Miro on Rails. Da uno dei PC, il Medico può esplorare e consultare tutti i dati memorizzati dal Sistema ed effettuare la refertazione degli esami. Il Sistema permette di controllare automaticamente tutto il flusso di lavoro di un esame clinico. In questo modo non è più necessario spostare da una località all’atra la stampa su carta dell’esame e la cartella clinica: attraverso internet le informazioni viaggiano in formato elettronico fino al Medico specialista e tornano indietro al paziente corredate dalla diagnosi. L’esame può essere effettuato presso qualunque laboratorio in qualunque regione del mondo e se necessario direttamente presso il domicilio del paziente (figura 2): 1. Un Requester tramite l’interfaccia di Miro on Rails inserisce i dati del paziente (i dati anagrafici possono essere inseriti all’atto della prenotazione dell’esame) e le informazioni sull’esame eseguito comprensivo dell’eventuale file di qualità diagnostica (per esempio tracciato ECG), il SW memorizzerà tutti i dati sul database del server centrale. 2. Il medico specialista tramite le apposite dotazioni HW/SW, potrà consultare la cartella clinica del paziente e l’evento per cui è richiesta un’opinione. 3. Eventualmente lo specialista provvederà alla stesura del referto in formato elettronico: procede alla consultazione del file (esempio: ECG) di qualità diagnostica e provvede alla stesura del referto tramite le funzionalità del SW. 4. La consultazione dei referti avviene direttamente dall’interfaccia del programma. 5. Eventualmente il Requester chiude l’evento. Il vantaggio è che il referto sarà reso disponibile ai medici curanti via web tramite Miro on Rails: sito dedicato, ad accesso riservato, consultabile in modo anonimo da qualsiasi PC connesso ad Internet. Sul sito appositamente costruito sarà ad esclusiva disposizione dagli utenti autorizzati la cartella clinica del paziente con il referto rilasciato dal medico specialista firmato digitalmente ed eventualmente accessibile in modalità anonima direttamente dal paziente in caso di necessità. 9 Figura 1: Architettura generale Figura 2: Flow Diagram 10 1.4 Ruby e telemedicina Le caratteristiche che dovrebbe avere un ideale linguaggio di programmazione nel campo della telemedicina sono: 9 Free 9 Open Source 9 Facile da scaricare da internet e da installare 9 Facile da imparare 9 Facile da “leggere” 9 Capace di rispondere ad ogni “Computational Task” dell’area di interesse del programmatore 9 Veloce 9 “User Community” attiva 9 Disponibilità di numerose librerie per espandere le funzionalità del linguaggio 9 Linguaggio che supporti una prospettiva compatibile all’ambito telemedico Occorre quindi valutare se Ruby ha tali caratteristiche. Dimostreremo che Ruby possiede le proprietà che lo rendono adatto alla realizzazione di un sistema client-server per servizi avanzati di telerefertazione medica. Ruby è un linguaggio molto interessante per programmatori e non programmatori ed è free e facile da installare. I programmatori si accorgeranno che Ruby permette loro di scrivere applicazioni software (well-designed e object-oriented) in meno tempo, con meno codice e con minore complessità rispetto ad altri linguaggi di programmazione. Quindi i non programmatori che cercano il miglior linguaggio da imparare farebbero bene a scegliere Ruby. Fra i numerosi linguaggi di programmazione esistenti, cosa rende Ruby speciale? Ruby è stato creato nel 1995 da Yukihiro Matsumoto, da allora ha avuto un enorme successo in Giappone e nel resto del mondo. I programmatori Ruby hanno accesso alle librerie Ruby dislocate nel mondo attraverso RubyGem, un sistema per scaricare e installare software Ruby che si trova su server remoti. RubyForge è una repository e risorsa di sviluppo per gruppi di programmatori che collaborano allo sviluppo di nuove 11 librerie di Ruby. Queste nuove librerie sono liberamente disponibili all’interno della community di Ruby. Il progetto Ruby più famoso è “Ruby on Rails”, un framework per applicazioni web sviluppato in Ruby (vedi capitolo 1.6). In generale se si sceglie Ruby, si avrà la possibilità di importare ed esportare dati RDF (Resource description Framework) e YAML (Yet Another Metadata Language), condividere immagini, creare Internet scripts client-server, accedere a databases, incapsulare richieste per web services in messaggi SOAP, scrivere e-mail scripts e creare applicazioni web. I più comuni “computational tasks” in telemedicina sono: ¾ Estrarre sottoinsiemi di dati da uno o più database ¾ Creare ed organizzare un nuovo database ¾ Analizzare i dati contenuti in un database usando diverse strategie ¾ Indexing e autocoding di testi e datasets ¾ Trasformare datasets in formati standard ¾ Unire dati eterogenei in datasets utili ¾ De-identificare (“scrubbing”) datasets che contengono dati riservati ¾ Distribuire dati e servizi attraverso internet ¾ Descrizione dei dati in maniera logica, in modo da facilitare la condivisione dei dati Ruby è un linguaggio object-oriented, ogni cosa in Ruby è un oggetto. Ruby conosce il tipo di ogni oggetto (per esempio, Array, Numeric, String e Class). Le classi sono oggetti speciali perché contengono dati e metodi e possono creare un’unica istanza di loro stessi. Le classi Ruby possono rappresentare il mondo naturale e adattarlo alla costruzione logica dei moderni modelli di dati (come RDF). Si troverà che la programmazione Ruby fa risparmiare tempo e aumenta la produttività. Ruby è un linguaggio che fornisce vari livelli di programmazione: ¾ Livello 1: uso di strutture dati, iteratori e classi incorporate in Ruby 12 ¾ Livello 2: uso di estensioni che provengono dalla installazione standard di Ruby, incluse le librerie standard e Ruby GEMS. Programmare facendo uso di tecniche di computational reflection ¾ Livello 3: gli utenti creano la propria classe di librerie. I programmatori usano RDoc per preparare una classe di librerie ben documentate ¾ Livello 4: creare applicazioni web con Ruby on Rails, CGI o protocolli Network ¾ Livello 5: usare tecniche avanzate class-oriented come delegazione e polimorfismo, sviluppare interfacce grafiche utente e usare tecniche di programmazione come error trapping e unit testing ¾ Livello 6: estendere e incorporare Ruby con altri linguaggi di programmazione ¾ Livello 7: preparare applicazioni software commercial-grade Di norma si tende ad associare la programmazione object-oriented ai tre concetti di: ereditarietà, polimorfismo e incapsulamento. In Ruby c’è una quarta proprietà: composition. La composition (anche chiamata layering e aggregation) è una tecnica dei linguaggi di programmazione object-oriented nella quale le classi sono provviste di metodi che non sono essenziali per la classe (cioè, la loro esclusione dalla classe non dovrebbe cambiare il nostro concetto degli oggetti contenuti nella classe). In Ruby, Mixins è una semplice e potente tecnica di programmazione composizionale così come la delegazione. La delegazione è una particolare tecnica che crea un oggetto “ombra” in una seconda classe. Ruby è distribuito con una varietà di programmi chiamati estensioni. Le estensioni sono programmi Ruby che sono estesi dall’inclusione di altre risorse, in particolare routines C. Le estensioni Ruby saranno supportate solo se il sistema operativo ha accesso alle risorse chiamate dalle estensioni. Il vantaggio della Libreria Standard Ruby è quello di avere le stesse librerie incluse nell’istallazione per chiunque utilizza Ruby. Le GEMS sono programmi Ruby a cui si può accedere attraverso internet. Dopo aver installato Ruby e verificato che il nostro computer è collegato ad internet, si può utilizzare il programma GEM per installare le gems. Una delle più importanti Ruby gems è Ruby on Rails. 13 1.4.1 “Ruby on Rails” Ruby on Rails è un ambiente di lavoro, un framework che semplifica lo sviluppo, l’utilizzo e la gestione delle applicazioni web. Ovviamente, tutti gli ambienti web sostengono di avere queste caratteristiche. Dunque, che cosa rende differente Rails? E’ possibile rispondere a questa domanda esaminandola da diversi punti di vista. Uno di questi è l’analisi dell’architettura. Nel corso degli anni, per i progetti web più impegnativi la maggior parte degli sviluppatori ha scelto di utilizzare un’architettura di tipo MVC (Modello-Vista-Controller) in quanto, secondo molti programmatori, consente di strutturare le applicazioni in modo più lineare. I framework Java come Tapestry e Struts si basano su MVC, come Rails, del resto. Quando si sviluppa in Rails, ogni porzione di codice ha la sua collocazione, e tutti i componenti dell’applicazione interagiscono in modo standard. Si potrebbe affermare che è come se si iniziasse con lo scheletro di un’applicazione già pronto. Un altro punto di vista consiste nell’osservazione del linguaggio di programmazione. Le applicazioni Rails sono scritte in Ruby, un moderno linguaggio di scripting orientato agli oggetti. Ruby è conciso, pur senza essere incomprensibilmente stringato: il codice Ruby consente di esprimere le idee in modo naturale e chiaro. Per questa ragione, i programmi possono essere scritti più facilmente e, considerazione non meno importante, letti anche a distanza di mesi. Inoltre, Ruby si presta a uno stile di programmazione che semplifica la creazione di metodi che agiscono quasi come estensioni sintattiche. Alcuni esperti definiscono questa tecnologia con il termine metaprogrammazione: grazie a essa, non soltanto i programmi risultano più brevi e leggibili, ma è possibile far eseguire direttamente nel codice di base attività che di norma richiederebbero l'impiego di file di configurazione esterni, tecnica che facilita l’analisi di quanto avviene all’interno del programma. Il codice seguente definisce la classe modello di un progetto: si consideri la quantità di informazioni che possono essere espresse in poche righe di codice. class Project < ActiveRecord::Base belongs_to :portfolio has_one :project_manager has_many :milestones has_and_belongs_to_many :categories validates_presence_of :name, :description validates_acceptance_of :non_disclosure_agreement validates_uniqueness_of :key end 14 Il terzo punto di vista è quello filosofico. La progettazione di Rails è stata governata da due concetti chiave: DRY (Don’t repeat Yourself, non ripetetevi) e convention-overconfiguration. DRY significa che ogni elemento di informazione nell’ambito di un sistema dovrebbe essere espresso in un solo punto e, per adattarsi a questo principio, Rails utilizza la potenza di Ruby. Difficilmente si trovano duplicazioni in un’applicazione Rails: si dice quello che occorre soltanto una volta, in un punto del codice spesso ispirato alle convenzioni dell’architettura MVC, quindi si procede oltre. Anche convention-over-configuration è un concetto essenziale, e indica che Rails offre soluzioni predefinite per quasi tutti gli aspetti che compongono un’applicazione. Attenendosi alle convenzioni, si riesce a realizzare un’applicazione Rails con meno codice di quello che occorre per la configurazione XML di una tipica applicazione web Java, ma Rails può essere utile persino per infrangere le convenzioni, perché semplifica anche questo compito. Si potrebbe accennare anche a tutte le interessanti funzionalità di Rails, tra e quali il supporto integrato ai servizi web, il ricevimento della posta elettronica, AJAX (per applicazioni web altamente interattive), l’ambiente completo per test unitari (unit testing). 15 1.5 Le immagini DICOM Esistono probabilmente centinaia di formati di file immagine. Usando Ruby è possibile manipolare più di 90 tipi di file immagine con RMagick e ImageMagick. RMagick e ImageMagick non fanno parte del pacchetto di distribuzione standard di Ruby ma possono essere ottenuti da RubyForge. Generalmente il formato più utilizzato nel web è il JPEG (joint photographic experts group), le immagini che usano questo formato possono essere visualizzate dai web bowsers più popolari. La possibilità di scambiare immagini mediche corredate da varie informazioni attraverso reti telematiche, ha richiesto la creazione di uno standard di comunicazione globale, tale da garantire l’intercomunicabilità tra sistemi eterogenei. DICOM (acronimo di Digital Imaging and Communication in Medicine) rappresenta un modello di tale standardizzazione, la cui nascita è stata però inizialmente ostacolata dalla pluralità di prodotti hardware in campo medico, i quali, proponendo standard proprietari avevano creato una situazione di completa incomunicabilità tra apparecchi di case costruttrici differenti. Sviluppato per la gestione di immagini digitali prodotte da diverse modalità radilogiche (TC,RM, Ecografia, Angiografia digitale, Fluoroscopia digitale, etc.) e la loro trasmissione su reti standard, garantisce l’intercomunicabilità tra sistemi eterogenei. A tal proposito un progetto open source molto interessante è Osiris, un lettore DICOM multipiattaforma che consente di fare numerose operazioni sulle immagini. Figura 3: caratteristiche del file 16 Figura 4: file associato al paziente Figura 5: esempio immagine DICOM 17 Il software permette di: • Aprire e visualizzare qualsiasi immagine DICOM con le relative caratteristiche (Figura 3 e 5) • Convertire il formato di qualsiasi file.DICOM nei principali formati immagine (TIFF, BITMAP, ecc) e viceversa • Mantenere una lista di pazienti anonimi a cui associare i file diagnostici (figura 4) • Eseguire le operazioni base dei normali visualizzatori di immagini 18 1.6 “Scrubbing” dei dati medici riservati Oggi una delle sfide più importanti in telemedicina è la sicurezza e la privacy dei dati trattati. Poiché i dati medici sono spesso estratti da archivi protetti, coloro che lavorano con questi dati devono essere a conoscenza della legislatura sulla protezione dei dati medici. Negli Stati Uniti, i dati medici privati sono protetti da due regolamenti federali: HIPAA (Health Insurance Portability and Accountability Act) e Common Rule. L’HIPAA, per esempio, elenca 18 identificatori che devono essere assenti da qualunque archivio medico de-identificato: 9 Nomi 9 Suddivisione geografica più piccola di uno stato 9 Date (eccetto l’anno) direttamente collegate al paziente 9 Numeri di telefono 9 Numeri di fax 9 Indirizzi e-mail 9 Numeri di previdenza sociale 9 Numeri di cartelle mediche 9 Numeri beneficiari piani previdenziali 9 Numeri di conto 9 Numeri di certificati e licenze 9 Identificatori e numeri di serie di veicoli 9 Identificatori e numeri di serie di dispositivi elettronici 9 Web URLs 9 Indirizzi IP 9 Identificatori biometrici 9 Immagine fotografica del paziente 9 Ogni altro numero identificativo univoco, eccetto quelli permessi da HIPPA per riidentificare i dati L’HIPAA e Common Rule permettono l’uso di dati medici riservati quando il paziente è informato dei rischi e fornisce il consenso al trattamento. In Italia i dati sensibili come quelli medici sono protetti da una normativa: Il Codice in materia di protezione dei dati 19 personali. Una particolare attenzione richiede l’Autorizzazione n. 2/2008 al trattamento dei dati idonei a rivelare lo stato di salute e la vita sessuale - 19 giugno 2008. Nei processi di automazione della sanità pubblica devono rientrare anche le regole previste per la protezione dei dati personali, lo ha ricordato Gaetano Rasi, componente dell'Autorità Garante per la protezione dei dati personali, nel corso di un convegno. Rasi ha sottolineato come le applicazioni di telemedicina sono in rapido aumento e in Italia ormai il 40 per cento delle strutture mediche si avvale del teleconsulto e il 25 per cento dispone di soluzioni di home care. Si tratta quindi di nuove opportunità di collegamento virtuoso tra paziente e strutture sanitarie e via via sono messi a disposizione servizi di informazione assai rilevanti come disponibilità di posti letto, accesso alle liste di prenotazione, gestione delle cartelle cliniche e via dicendo. Questi importanti sviluppi, ha spiegato Rasi, non devono però far dimenticare come i dati personali e in particolare quelli sensibili, vale a dire le informazioni più riservate ed intime della persona, devono essere trattati nella massima riservatezza e nel rispetto della dignità della persona "Anche l’outsourcing e il telelavoro - ha osservato Rasi - sono realtà in espansione e contribuiscono alla profittabilità del business e all’efficienza delle pubbliche prestazioni. Il 43% dei contratti di outsourcing copre, ad esempio, l’area dell’Information technology, il 25% quella delle risorse umane. In quest’ultimo caso vengono affidati a partner esterni non solo l’amministrazione, ma anche, per esempio, la gestione del percorso di carriera dei dipendenti". "L’euforia informatica - ha invece sottolineato il segretario generale dell'Autorità Giovanni Buttarelli - non deve far dimenticare che Costituzione Europea e Codice della Privacy mettono in primo piano il diritto della persona a verificare come vengono trattati i propri dati personali. Siamo forse primi nel mondo per il quadro giuridico sull’informatica e la telematica. Il Testo Unico sulla privacy è pronto per rispondere alle questioni poste dalla telemedicina, che recherà grossi vantaggi, come la possibilità di accedere con una password alla propria cartella clinica da qualunque parte del mondo, ma causerà anche nuovi rischi relativi, ad esempio, alla dispersione di informazioni. L’informativa al cittadino dovrà tenere conto di tutti questi aspetti. "Sulle nuove autostrade del sole del trattamento dei dati - ha concluso Buttarelli - il cittadino avrà sempre il diritto di intervenire sul trattamento dei propri dati". 20 1.7 Miro On Rails e l’Open Source Alcuni dei vantaggi delle soluzioni Open Source rispetto ai software proprietari sono: • Ha un basso TCO: il Total Cost of Ownership (TCO) è uno dei parametri per valutare il costo di un prodotto. Partendo dal modello di costo del prodotto, si identificano la varie voci di costo e si stimano. Non bisogna dimenticare tutte quello voci di costo "nascoste", come per il supporto tecnico, l'amministrazione del sistema, l'upgrade. • Non lega al fornitore: la possibilità di non essere legati ad uno specifico fornitore di software, libera il privato, ed ancora più l'azienda da eventuali politiche "protezionistiche" del fornitore • Innovazione e potenziale per l'evoluzione sono generalmente agevolati • Avendo una comunità di persone che controlla ed ottimizza il software, si ottiene un prodotto molto efficiente • Flessibilità: la possibilità di personalizzare ogni aspetto di un programma, aumenta la flessibilità del sistema complessivo. Aumenta così anche il range di possibilità applicative di un programma. Avendo il codice a disposizione si può "portare" un'applicazione su una nuova architettura, aprendo così nuove possibilità. Uno dei temi fondamentali nel campo della telemedicina e dell’informatica medica è cercare di ottimizzare la gestione sanitaria per garantire un considerevole abbassamento dei livelli della spesa e rendendo più efficienti, nel contempo, le prestazioni di qualità del servizio a tutto vantaggio della salute dei pazienti. Da un lato c'è il bisogno di far convergere tutte le informazioni, soprattutto quelle che concernono il singolo paziente, in una repository accessibile e condivisa dagli operatori sanitari, e dall'altro di realizzare questa razionalizzazione senza aumentare il livello di spesa: l'idea alla base del progetto Miro on Rails è stata allora quella di sfruttare al massimo la risorsa dell’Open Source 21 Lo sviluppo di Miro on Rails presenta come peculiarità delle scelte implementative la preoccupazione di non legare la soluzione proposta all'utilizzo di un particolare assetto informatico (tipologia di hardware, sistemi operativi, etc.), ma ,sfruttando l'approccio dei sistemi Open Source, assicurare quella flessibilità necessaria a promuovere una vasta adozione dei sistemi informativi e di comunicazione nel settore della Sanità. Il Sistema Sanitario si sta avviando verso nuove frontiere di riorganizzazione e di convergenza delle informazioni. Miro on Rails cerca di soddisfare proprio questa esigenza ponendosi come obiettivo fondamentale quello di realizzare una “Virtual Health-Care Agency”. 22 2. ANALISI DEI REQUISITI 2.1 Analisi orientata agli scenari Un analisi di questo tipo serve per modellare i requisiti del sistema dal punto di vista dell’utente. In tal senso il diagramma dei casi d’uso rappresenta un aiuto per definire che cosa esiste al di fuori del sistema (attori) e che cosa dovrebbe essere fatto dal sistema (casi d’uso). Gli attori del sistema sono tre: Administrator, Requester, Doctor; ognuno avrà uno specifico diagramma dei casi d’uso. Figura 6: diagramma dei casi d’uso Administrator 23 Figura 7: diagramma dei casi d’uso Requester Figura 8: diagramma dei casi d’uso Doctor 24 2.2 Schemi di Jacobson 1) SPECIFICA DI CASO D’USO: Autenticazione ATTORI: Administrator, Requester, Doctor BREVE DESCRIZIONE: Ogni utente per poter accedere al sistema deve autenticarsi immettendo username e password FLUSSO BASE: • l’utente accede alla pagina di login di Miro on Rails tramite internet • l’utente inserisce username e password • l’utente attende la risposta del sistema PRECONDIZIONI: l’utente deve essere registrato da un Administrator POSTCONDIZIONI: autenticazione riuscita ECCEZIONI: “Invalid user/password combination” FREQUENZA DI UTILIZZO: Alta CRITICITA’: Bassa 2) SPECIFICA DI CASO D’USO: Inserimento New User ATTORI: Administrator BREVE DESCRIZIONE: L’administrator può registrare le informazioni di un nuovo utente e scegliere lo “User type” (Administrator, Requester, Doctor) FLUSSO BASE: • l’administrator si autentica • inserisce tutti i dati del nuovo utente • salva la procedura PRECONDIZIONI: il nuovo utente deve fornire alcuni dati obbligatori (name, surname, username, mail, password) POSTCONDIZIONI: “registration complete” ECCEZIONI: • • “Username has already been taken” “obbligatory field!” FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 25 3) SPECIFICA DI CASO D’USO: Visualizza Last Users ATTORI: Administrator BREVE DESCRIZIONE: L’administrator può visualizzare gli ultimi users registrati FLUSSO BASE: • l’administrator si autentica • visualizza gli ultimi users inseriti FREQUENZA DI UTILIZZO: Bassa CRITICITA’: Bassa 4) SPECIFICA DI CASO D’USO: Cancellazione User ATTORI: Administrator BREVE DESCRIZIONE: L’administrator può cancellare dal sistema gli users del tipo Requester e Doctor FLUSSO BASE: • l’administrator si autentica • seleziona uno user • procede alla cancellazione PRECONDIZIONI: selezionare uno o più users POSTCONDIZIONI: cancellazione avvenuta FREQUENZA DI UTILIZZO: Bassa CRITICITA’: Bassa 5) SPECIFICA DI CASO D’USO: Visualizza Last Patients ATTORI: Administrator BREVE DESCRIZIONE: Il requester può visualizzare gli ultimi pazienti inseriti FLUSSO BASE: • il requester visualizza l’elenco degli ultimi pazienti inseriti FREQUENZA DI UTILIZZO: Alta CRITICITA’: Bassa 26 6) SPECIFICA DI CASO D’USO: Add New Patient ATTORI: Requester BREVE DESCRIZIONE: Il requester può inserire il profilo di un nuovo paziente FLUSSO BASE: • Il requester inserisce i dati del paziente • salva la procedura PRECONDIZIONI: “patient not found” POSTCONDIZIONI: “registration complete” ECCEZIONI: • • il paziente già esiste “obbligatory field!” FREQUENZA DI UTILIZZO: Alta CRITICITA’: Bassa 7) SPECIFICA DI CASO D’USO: Visualizza Last Clinical Problem ATTORI: Requester BREVE DESCRIZIONE: Il requester può visualizzare gli ultimi clinical problems FLUSSO BASE: • Il requester visualizza gli ultimi clinical problems aperti o di cui si richiede una second opinion FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 27 8) SPECIFICA DI CASO D’USO: Visualizza Clinical Folder ATTORI: Requester BREVE DESCRIZIONE: Il requester può visualizzare e gestire la clinical folder di un paziente FLUSSO BASE: • Il requester registra un nuovo paziente • Visualizza la clinical folder • Gestisce la clinical folder PRECONDIZIONI: il paziente deve essere registrato POSTCONDIZIONI: gestione clinical folder FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 9) SPECIFICA DI CASO D’USO: Inserimento e modifica dati paziente ATTORI: Requester BREVE DESCRIZIONE: Il requester può inserire i dati di un paziente e successivamente modificare il profilo dei pazienti FLUSSO BASE: • Inserisci dati • Modifica dati • Salva dati o modifiche PRECONDIZIONI: il paziente deve essere registrato POSTCONDIZIONI: modifiche salvate FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 28 10) SPECIFICA DI CASO D’USO: Add clinical problem ATTORI: Requester BREVE DESCRIZIONE: Il requester può aprire un clinical problem nella cartella clinica di un paziente FLUSSO BASE: • Compilare la scheda con le informazioni dell’esame clinico • Allegare eventuali file diagnostici • Salvare la procedura PRECONDIZIONI: aprire la cartella clinica del paziente POSTCONDIZIONI: clinical problem aggiunto alla lista ECCEZIONI: “Exam can't be blank” FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 11) SPECIFICA DI CASO D’USO: Visualizza Report ATTORI: Requester BREVE DESCRIZIONE: Il requester può visualizzare i reports emessi dai doctors FLUSSO BASE: • Selezionare un paziente • Selezionare un clinical problem • Visualizzare uno o più report PRECONDIZIONI: selezionare un clinical problem POSTCONDIZIONI: lettura referto medico FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 29 12) SPECIFICA DI CASO D’USO: Close clinical problem ATTORI: Requester BREVE DESCRIZIONE: Il requester può cambiare lo stato di un clinical problem a “close” nel caso sia soddisfatto del referto medico FLUSSO BASE: • Selezionare un paziente • Selezionare un clinical problem • Cambiare lo stato a “close” PRECONDIZIONI: aprire la cartella clinica del paziente POSTCONDIZIONI: stato clinical problem aggiornato FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 13) SPECIFICA DI CASO D’USO: Request second opinion ATTORI: Requester BREVE DESCRIZIONE: Il requester può cambiare lo stato di un clinical problem a “request another” nel caso voglia una second opinion FLUSSO BASE: • Selezionare un paziente • Selezionare un clinical problem • Cambiare lo stato a “request another” PRECONDIZIONI: aprire la cartella clinica del paziente POSTCONDIZIONI: stato clinical problem aggiornato FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 30 14) SPECIFICA DI CASO D’USO: Visualizza clinical problems ATTORI: Doctor BREVE DESCRIZIONE: Il Doctor può visualizzare i clinical problem di uno o più pazienti in base alla propria specializzazione FLUSSO BASE: • Selezionare “clinical problems” PRECONDIZIONI: i doctors possono visualizzare solo i clinical problems che riguardano la propria specializzazione medica POSTCONDIZIONI: scegliere un clinical problem FREQUENZA DI UTILIZZO: Alta CRITICITA’: Bassa 15) SPECIFICA DI CASO D’USO: Visualizza last messages ATTORI: Doctor BREVE DESCRIZIONE: Il Doctor può visualizzare i messaggi lasciati da altri colleghi su determinati clinical problems FLUSSO BASE: • Selezionare “last topics” • Selezionare il clinical problem di interesse • Consultare il message PRECONDIZIONI: selezionare “last topics” POSTCONDIZIONI: visualizza message FREQUENZA DI UTILIZZO: Alta CRITICITA’: Bassa 31 16) SPECIFICA DI CASO D’USO: Add message ATTORI: Doctor BREVE DESCRIZIONE: I doctors possono usufruire di un servizio di messaggistica con cui scambiarsi opinioni FLUSSO BASE: • Selezionare “add message” • Scrivere il message • Salvare la procedura PRECONDIZIONI: selezionare un clinical problem POSTCONDIZIONI: message aggiunto alla lista ECCEZIONI: “message not inserted” FREQUENZA DI UTILIZZO: Alta CRITICITA’: Bassa 17) SPECIFICA DI CASO D’USO: Add report ATTORI: Doctor BREVE DESCRIZIONE: I doctor possono emettere reports su clinical problem che riguardano la loro specializzazione FLUSSO BASE: • Selezionare “add report” • Scrivere il report • Salvare la procedura PRECONDIZIONI: selezionare un clinical problem POSTCONDIZIONI: report aggiunto ECCEZIONI: “report not inserted” FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 32 18) SPECIFICA DI CASO D’USO: Consultation clinical problem ATTORI: Doctor BREVE DESCRIZIONE: Il doctor può consultare un clinical problem selezionandolo da una lista di eventi FLUSSO BASE: • Selezionare un clinical problem • Consultare l’esame clinico • Fare il download di eventuali allegati PRECONDIZIONI: selezionare “clinical problems” POSTCONDIZIONI: visualizzare “consultation” FREQUENZA DI UTILIZZO: Alta CRITICITA’: Alta 33 2.3 Requisiti della cartella clinica La cartella clinica è la figura centrale del sistema Miro on Rails. 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 è altro che la soluzione grafica di tutti i dati relativi al paziente presenti nel database centrale: tutti gli eventi e le informazioni vengono salvati in questo database e visualizzati tramite opportune interfacce agli utenti, siano essi appartenenti alla categoria doctor o requester. Una cosa importante da dire è che ovviamente la cartella clinica sarà presentata allo stesso modo sia per il dottore che per il requester, con l’unica importante differenza che quest’ultimo è l’unico che può inserire e manipolare i dati. Solo la figura del requester gestisce il paziente e quindi è a contatto diretto con lo stesso. 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 del paziente, 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, quella utilizzata in medicina generale e quella utilizzata in ambito ospedaliero. In ospedale sarà necessario focalizzare l’attenzione sugli eventi prossimi che portano 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 sia attivi che inattivi; 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, dapprima 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 34 universale, atta soprattutto a non creare possibili diversità di interpretazione tra i vari medici, cosa molto importante nel campo della telemedicina. 2.3.1 Profilo del paziente Passiamo ora ad analizzare la struttura vera e propria della cartella clinica. Inizialmente è presente il profilo del paziente con tutti i dati anagrafici. Il profilo diventa utile sia per il requester che deve ricercare un paziente nel database che per il dottore che vuole conoscere la provenienza come fattore da considerare in una diagnosi. I dati presenti nel profilo sono: 9 data e città di nascita 9 informazioni generali sulla residenza attuale 9 la struttura in cui è ospitato 9 altre informazioni generali quali il sesso, il grado di istruzione, lo stato civile e la professione svolta 9 informazioni per contattare direttamente il paziente come il numero di telefono e l’e-mail Inoltre è stata anche implementata la possibilità di mantenere in archivio le informazioni di pazienti deceduti, perciò come dato verrà anche fornita l’eventuale data di decesso. 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 è 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. E’ importante ricordare che gli stessi dati sono visibili sia dal requester che dal dottore, tali dati non dovrebbero però essere utilizzati dal dottore per contattare direttamente il paziente tranne che per emergenza. Non devono essere quindi utilizzati per scavalcare la figura del requester che deve fare da tramite tra il sistema e il paziente. Infatti il paziente non ha conoscenze mediche adatte a fornire alcune generalità mediche, quindi, se si sfruttassero tali dati per creare un legame diretto dottore-paziente, si rischierebbe di cadere in situazioni non più gestibili. 35 2.3.2 L’anamnesi Successivamente è presente la sezione riguardante l’anamnesi del paziente. Presenta varie sottocategorie che rendono tutto più immediato e allo stesso danno una visione completa e dettagliata delle informazioni relative al paziente. La sezione anamnesi è stata creata con l’intento di avere sempre tutte le informazioni a portata del doctor: tutte le informazione rimangono in primo piano e quindi sempre visibili. Abbiamo deciso di operare con questo genere di struttura per dare un’ampia e completa visuale del paziente, cosicché al primo impatto il dottore può avere già un’idea generale sulla situazione medica. Le sezioni di seguito sono identiche dal punto di vista del doctor e del requester, a tal proposito bisogna ricordare che tutte le informazioni sono inserite dal requester, tramite un’apposita form di inserimento, e recepite dal dottore. Allergie La prima sottosezione che si incontra è quella relativa alle allergie del paziente. La prima scheda riassuntiva fornisce informazioni sul tipo di allergie salvate nella struttura dati e relative a quel paziente. Quando si entra nel dettaglio vengono fornite informazioni specifiche come la data di inizio dell’allergia e l’eventuale data di fine se questa non è più diagnosticata. Sono inoltre presenti delle note generiche sull’allergia riscontrata utili per una migliore diagnosi e lettura della cartella clinica. Informazioni generali sulla famiglia La sottosezione successiva è quella che comprende i dati della famiglia. Le informazioni qui presenti riguardano il padre e la madre del paziente. Qui non è presente come nella scheda precedente una parte riassuntiva, in quanto i dati sono univoci e al più potranno essere modificati nel tempo. Dal punto di vista del dottore vengono visualizzate 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 mirate ad ottenere una migliore diagnosi. 36 Patologie familiari Nella prossima sottosezione vengono invece presentate le informazioni sulle patologie di tutta la famiglia, cioè quegli individui con legami di sangue con il paziente. È qui presente una scheda riassuntiva delle patologie che i familiari hanno avuto. E’ anche presente 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 di carattere ambientale in cui vive il paziente. 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 confrontarle con una eventuale nuova diagnosi. In questa sottosezione comunque non devono essere inserite esclusivamente 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 che riguardano il gruppo sanguigno del paziente e quello dell’eventuale partner, con la possibilità di note aggiuntive. Ogni nota dovrebbe inquadrare subito il problema e fornire anche più dettagli possibili, si deve ricordare che il dottore non è a contatto diretto e quindi non può visitare di persona il paziente. Problemi clinici del paziente 37 Ora passiamo all’ultima parte di cui è composta la cartella clinica e che rappresenta anche il vero punto di scambio tra requester e doctor. In questa sezione è presente un elenco di tutti i problemi clinici del paziente, con l’aggiunta di informazioni come la data, il numero di esami svolti su ognuno, il numero di report e ovviamente lo stato di ogni evento. Bisogna ora differenziare il discorso tra doctor e requester, prima di iniziare a spiegare tali opzioni è utile però ricordare che il doctor può osservare tutti gli esami svolti ma può refertare solo i problemi riguardanti la sua specializzazione. Come prima cosa il doctor 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, 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 una form apposito. Questa form, uguale sia per l’inserimento dei referti che dei messaggi, permette di inserire informazioni a campo libero, tenendo però in 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 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 in questa sezione può inserire i problemi clinici relativi ad un 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 specializzazioneproblema 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 frequenza cardiaca, l’indice di massa corporea, delle considerazioni fisiche e una conclusione esaustiva sul tutto. Ovviamente è possibile aggiungere un esame di 38 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 possono essere cambiati nel tempo. Il requester può anche leggere i messaggi e soprattutto i referti dei dottori che hanno risposto: ogni referto o messaggio contiene le informazioni 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 alla sua soddisfazione nel leggere le altre diagnosi: non è prevista comunque nessuna limitazione in tal senso. Essendo nel campo della telemedicina asincrona è possibile passare dallo stato “close” 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. Come sopra accennato è qui che avviene il vero scambio di informazioni tra requester, e quindi indirettamente tra il paziente, ed il dottore. Tutto il progetto Miro on Rails 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. 39 3. PROGETTAZIONE 3.1 Architettura L’architettura mostra la struttura generale e l’organizzazione del software; per questo tipo di progettazione si può utilizzare il diagramma dei componenti. Nel nostro caso il progetto riguarda la realizzazione di un middleware client-server e quindi il punto di vista strutturale è “logica della applicazione distribuita”. Si è deciso di organizzare il sistema nel modo seguente: Figura 9: architettura 40 Middleware Client-Server Il termine middleware si applica in generale allo strato di software, compreso tra il sistema operativo e l’applicazione utente, che viene richiamato dall’applicazione in fase esecutiva e sgrava l’applicazione da pesanti compiti generali di gestione non strettamente legati alle sue funzionalità operative specifiche. Con l’avvento dei PC utilizzati come stazioni di lavoro ed il diffondersi conseguente di applicazioni client-server, i servizi di middleware cooperativo sono diventati sempre più importanti. L’espressione client-server è molto generale e può riferirsi a quattro diversi punti di vista: • livello hardware: aumentare la scalabilità; sfruttare caratteristiche dei sistemi; integrare applicazioni • livello fisico del software: la collocazione delle parti dell’applicazione è rivolta alla riduzione del traffico su rete e allo sfruttamento ottimale delle diverse risorse • livello logico del software: si valutano le varie opzioni di distribuire logicamente i compiti nell’applicazione • livello interno dell’applicazione: l’attenzione si sposta sulla separazione dei compiti all’interno dell’applicazione, per aumentare la scalabilità, svincolarsi da specifici fornitori di DBMS, scegliere tool di sviluppo ottimale, aumentare il riuso del software Miro on Rails è un middleware client-server a livello applicativo interposto tra l'utente finale e il database. Esso è composto da un server web relazionale scritto in Ruby, perfettamente adattabile a qualsiasi piattaforma e da tre classi di client, una per il refertante (doctor), una per il richiedente (requester) e una per l’amministrazione (administrator). I client si collegano al sistema direttamente tramite web browser, senza necessità di avere installati componenti particolari. Il requisito della compatibilità è fondamentale specialmente in ambiente remoto che può essere il più vario possibile. 41 3.2 Classi di progettazione Una classe di progettazione è una classe le cui specifiche sono talmente complete da poter essere effettivamente implementate, per ogni classe di analisi: • definire una classe di progettazione ed eventualmente una interfaccia • per la classe di progettazione completare l’insieme degli attributi della classe, includendo tutte le specifiche: nome, tipo, visibilità • verificare che la classe di progettazione sia ben formata: completezza e sufficienza, essenzialità, massima coesione, minima interdipendenza Nel diagramma che segue sono state incluse tutte le specifiche, aggiunti tutti i costruttori per accedere e modificare gli attributi, in base ai criteri di completezza ed essenzialità: Figura 10: classi di progettazione 42 3.3 Progettazione cartella clinica Vengono presentate ora le soluzioni tecnologiche utilizzate per la realizzazione della cartella clinica. Una considerazione sugli argomenti trattati in questa sezione è che verrà utilizzato impropriamente il termine pagina. Questo termine non rappresenta la nostra realtà progettuale se si considera che con la tecnologia Ajax la pagina è sempre la stessa, ma non perde neanche di significato se tale termine è utilizzato per esprimere le varie “viste” disponibili all’utente. Con il termine pagina verrà quindi identificato ogni vista visualizzabile dall’utente e sarà trattato come una pagina a sé. La struttura della clinical folder è formata da due aree differenti, una laterale sinistra ed una maggiore sulla destra che ricopre il resto della cartella. La parte laterale sinistra è formata da una struttura ad albero, con questa soluzione sono ci sono due vantaggi: il primo riguarda il fatto che l’utente ha sempre sotto controllo la posizione in cui sono i dati, il secondo 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 delle informazioni cliniche che vengono inserite si modifica dinamicamente: ogni nodo sarà un dato clinico sensibile del paziente ed è con esso che l’utente interagisce. Un esempio può essere visto in figura: Figura 11: clinical folder 43 La restante parte della cartella clinica è dove vengono visualizzate le informazioni non più sotto forma di elenco, ma nel loro dettaglio. Oltre a queste due aree sono disponibile alcuni strumenti per operare sui dati, utili soprattutto per il requester. Sono infatti disponibili dei menu a tendina che permetto 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. 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 un paziente, avvia la procedura di apertura e visualizzazione della cartella clinica. Clinical Folder L’attivazione della clinical folder, avviene quando un dottore o un requester, tramite la propria interfaccia grafica, seleziona un paziente o un clinical problem. Nel primo caso come “vista” iniziale viene visualizzato il profilo del paziente, mentre nel secondo l’attenzione è puntata sul problema scelto. Analizziamo ora l’albero laterale che rappresenta la clinical folder. La sua costituzione è 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, è stata quindi utilizzata per l’anamnesi. 44 Figura 12: struttura ad albero 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 la “vista” rispettiva nel pannello centrale, 45 3.4 Confronto tra Ruby on Rails e tecnologie alternative Si supponga di voler scrivere applicazioni (in particolare applicazioni web) solide e facilmente manutibili, si hanno a disposizione varie alternative tra cui muoversi. Java, nelle sue varie interpretazioni in forma di framework quali Struts, Java Server Faces o Tapestry, è un opzione sicuramente valida e percorribile. Il punto debole della maggior parte dei presentation frameworks in Java è tuttavia dato dal tempo di apprendimento necessario ad essere produttivi, per una serie di motivi: • i frameworks si evolvono, spesso più velocemente del supporto che può essere offerto dall'IDE • spesso da progetto a progetto il framework cambia • i framework sono complessi e necessitano della familiarità con il pattern MVC, con XML, HTML, JSP, Tag Libraries, Servlet, etc. Il risultato ottenuto sarebbe un' applicazione solida, ma, probabilmente non lo otterremmo agevolemente e in maniera rapida. Inoltre il costo di manutenzione risulterebbe essere non trascurabile. Nell' eventualità in cui il contesto renda necessario il ricorso ad applicazioni J2EE di una certa complessità, la scelta risulterebbe essere quasi obbligata nell'utilizzo di Java Enterprise. Uno dei punti di forza di Ruby è senza dubbio velocità di sviluppo e la semplicità di manutenzione. Premettendo che Java è un linguaggio maturo, testato ma anche molto "verboso", ciò che si noterà abbastanza rapidamente passando a Ruby è la riduzione drastica delle righe di codice scritto. Ruby, come Java: • è un linguaggio interpretato • è un linguaggio OO (Object Oriented) • dispone di un Garbage Collector per la gestione della memoria • è portabile ed è utilizzabile con i principali sistemi operativi • ha oggetti fortemente "tipati" 46 • dispone di uno strumento di documentazione del sorgente (RDoc) molto simile a JavaDoc • ha "accessori" publici, protetti e privati, anche se il loro significato è diverso dai più comuni linguaggi OO e da Java a differenza di Java: • il codice non necessità di compilazione. • non è presente la dichiarazione del tipo • è presente la keyword end per terminare classi, metodi e strutture in genere • non sono presenti primitive • non sono presenti interfacce, ma dispone di mix-in • non è possibile fare overloading dei metodi • ma soprattutto è "dynamically typed" E' giunto il momento di focalizzare l'attenzione su un insieme di possibili rischi nell'adottare Ruby come linguaggio di programmazione al posto di Java: ¾ Come ogni nuovo linguaggio Ruby potrebbe stagnare, il che comporterebbe a una ricerca delle risorse sempre più difficile da effettuare. ¾ Non ci sono ancora molte applicazioni Ruby pubblicate e quindi non si hanno molte dimostrazioni sulla massima scalabilità del linguaggio. ¾ La comunità di Ruby non ha le dimensioni che la comunità di Java ha raggiunto, in questo modo risulta più difficile trovare componenti di terze parti, frameworks e servizi. ¾ Ruby è meno strutturato di Java e ha meno caratteristiche automatizzate per proteggere le applicazioni dagli abusi, questa flessibilità è sia un punto di forza che di debolezza. i vantaggi di Java: ¾ La comunità Java è enorme, è possibile trovare sempre sviluppatori da assumere o consultare. 47 ¾ La popolazione open source di Java cresce in continuazione. Con Java, è possibile ottenere software libero. ¾ Java è maturo ed è ancora la scelta più sicura. ¾ Java è scalabile. ¾ Java offre molta scelta. Ci sono molti standards aperti che definiscono interfacce e fornitori da qui scegliere. ¾ Ruby è relativamente nuovo, e non ha molti frameworks disponibili come Java. Infatti se c'è da sviluppare un progetto che richiede particolari caratteristiche, si possono trovare molte librerie in Java ma non in Ruby. Per queste ragioni Java ha dominato nel campo delle applicazioni web. Sicuramente Java può gestire le situazioni più complicate: • Two-phase commit: quando le applicazioni devono coordinare due risorse, come ad esempio due database, c'è bisogno di programmi sofisticati per collegare i due. Questi software usano spesso il two-pahse commit, e java lo supporta. • Oggetti distribuiti: Java può gestirli in molti modi, invece le opzioni di ruby sono più limitate. Produttività In molti casi la produttività è il fattore più importante nello sviluppo di un software. Se il proprio progetto deve essere enfatizzato per la qualità, caratteristiche, disponibilità, o performance, la produttivita è la chiave per ottenere ciò. I migliori team di sviluppo costruiscono software in tre fasi: 1) Nella prima si ha l'obbiettivo di farlo funzionare. 2) Nella seconda quello di renderlo corretto (qualità). 3) Infine di renderlo veloce (performance). Con una maggiore produttività, si ha più tempo per concentrarsi sul miglioramento delle caratteristiche, delle performance, e sulla qualità. 48 La produttività con il linguaggio Java: Nel 1996, C++ era il linguaggio di programmazione per lo sviluppo di applicazioni su piattaforme server. Il C++ non era un linguaggio molto produttivo, ma era veloce. A quel tempo la velocità era molto più importante della produttività. C++ aveva la comunità e il mercato ma quando ci furono condizioni favorevoli, emersero nuovi linguaggi di programmazione e i più vecchi sparirono. Questi linguaggi di programmazione necessitavano di una comunità per raggiungere la diffusione, ma era difficile ottenere nuovi utenti senza averne già una. Quando Sun ha creato Java, ha accettato qualche compromesso per poter incrementare la comunità in tempi assai brevi: ¾ Sun ha fatto Java simile a C++. Java ha una sintassi paragonabile a quella del C++. In questo modo non c'è stato il bisogno di creare una comunità dal nulla. ¾ C++ presenta anche dati primitivi che non sono oggetti, come caratteri e numeri. Java ha effettuato la stessa scelta. ¾ Sun come C++ ha adottato la caratteristica chiamata static typing. Static typing sta indicare che a ogni variabile viene associato rigidamente un tipo che rimane lo stesso per tutto il programma. La maggior parte dei linguaggi più produttivi usa una strategia differente, chiamata dynamic typing. ¾ I programmi scritti in Java hanno una lunghezza del codice maggiore degli stessi programmi scritti in linguaggi dinamici come Ruby. Molti pensano che programmi più brevi riducano i costi di mantenimento in maniera proporzionale. ¾ Lo static typing di Java richiede un compilatore, così il compilatore può controllare certi dettagli per i programmatori, come le forme di compatibilità tra due parti di un programma. Come conseguenza, gli sviluppatori Java devono effettuare molte compilazioni. Gli sviluppatori ruby non ne hanno bisogno. ¾ Le primitive Java, non oggetti; questo significa che le librerie Java sono spesso più grandi delle librerie per linguaggi puramente orientati agli oggetti. 49 Una possibile alternativa a Ruby on Rails Una possibile combinazione di tecnologie open source, alternative a Ruby on Rails, che uno sviluppatore potrebbe utilizzare per risolvere i problemi è questa: ¾ Il framework MVC Struts permette di separare modelli, viste e controllers. La maggior parte degli sviluppatori ricorrono a opzioni come Tapestry e JSF (JavaServer Faces) che stanno emergendo, ma Struts è il più popolare tra i framework MVC. ¾ Il framework Spring un che ha come obiettivo principale quello di gestire la complessità nello sviluppo di applicazioni "enterprise" in ambiente Java. ¾ Hibernate permette di accedere ai database relazionali attraverso i tradizionali oggetti Java. Ognuna di queste tecnologie permette ai programmatori di scrivere codice con tempi minori rispetto ai frameworks Java tradizionali ma c'è un costo: sono complessi. Considerazioni sui costi Ridurre la complessità del progetto attraverso il miglioramento dei processi di sviluppo o del linguaggio di sviluppo comporta diversi benefici. La scelta di un linguaggio di programmazione può influenzare i costi: • Più produttività porta ad aver bisogno di un numero minore di sviluppatori per progetto. • Si spende meno fatica sulla comunicazione per piccoli progetti. • Avere meno sviluppatori per progetto comporta anche costi di managment minori per progetto. 50 Utilizzo di un framework con Ruby e con Java Task Ruby Java Installare un framework gem install framework • Identificare tutte le dipendenze • Installare ogni dipendenza • Installare il framework Configurarlo • Configurazione minima • Installare le librerie necessarie con gem • Eseguire l'applicazione • Aggiungere le librerie necessarie al progetto • Settare le variabili come classpath • Configurare con XML • Compilare e eseguire l'applicazione Scegliere un framework • Poche scelte • Molte scelte con differenti trade-offs 51 3.4.1 Rails Rails è un un framework open source per applicazioni Web, altamente produttivo, che implementa strettamente, e in maniera pressochè automatica, il pattern MVC. Dispone di uno strumento ORM molto potente e risulta ideale per la creazione di una "classica" applicazione web basata su DBMS. Gli obiettivi che RoR cerca di raggiungere sono. DRY (Don't Repeat Yourself): si riferisce al concetto secondo il quale non ci debbano essere duplicazioni in una applicazione Rails. Ogni idea deve essere espressa una sola volta. Questo obiettivo viene raggiunto utilizzando il linguaggio Ruby. Convention Over Configuration: questo concetto è forse il più importante tra quellli qui elencati. Si riferisce al fatto che Rails ha "sensible default" per pressoché ogni componente dell'applicazione. Questo implica che il lavoro principale si riduce a mettere insieme i pezzi piuttosto che programmare. Agility: il concetto fa parte della struttura di Rails. Nella struttura del framework il concetto è già implicito con una serie di strumenti che orientano (e spingono quasi) il programmatore a questo approccio. Dal punto di vista architetturale RoR non si discosta troppo da una classica applicazione Web basata su di un framework Java tipo Struts. Se a questo si aggiunge poi uno strumento ORM (tipo Hibernate) per il mapping del DB, le differenze si limitano al linguaggio e ad alcune convenzioni specifiche del framework. Facendo un confronto con Struts, il framework per applicazioni web Java più utilizzato, si nota come in Rails non sono presenti file (xml) di configurazione (struts.xml) con il fine di mettere insieme le varie componenti dell'applicazione (realizzate in maniera indipendente) ma ci si basa piuttosto sulla semantica. Ovviamente tutte le convenzioni utilizzate sono suscettibili di override. 52 Sicuramente Ruby e Rails risultano essere sufficientemente maturi per implementazioni in contesti "non enterprise" non in cui non si hanno di solide, ma costose applicazioni Java EE, Rails fornisce una valida soluzione Ajax-based, MVC e Object Oriented. Progetto Linguagg Ajax MVC I18n io Framewo i10n & ORM Testing DB Security framewor migration Framewo framewor rk(s) k(s) rk Template Caching Form Validation Framewo Framewo Framework(s) rk(s) rk(s) k(s) Si, Active Si, Si, Active Si, Ruby on Ruby Si, Rails Prototype Record e Localizati Record & Action script.acu Pack Unit Si Tests, on Plug- Functiona in l Tests e Si, plug- Si Si Si in Integratio lo.us n Tests Tapestry Google Java Java Si Si Si Si Si Si, Si, integrato tapestry5- con acegi Hibernate library Si Si, built-in validation system Si, Web integrato Toolkit con Hibernate 53 4. IMPLEMENTAZIONE LATO CLIENT 4.1 Realizzazione Database Figura 13: database andromeda Abbiamo chiamato il database “andromeda” e presenta 21 tabelle come si vede in figura 13. Ora esaminiamo singolarmente la struttura delle tabelle: Tabella Funzione Collegamento allergies elenca le possibili allergie anamnesis_allergologies allergy_categories elenca le categorie di allergie allergies anamnesis_allergologies contiene le allergie di ogni paziente 54 Tabella anamnesis_familiars Funzione Collegamento contiene le informazioni sui familiari di ogni paziente anamnesis_family_pathologies contiene le patologie parenti di ogni paziente dei anamnesis_pathologicals contiene le patologie di ogni anamnesis_allergologies paziente anamnesis_phisyologicals contiene informazioni allergies fisiologiche su ogni paziente ecgs contiene i dati di ogni ECG effettuato events contiene i problemi clinici dei visits, forums, reports pazienti exams contiene l’elenco degli esami exams_users contiene l’elenco dei pazienti visits, exams, users da collegare agli esami o problemi clinici exam_files contiene allegati forums contiene i messaggi che si possono scambiare i dottori laboratory_exams contiene i dati di laboratorio locations contiene informazioni su structures, users residenza e luogo di nascita pathologies elenca le possibili patologie patients contiene i dati generici di ogni tabelle anamnesis, visits, paziente events reports contiene i referti fatti dai dottori sugli esami dei problemi clinici structures contiene i dati sulle strutture patients, users ospedaliere l’elenco dei file anamnesis_pathologicals ,anamnesis_family_path ologies 55 users contiene i dati di ogni tipo di patients, utente (administrator, exams_users, requester, doctor) reports, forums visits, events, visits contiene i dati degli esami dei ecgs, exams_file, pazienti laboratori_exams Di seguito sarà riportato lo schema E-R del database: Figura 14: schema E-R database 56 4.2 Ambiente di sviluppo e tecnologie software utilizzate In questa sezione tratteremo in modo dettagliato gli aspetti implementativi che caratterizzano il progetto. In particolare focalizzaremo l’attenzione sui framework impiegati, l’ambiente di sviluppo, i linguaggi di programmazione e tutte le tecnologie di supporto alla realizzazione di Miro on Rails. 4.2.1 Eclipse con plugin Aptana + Radrails + Ruby Eclipse è un progetto open source legato alla creazione e allo sviluppo di un IDE (sistema di sviluppo integrato) ideato da un consorzio di grandi società, chiamato Eclipse Foundation, e creata da una comunità strutturata sullo stile dell'open source. E’ disponibile per le piattaforme Linux, HP-UX, AIX, Mac OS X e Windows. Il programma è scritto in Java, ma utilizza delle librerie grafiche diverse da quelle della Sun Microsystems in modo da rendere più veloce la visualizzazione (ecco perchè è così lento all'avvio...). La piattaforma di sviluppo è incentrata sull'uso di plug-in, delle componenti software ideate per uno specifico scopo (per esempio la generazione di diagrammi UML) ed in effetti tutta la piattaforma è un insieme di plug-in, versione base compresa, che chiunque può sviluppare e modificare. Nella versione base è possibile programmare in Java, con funzioni di aiuto come completamento automatico, suggerimento dei tipi di parametri dei metodi, possibilità di accesso diretto a CVS e riscrittura automatica del codice in caso di cambiamenti nelle classi. Eclipse non è un editor, ma un ambiente di sviluppo; non si può, ad esempio, aprire e compilare un qualunque file. Per usare questo IDE bisogna organizzare il proprio lavoro in progetti. Un progetto è l'insieme di file e impostazioni relativi ad un programma che si sta sviluppando. Nel caso di Java, ad esempio, il progetto conterrà non solo i file sorgenti .java ma anche l'indicazione di quale JDK usare, le librerie esterne (file .jar) e altro. Per "workspace" si intende la directory in cui vengono salvati i progetti. Al primo avvio viene suggerita una cartella all'interno della cartella di installazione di Eclipse, ma questa a lungo termine non è la scelta migliore. Se si seleziona una cartella esterna si può successivamente usare una nuova versione di Eclipse senza problemi. 57 Con Eclipse è possibile cambiare l'interfaccia grafica in base a quello che si sta facendo. Una "prospettiva" è un modo di vedere il progetto (cioè organizzare l'interfaccia grafica) e modificarlo nella maniera più comoda. Ad esempio, è possibile programmare nella prospettiva "Java", organizzare i file in "Resource", eseguire debug in "Debug", etc etc. Questa organizzazione dell'interfaccia permette di avere sottomano i comandi immediatamente utili e non dover ricordare mille scorciatoie di tastiera. I numerosissimi plugin di Eclipse sfruttano questa caratteristica per offrire funzionalià non solo testuali (editor, debug, ...) ma anche grafiche (come il plugin di supporto agli schemi UML). Rimanendo sempre nella metafora visuale una prospettiva è composta da "viste". Una vista è una tab che solitamente visualizza un output o un qualche genere di struttura. Esempi di viste: 9 "Navigator": visualizza i file contenuti in un progetto. 9 "Console": alla stregua della console riceve input e mostra l'output del programma. 9 "Problems": evidenzia i problemi di compilazione del programma. Le prospettive possono essere personalizzate spostando, aggiungendo o eliminando le viste. In questo modo si può avere l'interfaccia più congeniale al lavoro che si sta facendo. Le views posso essere chiuse, spostate, ridotte o ingrandite. Con Ctrl+F6 ci si può spostare nelle tab dei file aperti nell'editor, tenendo premuto Ctrl e usando le frecce direzionali. Plugin APTANA Aptana, Inc. fondata da Paul Colton nel 2005 ha come obbiettivo la creazione di strumenti e prodotti destinati a sviluppatori web che usano linguaggi di scripting (JavaScript, Ruby, PHP and Python) per il Web 2.0 e lo sviluppo di applicazioni Ajax. I principali prodotti di aptana sono Aptana Studio, Aptana Cloud and Aptana Jaxer. Aptana Studio è un ambiente di sviluppo integrato open source (IDE) per la creazione di applicazioni Web Ajax. Esso comprende supporto per Javascript, HTML, DOM, CSS con completamento del codice, outlining, JavaScript debugging, notifica di error e warning e 58 documentazione integrata. Aptana studio fornisce anche plugins per estendere il supporto anche a Ruby on Rails, PHP, Python, Adobe AIR, e Apple iPhone. Aptana Studio è basato su Eclipse ed è disponibile per Microsoft Windows, Mac Os e linux oppure come plugin per Eclipse. Requisiti di sistema: • Windows - 512 MB RAM, Pentium 4 • Mac OS - 512 MB RAM, G5 or Intel • Linux - 512 MB RAM, Pentium 4 Radrails è un IDE per il framework Ruby on Rails. L'obbiettivo di questo progetto è fornire ai programmatori Rails ogni cosa di cui hanno bisogno per sviluppare, gestire, testare e pubblicare le loro applicazioni. Le caratteristiche includono il controllo del codice, code assist, refactoring, debugging, servers WEBrick, generatori e strumenti per la gestione dei dati. L' IDE RadRails è basato su Eclipse RCP e include i plugin RDT e Subclipse. RadRails è anche disponibili sotto forma di plugins. CARATTERISTICHE Web: • Free e open source; multipiattaforma, stand-alone IDE o plug-in Eclipse • Real-time, help online basato su Wiki; IDE tradotto in più lingue • Scriptable usando JavaScript (“Aptana Monkey”) • Supporto (con suggerimenti per vari browser) per JavaScript, HTML, CSS • Syntax highlighting, auto completion, code assist, error reporting, etc. • Pieno supporto a JavaScript e CSS dentro il codice HTML • Lavora con tutte le librerie Ajax • Il debugger JavaScript ha integrato Firebug Rails: • Pieno supporto a Ruby, Rails, RHTML, JS, HTML e CSS. 59 • Syntax highlighting, auto completion, code assist, error reporting, outlining • Generazione di codice Ruby: costruttori, templates, accessors • Refactoring • Debugger integrato • Supporto per i generatori Rails, Rake, plugins, e gestione del server • Navigatore del database e console per le query • help integrato 4.2.2 SSH SSH (Secure SHell, shell sicura) è un protocollo che permette di stabilire una sessione remota cifrata ad interfaccia a linea di comando con un altro host. Il client SSH ha una interfaccia a linea di comando simile a quella di telnet e rlogin, ma l'intera comunicazione (ovvero sia l'autenticazione che la sessione di lavoro) avviene in maniera cifrata. Per questo motivo, SSH è diventato uno standard di fatto per l'amministrazione remota di sistemi unix e di dispositivi di rete, rendendo obsoleto il protocollo telnet, giudicato troppo pericoloso per la sua mancanza di protezione contro le intercettazioni. Il client ed il server SSH sono installati o installabili su molte versioni di UNIX, Linux, Mac OS X e Microsoft Windows, inoltre è disponibile come strumento di amministrazione su alcuni apparati di rete. Port forwarding SSH permette di realizzare dei tunnel criptati, che permettono di trasportare sessioni TCP arbitrarie all'interno della connessione criptata, permettendo di proteggere da intercettazione protocolli non sicuri, o di aggirare limitazioni di routing. Questa funzionalità è detta port forwarding, e permette di aprire un socket TCP sul client SSH (local port forwarding) o sul server (remote port forwarding). Le connessioni ricevute su questa porta vengono inoltrate dall'altro capo della connessione SSH, verso un host e una porta specificata. Ad esempio, con questo comando ci si collega ad host1, inoltrando la porta 10022 della 60 macchina in cui lanciamo il client ssh alla porta 22 di host2 attraverso un canale sicuro tra client e host1. X forwarding Il port forwarding è utile anche per trasportare applicazioni X Window attraverso una connessione SSH. SSH imposta anche automaticamente le opportune variabili d'ambiente, in modo che le applicazioni X lanciate da un terminale remoto vengano visualizzate sul display da cui è stata avviata la connessione. L'X forwarding dal lato client deve essere abilitato passando l'opzione "-X" mentre dal lato server va modificato il file di configurazione /etc/ssh/sshd_config abilitando la direttiva X11Forwarding. Meccanismi di autenticazione del client Esistono principalmente due metodi di autenticazione per controllare l'accesso ad un server ssh: • Username/Password: l'utente fornisce un nome utente ed una password, che vengono validati dal server. Questo scambio avviene all'interno di un canale cifrato, per cui non è a rischio di intercettazione. Procedura: 1. A => B: SSH_MSG_USERAUTH_REQUEST, pappy, ssh-userauth, keyboardinteractive 2. B => A: SSH_MSG_USERAUTH_INFO_REQUEST, pappy, passwordauthentication, 1, "Enter Password" 3. A => B: SSH_MSG_USERAUTH_INFO_RESPONSE, 1, "love" 4. B => A: SSH_MSG_USERAUTH_SUCCESS • Chiave pubblica: questo metodo di autenticazione è basato sulla crittografia asimmetrica. Per utilizzarlo l'utente genera una coppia di chiavi. La chiave pubblica è copiata sul server, tipicamente in un apposito file nella home directory dell'utente; la chiave privata deve essere conservata dall'utente, ed è bene che sia protetta con una 61 parola chiave (passphrase). Nella fase di accesso, il client ssh prova al server di essere in possesso della chiave privata, e in caso di successo viene consentito l'accesso. In questo modo, all'utente non è richiesto di fornire la propria password ad ogni connessione. Autenticazione del Server SSH prevede anche la verifica dell'autenticità del server. Questo serve ad evitare che un utente maligno "impersoni" il server, facendosi fornire le credenziali dell'utente (attacco man in the middle). A questo scopo, per ciascun server viene generata una coppia di chiavi asimmetriche. La chiave privata rimane sul server, la chiave pubblica deve essere installata sui client. Quando un client si collega ad un server di cui conosce la chiave pubblica, verifica che il server sia ancora in possesso della chiave privata. Se questa verifica fallisce, la connessione viene abbattuta, evitando di fornire credenziali al server. Nella pratica, quando ci si collega ad un server per la prima volta, il client chiede se si vuole accettare la chiave pubblica di questo server, e se l'utente risponde positivamente memorizza questa chiave e prosegue nella connessione. Alle connessioni successive con lo stesso server, il client ne verifica l'autenticità, e nel caso la chiave privata non corrisponda impedisce di proseguire la connessione. OpenSSH (Open Secure Shell) è un insieme di programmi che rendono disponibili sessioni crittografate di comunicazione in una rete di computer usando il protocollo SSH. È stato creato come alternativa opensource al software proprietario Secure Shell. Poiché OpenSSH deve supportare l'autenticazione, una funzione che ha svariate implementazioni nei diversi sistemi operativi, necessita di una infrastuttura per la portabilità. Piuttosto che includerla direttamente all'interno di OpenBSD o OpenSSH, viene sviluppata separatamente come sotto progetto dal OpenSSH Portability Team, e rilasciata come "portable releases". Questo modello viene utilizzato anche per altri progetti BSD come OpenNTPD. La suite OpenSSH include le seguenti utilità: • ssh, un rimpiazzo per rlogin e telnet: ssh [email protected] • SCP, un rimpiazzo rcp: scp [email protected]:~/somefile 62 • SFTP, la versione sicura di ftp: sftp [email protected] • sshd, il demone SSH: sshd Configurare SSH E' possibile installare SSH digitando da shell: sudo apt-get install ssh che provvederà ad installare automaticamente il daemon SSH che fungerà da Server. Una volta installato è possibile trovare i file di configurazione in : /etc/ssh e il file che bisogna modificare per configurare in modo corretto il Server SSH è /etc/ssh/sshd_config Una volta aperto con un editor di testo è possibile trovare i seguenti parametri: • Port 22 di default viene aperta la porta 22 per questioni di sicurezza e per evitare si essere trovata da un portscan occasionale è consigliato cambiarla possibilmente scegliendone una superiore alla 1024, in quanto fino alla 1024 sono riservate per servizi noti. • ListenAddress 0.0.0.0 di default è 0.0.0.0 che indica l’ascolto in tutte le interfacce di rete configurate e tutti gli indirizzi IP associati, pertanto è utile cambiarlo con l’indirizzo IP specifico nel quale ci si aspettano connessioni. • HostKey /etc/ssh/ssh_host_key specifica la posizione che contiene le chiavi private di un host e può essere lasciato il valore di default. Possono essere specificati più files ripetendo HostKey e cambiando il file di destinazione. • Protocol 2 indica il protocollo da utilizzare. Possono essere indicati 1 o 2 o entrambi separandoli con la virgola (1,2), tuttavia non è consigliato permettere connessioni con il protocollo 1. 63 • UsePrivilegeSeparation yes yes è il valore di default, e indica che per ogni login viene creato un processo figlio con i privilegi dell’user che ha effettuato il login per evitare tecniche di “privilege escalation” basati sui privilegi dei processi. • ServerKeyBits 1024 dice quanti bit devono essere utilizzati per la creazione della chiave di criptazione della connessione. Si preferisce di solito utilizzare 1024 che è un buon compromesso tra velocità ed efficacia di criptazione. • LoginGraceTime 120 rappresenta il tempo massimo in secondi che intercorre tra il momento in cui viene stabilita la connessione e quello in cui avviene un login con successo. • KeyRegenerationInterval 3600 rappresenta il massimo tempo in secondi che il demone aspetta prima di rigenerare una nuova chiave per la connessione corrente e non deve essere eccessivamente elevato per evitare il cracking della chiave utilizzata nella sessione corrente. • PermitRootLogin no in genere di default è impostato su yes ma è altamente sconsigliato permettere ad un utente di effettuare il login remoto come root; è molto più sicuro far si che si effettui un login come normal user e da li guadagnare i permessi di root. • IgnoreRhosts yes dichiara di ignorare i files rhosts e shosts per l’autenticazione. • IgnoreUserKnownHosts yes hosts conosciuti presente dice al daemon di ignorare la lista degli in $HOME/.ssh/known_hosts durante la RhostsRSAAuthentication. • StrictModes yes questo serve per proteggere i files nelle home degli user che di solito vengono lasciati “world-writable”. 64 • X11Forwarding no permette di disabilitare o abilitare il forwarding su X11. Se non è presente una GUI installata nel server viene settato a no. • PrintMotd yes • IgnoreUserKnownHost yes abilita la visualizzazione di /etc/motd a login avvenuto. si ignora l’utilizzo di ~/.ssh/known_host e per il login ci si basa unicamente su user e password. • SyslogFacility AUTH • LogLevel INFO ci indica il grado di prolissità dei log. I valori possibili sono QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, DEBUG3. Utilizzare un flag di DEBUG viola la privacy degli utenti pertanto non è consigliato. • RSAAuthentication yes • PasswordAuthentication yes indica se sono concessi i login con solo RSA. indica se utilizzare come metodo di accesso le password o meno. • PermitEmptyPasswords no non permette i login senza user o senza password. • AllowUsers mironrails permette il login via SSH solo agli user specificati. Da notare che gli user sono separati da spazi vuoti, quindi niente virgole o punto o altro Una volta configurato il sistema per effettuare il login al server di miro on rails è sufficente lanciare da shell il seguente comando: “ssh -l mironrails 193.205.130.163” e inserire la password. 65 4.2.3 Prototype Prototype è un Framework JavaScript che mira a facilitare lo sviluppo di applicazioni web dinamiche. Offrendo un toolkit per lo sviluppo basato sulle classi unico e facile da usare e la migliore libreria Ajax attualmente disponibile, Prototype sta velocemente diventando il termine di paragone per gli sviluppatori di applicazioni. Funzionalità: • Sviluppo semplice di applicazioni Ajax: Oltre che semplici richieste, questo modulo gestisce in maniera intelligente anche il codice JavaScript restituito dal server e fornisce classi helper per il polling. • Estensioni DOM: aggiunge diversi e utili metodi agli elementi restituiti dalla funzione $(), ad esempio, potete scrivere $(’comments’).addClassName(’active’).show() per prendere l’elemento con l’ID ‘comments’, aggiungere una classe e visualizzarla (se precedentemente nascosta). • Utilizzo di JSON (JavaScript Object Notation): JSON è un’alternativa veloce e leggera a XML nelle richieste ti tipo Ajax. Prototype non è un framework di sviluppo di applicazioni completo: non fornisce widgets o set di algoritmi standard o sistemi di I/O. Nonostante sia stato progettato per il linguaggio di programmazione Ruby, Prototype non è legato a nessuna tecnologia lato server. Prototype è distribuito come un singolo file chiamato prototype.js, pesante circa 120KB nella versione non compressa altrimenti sui 50KB. Abilitare Prototype in una pagina web è veramente semplice, è infatti sufficiente caricare prototype.js come qualsiasi altro file Javascript: <head>... <script type="text/javascript" src=".../prototype.js" ></script> ...</head> 66 4.2.4 Ajax AJAX (acronimo di Asynchronous JavaScript and XML) è una tecnica di sviluppo che si sta proponendo sul fronte delle applicazioni web interattive. L’obiettivo è quello di migliorare l’interattività, la velocità e l’usabilità delle pagine web. Questo avviene grazie allo scambio in background di pacchetti di dati, di piccole dimensione, con il server, in modo da non dover ricaricare l’intera pagina ogni volta che l’utente apporta delle modifiche. La tecnica AJAX utilizza una combinazione di diversi paradigmi d’implementazione, quali: • HTML (o XHTML) e CSS per la parte visiva; • DOM (Document Object Model) modificato attraverso un linguaggio come il Javascript per mostrare dinamicamente le informazioni e interagirvi; • L'oggetto XMLHttpRequest per interscambiare in modo asincrono i dati tra il browser dell’utente e il webserver. In genere viene utilizzato l’XML come formato di scambio dati, anche se possono essere utilizzate altre tecnologie come l'HTML preformattato, testo semplice, JSON e perfino EBML. Questi file vengono generati di solito in modo dinamico da script lato server. Il fondamento della versatilità di questa tecnica è proprio nell’oggetto XMLHttpRequest. A seconda del browser che si utilizza, prende nomi differenti oppure viene richiamato in maniera diversa. Nel caso di Internet Explorer, ad esempio è restituito da un ActiveXObject, mentre in altri browser come Mozilla, Firefox, Safari questo oggetto è supportato nativamente. La sua manipolazione permette di effettuare la richiesta asincrona di una risorsa ad un server web, in maniera indipendente dal browser. Il fatto che la richiesta sia di tipo asincrono, significa che per effettuare altre operazione non bisogna necessariamente attendere che sia stata completata, anche se da un certo punto di vista è come se stravolgessimo il tipico flusso di dati di una pagina web. Ciò che resta del vecchio flusso è il tempo di attesa, ossia il tempo che trascorre tra una richiesta dell’utente e la risposta del server, che è considerato uno dei maggiori problemi dell’utilizzo di AJAX, sia per sviluppatori che per gli utenti normali. 67 Lato Client Avendo a che fare con questo tipo di applicazioni, abbiamo subito l’impressione di avere a che fare con pagine apparentemente più interattive, dinamiche e professionali. Questo tipo di aspetti che possono sembrare superficiali , hanno invece permesso l’affermazione di tecnologie come il Flash nel contesto web. L’oggetto XMLHtttpRequest , grazie alla sua compatibilità totale con i browser più diffusi ed il supporto nativo o integrabile, ha reso possibile l’accrescimento dello sviluppo delle applicazioni internet, allargando i campi di impiego per gli sviluppatori e coinvolgendo sempre di più gli utenti, che potranno trarre numerosi vantaggi da AJAX. Per contro, bisogna tener conto di certe penalizzazioni, soprattutto nei confronti degli utenti che necessitano di tecnologie di assistenza e quelli che non dispongono di connessioni abbastanza veloci. Questo perché non potranno sfruttare tutte le potenzialità o perché, almeno la prima volta, dovranno caricare una grossa mole di dati (i vari file Javascript) e quindi potrebbero non riuscire a sfruttare nulla di quanto caricato. Tra i problemi tecnici che si possono verificare, potremmo avere casi di Javascript disabilitato , browser non aggiornati o assenza dell’oggetto XMLHttpRequest. Dal punto di vista pratico il problema più fastidioso e noto che si può riscontrare è quello relativo alle frecce presenti nel browser. Ogni interazione asincrona , ci dà la sensazione di aver cambiato stato alla pagina e quindi si ha l’abitudine a tornare indietro ciccando sulla freccia “indietro” del browser. In realtà però si verrà reindirizzati alla pagina precedente. Questo problema , oltre ad essere un vincolo di navigazione è anche un vincolo per l’indicizzazione o la possibilità di segnalare agli altri una pagina da noi visualizzata. 68 4.2.5 Mantis bug tracker Durante la fase di testing e monitoraggio di Miro on Rails abbiamo utilizzato uno strumento che ci ha permesso di avere una base comune per la comunicazione di eventuali bug o malfunzionamenti. La scelta è caduta su un software open source in linea con la nostra filosofia progettuale, Mantis bug tracker: Mantis Bug Tracker Victor Boctor Sviluppatore: Ultima versione: Ultima beta: 1.1.4 / novembre 2008 1.2.0a2 / novembre 2008 Multipiattaforma SO: PHP Linguaggio: Genere: Bugtracker Licenza: GNU GPL Sito web: http://www.mantisbt.org/ Mantis Bug Tracker è un software lato server scritto in PHP e si può appoggiare a diversi database: MySQL, MSSQL, DB2, Oracle e PostgreSQL. E’ rilasciato sotto la licenza GNU General Public License (è una licenza per software libero). 69 Funzionalità: • E' possibile farlo funzionare solamente con un webserver e un database, su qualunque sistema operativo • Possibilità di scelta tra 68 lingue differenti • Generazione automatica di ChangeLog ("Registro delle modifiche"). Il ChangeLog, di solito memorizzato su un file di testo, raccoglie, in ordine cronologico, tutti i possibili riferimenti di rintracciabilità di ogni modifica effettuata ad un progetto fino alla release attuale. Un ChangeLog, per ogni modifica, deve riportarne almeno la release di intervento, l'autore, la data, la descrizione, le motivazioni ed eventualmente la versione. • Generazione automatica di Roadmap • Visualizzazione di Report peronalizzati per ogni utente • Completa personalizzazione di ogni opzione del bug: permette di aggiungere nuovi campi ed eliminare quelli predefiniti • Sistema di ricerca con filtri • Supporto per 1 o più progetti, ognuno dei quali con politiche di accesso differenti • Creazione di sottoprogetti e categorie di progetti • Esportazioni csv, Microsoft Word e Microsoft Excel • Integrazione di una Wiki (MediaWiki,DokuWiki, XWiki e TWiki): sito web (o comunque una collezione di documenti ipertestuali) che può essere modificato dai suoi utilizzatori e i cui contenuti sono sviluppati in collaborazione da tutti coloro che ne hanno accesso, come in un forum. La modifica dei contenuti è aperta e libera, ma viene registrata in una cronologia permettendo in caso di necessità di riportare la parte interessata alla versione precedente; lo scopo è quello di condividere, scambiare, immagazzinare e ottimizzare la conoscenza in modo collaborativo 70 Requisiti: • • Mantis 1.1.x ¾ PHP 4.3.0 o superiore ¾ MySQL 4.1.1 o superiori (MS SQL e DB2 sono già supportati) ¾ Web server (Apache, IIS, ecc.) Mantis 1.2.x ¾ PHP 5.1.0 o superiori ¾ MySQL 4.1.1 o superiori (MS SQL, DB2. Oracle e PostgreSQL sono supportati ma in via sperimentale) ¾ Web server (Apache, IIS, ecc.) 71 4.2.6 Sistema Operativo Miro On Rails è un Middleware Client-Server, rappresenta uno strato software comune all’eterogeneità di classi client che interagiscono con il database centrale. Infatti ogni client hai il proprio sistema operativo, quindi dal lato client ci siamo preoccupati di esclusivamente della corretta progettazione e implementazione del Middleware. Per il server centrale invece abbiamo utilizzato il sistema operativo Ubuntu 8.04 Hardy Heron. Ubuntu è un sistema operativo open source basato sul kernel Linux. La comunità di Ubuntu è fondata sull'idea insita nella filosofia che il software deve essere disponibile gratuitamente; gli strumenti del software devono essere disponibili agli utenti nella loro lingua madre a prescindere dalle loro abilità e che gli utenti devono avere la libertà di personalizzare e modificare il software in qualunque modo lo desiderino. Per queste ragioni: 9 Ubuntu non sarà mai a pagamento e non c'è nessun extra per alcuna «enterprise edition», il miglior lavoro sarà sempre disponibile a tutti, agli stessi termini gratuiti. 9 Ubuntu comprende le migliori traduzioni e strutture d'accesso che la comunità del software libero possa offrire, al fine di renderlo utilizzabile dal maggior numero di utenti possibile. 9 Ubuntu è rilasciato regolarmente a scadenze previste; una nuova versione è rilasciata ogni 6 mesi. È possibile scegliere di usare la versione stabile o di sviluppo. Ogni edizione è supportata per almeno 18 mesi. 9 Ubuntu è votata completamente ai principi del software libero e open source; gli utenti sono incoraggiati all'uso, al miglioramento e alla diffusione del software libero e open source. Ci sono molti sistemi operativi diversi basati su Linux: Debian, SuSE, Gentoo, RedHat e Mandriva sono degli esempi. Ubuntu è un altro contendente in quello che è già un campo altamente competitivo. Quindi, cosa rende Ubuntu differente? Basato su Debian, una delle più acclamate, tecnologicamente avanzate e meglio supportate distribuzioni, cerca di creare una distribuzione che fornisca un ambiente Linux aggiornato e coerente sia per l'ambiente desktop sia per l'ambiente server. Ubuntu comprende molti pacchetti selezionati direttamente dalla distribuzione Debian e mantiene il suo software per 72 la gestione dei pacchetti che consente di installare e rimuovere facilmente i programmi. Diversamente da molte distribuzioni che comprendono una grande quantità di software che può o non può essere utile, comprende una lista ridotta di programmi, ma di alta qualità e importanza. Concentrandosi sulla qualità, Ubuntu fornisce un ambiente solido e ricco di funzionalità che può essere utilizzato sia in ambito domestico sia in ambienti commerciali. Il progetto si prende i tempi necessari per migliorare anche i più piccoli dettagli ed è in grado di rilasciare una nuova versione che incorpori i più aggiornati programmi ogni 6 mesi. Ubuntu è disponibile per le architetture i386 (processori 386/486/Pentium(II/III/IV), Athlon/Duron/Sempron), AMD64 (processori Athlon64, Opteron e i nuovi processori a 64bit Intel) e PowerPC (iBook/Powerbook, G4 e G5). Ambiente grafico: Il desktop è ciò che si vede dopo l'accesso al computer ed è ciò che si utilizza per la gestione e l'avvio delle applicazioni. L'ambiente grafico predefinito di Ubuntu è GNOME, un'importante suite per il desktop e piattaforma per lo sviluppo per Unix e Linux. È anche possibile installare gli ambienti grafici KDE e Xfce, con il loro aspetto particolare. KDE e Xfce sono resi disponibili per Ubuntu attraverso i rispettivi progetti Kubuntu e Xubuntu. È anche possibile installare una versione di Ubuntu solamente con KDE oppure con Xfce. 73 5. STRUTTURA MIRO ON RAILS Come gia detto in precedenza, il sistema Miro on Rails fornisce il servizio di tele refertazione come risposta alle esigenze diagnostiche. Nel descrivere dettagliatamente come è stato strutturato il sistema, suddivideremo il progetto in diverse aree. Parleremo quindi dei vari tipi di utente, che il sistema è in grado di fornire, delle loro funzionalità e modalità. Le tre figure principali che interagiscono con il sistema sono l’amministratore, il requester e il dottore. Ogni tipo di utente ha un suo stile grafico che lo differenzia dall’altro, l’interfaccia utilizzata da Miro on Rails è composta da delle schede diversificate in base alle funzioni di ciascun utente. La cosa che accomuna ciascun utente è la scheda iniziale, che serve a scopo informativo per gli utenti alle prime armi, che contiene una descrizione riassuntiva (diversa per ogni tipo di utente) delle varie funzioni messe a disposizione. Successivamente verrà analizzata la sezione “Cartella Clinica” "del paziente e il modo in cui si è voluta organizzare. Solo nel capitolo successivo, si tratterà invece nel dettaglio la sezione relativa al “Sistema di Messaggistica” di cui si avvale il sistema per la comunicazione virtuale tra il personale medico. Figura 15: logo Miro on rails 74 5.1 Autenticazione Ogni utente per accedere al sistema deve prima compiere una procedura di autenticazione. Un esempio di tutti i giorni è la comune procedura di autenticazione che conosciamo come login, ed è proprio quella utilizzata da Miro on Rails Un sistema di elaborazione, progettato per essere usato soltanto da utenti autorizzati, deve essere in grado di rilevare ed escludere i non autorizzati. L'accesso ad esso, dunque, viene garantito solo dopo aver eseguito con successo una procedura di autenticazione, di solito attraverso una username e una password personale. Ecco cosa appare ad un utente che si collega, tramite internet, al servizio di tele refertazione offerto da Miro on Rails all’indirizzo: http://193.205.130.163/miro/user/login. Figura 16: form di login Figura 17: autenticazione non riuscita 75 Occorre inoltre sottolineare che il meccanismo di autenticazione che abbiamo realizzato presenta un accorgimento che aumenta la sicurezza. Quando un nuovo utente riceve username e password da un administrator, deve modificare la password al primo accesso. Al primo log-in, il sistema reindirizza automaticamente l’utente nella pagina di modifica password. Successivamente l’utente potrà comunque modificare la propria password in qualunque momento. Figura 18: modifica password primo accesso 76 5.2 Administrator L’ Amministratore è un tipo di utente che si differenzia rispetto agli altri sia per il livello di gestione del sistema sia per quanto riguarda la stabilità e la sicurezza che deve garantire. Per quanto riguarda l’interfaccia grafica della sua sessione, la home presenta la seguente struttura: Figura 19: home administrator Le funzionalità a cui può accedere un administrator sono fondamentalmente tre: 1) Visualizzare gli ultimi utenti inseriti 2) Inserire un nuovo utente nel database 3) Cancellare un utente dal database inoltre, sempre nella home, può visualizzare il proprio profilo ed eventualmente modificare la password. 77 Selezionando il tasto “Last Users” comparirà la lista degli ultimi utenti inseriti e cliccando su uno di questi utenti apparirà la pagina con il profilo. Inoltre grazie alla struttura ad albero a sinistra è possibile effettuare una selezione in base al tipo di utente. Selezionando il tasto “New User” sarà possibile inserire un nuovo utente nel database, l’administrator visualizzerà la pagina di inserimento dati (figura 19). Figura 20: inserimento dati nuovo utente Nella fase di inserimento dati, assume un aspetto molto importante la scelta del tipo di utente (“User type”). Come si vede dalle figure nella pagina successiva, se si sceglie “Requester” bisognerà inserire anche le informazioni relative alla struttura ospedaliera a cui appartiene; se si sceglie “Doctor” si dovrà selezionare anche la specializzazione medica in quanto i dottori vedranno solo i problemi clinici relativi al proprio ramo specialistico; infine “Administrator” non avrà bisogno di altre informazioni aggiuntive. 78 Figura 21: scelta “User type” Un’altra funzionalità messa a disposizione dell’administrator e che solo lui può eseguire, è la possibilità di cancellare un utente dal database. In verità anche in questo caso ci sono delle restrizioni in quanto si possono cancellare solo utenti del tipo “Requester” e “Doctor” e non altri “Administrator”. Un administrator potrà essere cancellato solo se si agisce direttamente sul database centrale e questo potrà essere fatto solo da un Super Administrator del tutto trasparente al sistema. 79 Per poter cancellare un utente occorre selezionarlo dalla lista “Last Users” e premere il tasto “Delete” (vedi figura 23). Figura 22: elimina utente dal database In alto a sinistra della home page sono presenti le informazioni dell’utente che si è autenticato. Figura 23: informazioni utente Come si vede dalla figura sopra, compariranno nell’ordine: 9 nome e cognome dell’utente 9 il link a “My Profile” dove c’è il riepilogo delle informazioni personali e la possibilità di modificare la propria password 9 il link a “Exit” che permette all’utente di uscire dal sistema 80 5.3 Requester Il Requester è una tipologia di utente a cui viene assegnata come funzione principale la gestione dei pazienti, quindi un punto cardine per il funzionamento del sistema. Quello del requester è considerato un ruolo indispensabile per far si che il sistema Miro on rails fornisca un buon servizio al sistema sanitario. Questo perché tutte le sue funzionalità sono incentrate verso la figura principale da prendere in considerazione ossia il paziente. Come accennato nei paragrafi precedenti, le figure che assumeranno questo ruolo, dovranno essere persone che lavorano presumibilmente nell’ambito sanitario, ad esempio in ospedali, ambulatori o aree di emergenza e così via. L’obiettivo principale del sistema Miro on rails è quello di aiutare il servizio sanitario dei Paesi in via di sviluppo, dove la gestione la qualità e l’efficienza del servizio medico sono più scadenti. Quindi la ricerca di figure professionali che possano svolgere il compito di requester, sarà da effettuare tra le varie strutture sanitarie presenti nel territorio. Naturalmente la ricerca di persone abbastanza competenti in queste zone territoriali non sarà molto facile. E’ anche per questo motivo che per l’estensione e la comprensione di questo servizio fornito, verranno organizzate delle visite in queste zone da parte di persone competenti che spiegheranno il funzionamento del sistema e conseguentemente verrà effettuata l’assegnazione di account requester a persone che si adoperano in queste strutture sanitarie. A questo punto il sistema sarà operativo grazie alla presenza di requester che si occuperanno della gestione dei pazienti legati alle strutture in cui il requester svolge il suo compito. Le operazioni principali che un requester è in grado di effettuare sono: 1) registrazione dei pazienti 2) gestione della cartella clinica 3) gestione degli eventuali problemi clinici 81 La home del Requester avrà la seguente struttura: Figura 24: home Requester come si vede dalla figura l’interfaccia grafica della home page mostra la lista degli ultimi clinical problems inseriti. Ogni clinical problem è caratterizzato da: 9 Date open: data e ora di apertura evento 9 Patient: nome e cognome del paziente a cui è associato l’evento 9 State: stato dell’evento che può essere open, reported o request another 9 Last report: data e ora di emissione referto medico Tramite il tasto “New Patient”, il requester è in grado di inserire nuovi pazienti nel database del sistema specificando i dati personali richiesti per la registrazione, tra cui ad esempio le informazioni riguardo la residenza, la struttura ospedaliera in cui è ospitato e informazioni come telefono o e-mail. A questo punto il nuovo paziente inserito potrà usufruire del servizio fornito da Miro on rails in caso di eventuali problemi clinici. Il requester quindi dovrà farsi carico della stato dei pazienti che gestisce, aggiornando di volta in volta la loro condizione nel sistema, in modo che i medici possano avere a 82 disposizione un insieme di dati aggiornati il più possibile. Una volta che il paziente sarà inserito nel sistema, si potrà successivamente modificare il suo profilo, ossia apportare dei cambiamenti alle informazioni personali registrate in partenza. Per esempio, nel caso in cui un paziente sia deceduto, si potrà modificare il suo profilo registrando la data di decesso. Figura 25: inserimento dati paziente In realtà la fase di inserimento di un nuovo paziente passa prima per la ricerca nel database. Se infatti il paziente è già stato inserito comparirà un link al suo profilo, in caso contrario comparirà un link alla pagina di inserimento dati. 83 Figura 26: ricerca paziente Come per gli utenti administrators, anche il requester può visualizzare una lista degli utenti da lui registrati nel sistema. Da sottolineare il fatto che ogni requester visualizzerà nella 84 propria lista solo ed esclusivamente i pazienti da lui inseriti e quindi si occuperà della loro gestione; ogni requester avrà perciò solo la responsabilità dei pazienti da lui registrati. Figura 27: lista pazienti Selezionando un record della lista, si potrà accedere alla cartella clinica relativa al paziente in questione. Il requester, una volta entrato nella cartella relativa ad un determinato paziente, potrà visualizzare il suo profilo ed eventualmente apportare delle modifiche. Inoltre si occuperà della gestione delle informazioni riguardanti lo stato di salute del paziente, come ad esempio le allergie da cui è influenzato, informazioni mediche riguardanti il suo nucleo familiare come ad esempio le patologie presenti in famiglia, le patologie presenti nell’ individuo e tutte le relative informazioni fisiologiche. Il requester sarà quindi in grado di gestire completamente i dati dei suoi pazienti, con possibilità di eventuali aggiunte e modifiche. Oltre al profilo del paziente e alle varie anamnesi presenti, la cartella clinica del paziente conterrà anche i problemi clinici. Questa può essere definita la funzione più importante per il requester, poiché l’obiettivo del sistema è proprio quello di fornire una procedura di refertazione riguardo ai problemi clinici presenti. Infatti i medici registrati nel sistema 85 avranno proprio il compito di emettere dei referti o dei pareri personali sui problemi clinici presenti. Figura 28: cartella clinica Tramite il tasto “Last Clinical Problem”, il requester sarà in grado di visualizzare tutti i problemi clinici relativi a tutti i pazienti da lui gestiti. In sostanza ottiene la stessa lista che si presenta nella home page al momento dell’accesso alla propria sessione di lavoro. Un problema clinico potrà essere composto da uno o più esami, e l’insieme di questi verrà considerato come un'unica entità ( l’entità “problema clinico”). Il personale medico fornirà veri e propri “referti” da allegare al problema clinico di un certo paziente, o più semplicemente fornirà un parere personale cioè un “messaggio”. In questo modo il requester, visualizzando la lista vedrà lo stato attuale di un certo problema clinico, si baserà sugli eventuali interventi effettuati dai medici del sistema e di conseguenza apporterà eventuali modifiche al problema. Il valore del campo “stato” è conseguente all’intervento medico che è avvenuto per quel dato problema e alla gestione apportata dal requester. Gli stati del problema possono essere: 86 • Open: se il problema clinico inserito dal requester non ha ricevuto ancora nessun referto medico. Lo stato permane quindi ad “open” anche se sono arrivati eventuali messaggi. • Reported: se il problema clinico ha ottenuto un referto da un medico. • Another request: se il requester richiede una second opinion per il problema clinico in questione. • Closed: se il requester considera chiuso il problema clinico in questione, ossia non ha bisogno di ulteriori risposte dal personale medico (in questo caso il problema clinico scomparirà automaticamente dalla lista). Figura 29: aggiungi problema clinico Nel momento in cui si visualizza la lista dei problemi clinici presenti nella cartella clinica di un paziente, è possibile aggiungere un nuovo problema clinico procedendo come mostrato nella figura 24. Verrà mostrata una pagina dove è possibile inserire tutti i dati relativi ad un nuovo esame clinico (“New Consultation”). 87 Figura 30: inserimento dati esame clinico 88 Figura 31: upload file diagnostico Sicuramente l’aspetto più importante in questa fase è l’upload di eventuali file diagnostici come per esempio un semplice elettrocardiogramma. Questo aspetto rappresenta il punto cardine dell’intero sistema di telerefertazione, infatti il medico che effettuerà il download dei file, di natura diagnostica, sarà in grado di rilasciare un referto medico o una second opinion sul caso clinico pur trovandosi in un tutt’altro luogo. Un altro aspetto da sottolineare è la facilità con cui il requester può effettuare un esame clinico, si presuppone infatti che sia un tecnico e non uno specialista. Ha a disposizione una serie di form già impostate dove deve inserire solo i valori. Nelle figure seguenti viene riportato l’esempio di “Insert laboratory exam” e la stessa cosa vale per “Insert ECG”. Se il requester si trova a dover effettuare un esame di laboratorio e ha gli strumenti adeguati, può continuare l’esame clinico arricchendolo di tutta una serie di informazioni aggiuntive di cui deve solo seguirne la schematizzazione. In definitiva possiamo dire con assoluta certezza che Miro on rails mette a disposizione del requester tutti gli strumenti necessari ad effettuare un esame clinico pressoché completo. Questo però non è vincolante, in quanto se non si dispone dei mezzi necessari ad effettuare tutti gli esami, si potrà in ogni caso inserire le sole informazioni di cui si ha conoscenza o utilizzare il solo meccanismo di upload dei file diagnostici. 89 Figura 32: Insert laboratory exam 90 5.4 Doctor L’ultima sezione che dobbiamo prendere in considerazione, è la sezione Dottore. L’obiettivo principale degli utenti appartenenti a questa tipologia, è quello di fornire un servizio in grado di aiutare i pazienti fornendo un consulto specialistico a distanza. Per far si che questo possa avvenire, dovrà essere presente un canale di comunicazione tra l’entità paziente e l’entità dottore. Nel nostro caso specifico, la gestione dei dati clinici dei pazienti è portata avanti dai requester, il paziente quindi è affidato nelle mani di uno specifico requester. Per questo motivo il sistema di comunicazione non lega la figura del dottore esattamente con l’entità paziente, ma con il requester che si occupa della sua gestione, e che conseguentemente terrà il paziente al corrente di tutti gli aggiornamenti, come per esempio la presenza di nuove diagnosi legate alle sue problematiche. Tutto questo ci fa capire l’importanza dell’interconnessione tra le figure requester e il dottore. Essere in possesso di un account di tipo dottore, implica come requisito una certa professionalità. Sarà quindi compito del “super administrator” occuparsi dell’assegnazione di account di tipo dottore, il quale si assumerà la responsabilità del suo operato, pertanto dovrà fare in modo che questi account vengano assegnati a persone qualificate. Tramite questa procedura di assegnazione, tutti i dottori che verranno inseriti nel sistema formeranno il personale medico di Miro on rails. Questi medici verranno selezionati tra le varie strutture sanitarie che vorranno contribuire al servizio fornito dal nostro sistema. Quindi il nostro personale medico sarà caratterizzato dalla presenza di molti dottori, appartenenti a diverse strutture anche molto lontane tra loro. L’obiettivo sarà quindi quello di far aderire il maggior numero di medici qualificati nelle più svariate specializzazioni affinchè il servizio offerto sia il più efficiente possibile. 91 Figura 33: home page doctor Nella home del doctor compare la lista dei clinical problem, ciascun record corrisponde ad uno specifico problema clinico relativo ad un determinato paziente. I campi della lista contengono informazioni relative alla data di creazione del problema clinico, il suo stato (la spiegazione dei vari stati è già stata effettuata nel paragrafo precedente), il nome del paziente a cui è riferito il problema e il requester che si occupa della sua gestione. In realtà il dottore non visualizzerà nella lista tutti i problemi clinici presenti nel sistema, questo dipenderà dalla politica di gestione che si è voluta adottare. Innanzitutto, quando viene creato un utente di tipo dottore, l’administrator dovrà stabilire per quali specializzazioni il dottore sarà ritenuto qualificato. Quindi le specializzazioni possedute da un dottore caratterizzeranno il suo livello di operatività all’interno del sistema, in altre parole, il dottore riuscirà a visualizzare nella lista in figura, solo i problemi clinici inerenti alle specializzazioni da lui in possesso. Come abbiamo detto in precedenza il problema clinico è composto da uno o più esami clinici, ed è visto come un unico evento. Quindi i referti e i messaggi non saranno legati ai singoli esami clinici ma all’evento. Quindi potrebbe capitare ad esempio che ci siano problemi clinici composti da più esami relativi a diverse specializzazioni, un dottore 92 potrebbe quindi possedere o tutte le specializzazioni legate a quel problema clinico o almeno una di esse o nessuna. La politica di gestione che si è voluta adottare in questo caso, stabilisce che un dottore potrà visualizzare nella lista solo i problemi clinici che hanno almeno un esame clinico relativo ad una qualsiasi delle specializzazioni possedute. Un’altra specifica relativa alla politica di visualizzazione è legata allo stato, i dottori saranno in grado di visualizzare solo i problemi clinici in stato di “open” o “request another”. In questo modo si cerca di dare priorità ai problemi che non hanno ottenuto finora ancora nessun referto medico (stato “open”) e quelli che hanno già ottenuto un referto, ma che ne richiedono una second opinion (stato “request another”). Gli altri problemi in stato “reported”o “closed” non compaiono in questa lista. Il fatto di applicare queste specifiche alla visualizzazione è dovuto al fatto che senza di queste i dottori avrebbero a che fare con delle liste enormi e quindi difficili da gestire. Riassumendo, con l’introduzione delle specifiche relative alle specializzazioni e allo stato dei problemi si effettua un filtraggio, si cerca cioè di limitare il numero di problemi da visualizzare ed avere una gestione più efficiente da parte dei medici. Quando verrà selezionato dalla lista un determinato problema clinico, in automatico verrà aperta la Cartella Clinica del relativo paziente. Il dottore avrà non solo la possibilità di esaminare il problema clinico che ha selezionato, ma anche tutti gli altri relativi allo stesso paziente. In questo modo avrà una visione globale dello stato del paziente e sarà più facile emettere referti o semplici pareri. Anche il dottore come il requester, è in grado di accedere alla cartella clinica dei pazienti, anche se le funzioni che potrà svolgere sono diverse. Il dottore sarà in grado di visualizzare i problemi clinici e le informazioni personali dei pazienti ed inoltre, cosa più importante, sarà in grado di fornire delle risposte alle problemi clinici tramite referti o semplici pareri personali. Le funzioni fondamentali che può utilizzare un dottore sono: 1) Add report: aggiunta di un referto medico 2) Add message: aggiunta di un parere medico 93 Figura 34: Add report e Add message Un aspetto molto importante è che il dottore può fare il download, sul proprio computer, di eventuali file allegati al problema clinico. In questo modo può avere una situazione ancora più dettagliata del caso clinico. Figura 35: file di natura diagnostica In figura è mostrato un esempio con due allegati (“Attachments”). Cliccando su uno dei due file è possibile salvarlo sul proprio PC. 94 Figura 36: salva allegato 5.5 Sezione messaggi Un’area importante che caratterizza il portale Miro on rails è quella incentrata sullo scambio di messaggi da parte dei dottori. In questo modo il personale medico del sistema sarà in grado di fornire delle risposte più precise e di confrontarsi su specifici problemi clinici. Figura 37: Last topics 95 Nella figura si vede che il dottore può visualizzare la lista degli ultimi messaggi e selezionando un record dalla lista può leggere un messaggio relativo ad uno specifico problema clinico. Figura 38: esempio messaggio Il messaggio è accompagnato da una serie di informazioni come: 9 Message cod.: codice identificativo del messaggio 9 data e ora di inserimento messaggio 9 nome del dottore e link al suo profilo Figura 39: operazioni “Add message” e “Close” 96 Sul messaggio si possono compiere due operazioni: “Add message” con la quale si aggiunge un messaggio ad uno specifico problema clinico e “Close” con la quale si chiude il messaggio e si ritorna alla lista iniziale. 97 6. CONCLUSIONI E SVILUPPI FUTURI 6.1 Conclusioni Lo scopo del progetto, era quello di creare una struttura software flessibile che offrisse servizi di telerefertazione distinguendo in modo netto la fase di effettuazione dell’esame dalla fase di refertazione. L’architettura che è stata realizzata è in grado di fornire un valido supporto anche per altri servizi tipici di applicazioni di telemedicina come la consultazione della cartella clinica on-line. Un aspetto importante del progetto è stato sicuramente l’utilizzo del framework Ruby on rails, la scelta è stata dettata dalla fama che in questi anni ha ottenuto Rails nello sviluppo di applicazioni web al pari di altri framework più consolidati scritti in Java. Questa scelta ci ha dato la possibilità di rilevare interessanti aspetti di confronto con altre tecnologie. Utilizzando Ruby on rails siamo arrivati alla conclusione che il termine più adatto per definire il suo punto di forza è la “produttività”. In molti casi la produttività è il fattore più importante nello sviluppo di un software, se il proprio progetto deve essere enfatizzato per qualità, disponibilità e performance, la produttività è la chiave per ottenere ciò. Con una maggiore produttività si ha quindi più tempo per concentrarsi sul miglioramento delle caratteristiche dell’applicazione. Un altro aspetto fondamentale ha riguardato la verifica del corretto funzionamento dell’applicazione Miro on Rails e della sua usabilità, occorre infatti ricordare che l’applicazione è rivolta a molte tipologie di utenti: dottori, personale tecnico sanitario, missionari e utenti senza alcuna competenza specifica. Abbiamo avuto la possibilità di testare il sistema utilizzando come server centrale una macchina del dipartimento DEIT (Dipartimento di Elettronica Intelligenza Artificiale e Telecomunicazioni) della facoltà di ingegneria di Ancona. Il sistema è attualmente on-line all’indirizzo http://193.205.130.163/miro/user/login. In questa fase abbiamo coinvolto una piccola comunità di medici e tecnici del settore affinchè utilizzassero l’applicazione e ci informassero di tutti i miglioramenti possibili. In conclusione possiamo dire con assoluta certezza che il sistema realizzato è stabile ed efficiente anche se ci sono numerosi sviluppi da portare avanti. 98 6.2 Sviluppi futuri Nonostante il portale Miro on rails sia stabile, ben organizzato ed efficiente dal punto di vista delle funzionalità, ci possono essere numerosi spunti per sviluppi futuri. In questa sezione vorremmo focalizzare l’attenzione su quattro possibili sviluppi che in futuro potrebbero migliorare le funzionalità di base dell’applicazione. 1) download e upload più interattivi con possibilità di resume: attualmente se la connessione cade durante l’upload (o un download) di un file diagnostico, bisogna ripetere l’operazione dall’inizio. Sarebbe più efficiente prevedere un protocollo di resume che permetta di riprendere l’operazione laddove è stata interrotta. 2) possibilità per il requester di lavorare off-line: se il requester si trova in zone senza collegamento internet ma riesce ad effettuare un esame clinico, dovrebbe salvare tutti i dati da qualche parte per poi ricopiare il tutto sul portale quando raggiunge una zona con connessione. Si trova quindi a dover compiere due volte le stesse operazioni. Molto più vantaggioso sarebbe progettare una plugin che renda il portale visibile in locale, poi quando ci si connette ad internet tutte le informazioni in locale verrebbero automaticamente inviate al server. 3) meccanismo di timestamping per problemi di tipo legale tra dottore e requester: supponiamo che il requester inserisca un certo problema clinico e che successivamente il dottore visualizzi tale problema ed emetta un referto medico. Potrebbe succedere che il requester abbia tralasciato informazioni importanti e che li aggiunga al problema in questione successivamente dopo l’arrivo del referto. Data la mancanza di alcuni dati, il dottore potrebbe aver sbagliato la sua diagnosi. Nel caso in cui tale referto provochi danni al paziente, il dottore potrebbe essere perseguibile legalmente. Utilizzando invece il meccanismo di timestamping verrà registrata la data e l’ora per ogni operazione di inserimento, cancellazione e modifica sui dati. In questo caso, il requester, potrebbe verificare se il referto è arrivato prima dell’inserimento degli esami clinici mancanti. 4) meccanismo di firma digitale con smart card per i dottori 99 5) servizi disponibili direttamente al paziente: l’obiettivo futuro sarà sfruttare la grande flessibilità con sui è stato progettato Miro on rails, espandendo le sue funzionalità per poter raggiungere direttamente gli utenti finali, i pazienti, in modo da rendere il servizio sanitario efficiente e capillare. 100 Appendice A: CONTROLLERS # Filters added to this controller apply to all controllers in the application. # Likewise, all the methods added will be available for all controllers. class ApplicationController < ActionController::Base layout "layout" # Pick a unique cookie name to distinguish our session data from others' session :session_key => '_tesi_session_id' def init @root_directory= "/miro" @user_logged=is_logged redirect_to(:controller=>:user,:action=>:login) unless @user_logged end def is_logged if session if session[:user_id] user=User.find(:first,:conditions=>["id= ? ",session[:user_id]]) return user elsif cookies[:user_id]!=nil user=User.validate_login_with_cookie(cookies[:user_id],cookies[:user_pass word]) if user session[:user_id] = user.id return user end end end nil end def get_cities @cities=City.find(:all,:conditions=>["province_id=?",params[:province_id] ],:order=>"name") @province_name=Province.find(:first,:conditions=>["id=?",params[:province _id]]).name render :text=>'<label for="'+params[:tag]+'_city" class="DetailTitle">City</label><br /><select name="'+params[:tag]+'city_id" id="'+params[:tag]+'city_id" class="UserTextFieldHalf">'+ render_to_string(:partial => "/city_options",:collection=>@cities)+"</select>" end def get_provinces @provinces=Province.find(:all,:conditions=>["country_id=?",params[:countr y_id]],:order=>"name") render :text=>'<label for="'+params[:tag]+'provinces" class="DetailTitle">Province:</label><br /><select 101 name="place['+params[:tag]+'province_id]" id="'+params[:tag]+'province_id" class="UserTextFieldHalf" >'+ "<option value=''>Province</option>"+render_to_string(:partial => "/province_options",:collection=>@provinces)+"</select>"+ "<script>document.getElementById('"+params[:tag]+"province_id').onchange= function(){new Ajax.Updater('"+params[:tag]+"city_idDiv', '/miro/index/get_cities', {asynchronous:true, evalScripts:true, parameters:'tag="+params[:tag]+"&province_id='+this.value});return false;}</script>" end def get_visit_body init render :partial=>"/patient/view_visit",:object=>Visit.find(:first,:conditions=>[ "id=?",params[:id]]) end def get_reports_body init render :partial=>"/view_report",:object=>Report.find(:all,:conditions=>["event_i d=?",params[:id]],:order=>"id desc") end def get_topic_body init render :partial=>"/view_message",:object=>Forum.find(:all,:conditions=>["event_i d=?",params[:id]],:order=>"id desc") end def get_form_allergology @form=params[:new] ? "new" : "edit" @anamnesis_allergology=params[:new] ? AnamnesisAllergology.find(params[:id]) render :partial=>"fields_allergology" end def get_form_family @form=params[:new] ? "new" : "edit" @anamnesis_familiar=params[:new] ? AnamnesisFamiliar.find(params[:id]) render :partial=>"fields_familiar" end AnamnesisAllergology.new : AnamnesisFamiliar.new : def get_form_family_pathology @form=params[:new] ? "new" : "edit" @anamnesis_family_pathology=params[:new] ? AnamnesisFamilyPathology.new : AnamnesisFamilyPathology.find(params[:id]) render :partial=>"fields_family_pathology" end def get_form_pathological @form=params[:new] ? "new" : "edit" @anamnesis_pathological=params[:new] ? AnamnesisPathological.new : AnamnesisPathological.find(params[:id]) 102 render :partial=>"fields_pathological" end def get_form_physiological @form=params[:new] ? "new" : "edit" @anamnesis_physiological=params[:new] ? AnamnesisPhysiological.new : AnamnesisPhysiological.find(params[:id]) render :partial=>"fields_physiological" end def get_form_visit @form="new" #params[:new] ? "new" : "edit" @visit=Visit.new #params[:new] Visit.find(params[:id]) render :partial=>"fields_visit" end ? Visit.new : def get_form_info @form="edit" @patient=Patient.find(:first,:conditions=>["id=?",params[:id]]) render :partial=>"fields_info" end def get_edit_form_familiar @form="edit" @anamnesis_familiar=AnamnesisFamiliar.find(:first,:conditions=>["patient_ id=?",params[:id]]) render :partial=>"fields_familiar" end def get_edit_form_physiological @form="edit" @anamnesis_physiological=AnamnesisPhysiological.find(:first,:conditions=> ["patient_id=?",params[:id]]) render :partial=>"fields_physiological" end def get_edit_form_allergology @form="edit" @anamnesis_allergology=AnamnesisAllergology.find(:first,:conditions=>["id =?",params[:id]]) render :partial=>"fields_allergology" end def get_edit_form_family_pathology @form="edit" @anamnesis_family_pathology=AnamnesisFamilyPathology.find(:first,:conditi ons=>["id=?",params[:id]]) render :partial=>"fields_family_pathology" end def get_edit_form_pathological @form="edit" 103 @anamnesis_pathological=AnamnesisPathological.find(:first,:conditions=>[" id=?",params[:id]]) render :partial=>"fields_pathological" end def get_city city=GeoKit::Geocoders::GoogleGeocoder.geocode(params[:place_city]) render :text => city.precision=="city" ? city.full_address : "" end def set_city(city_s) city=Location.find(:first,:conditions=>["full_address=?",city_s]) if city return city.id else city=GeoKit::Geocoders::GoogleGeocoder.geocode(city_s) if city loc=Location.new loc.lat=city.lat loc.lng=city.lng loc.country_code=city.country_code loc.full_address=city.full_address loc.precision=city.precision loc.city=city.city loc.state=city.state loc.save return loc.id else return nil end end end end class DoctorController < ApplicationController def get_exam_file init if @user_logged and @user_logged.class.to_s=="Doctor" @exam_file=ExamFile.find(:first,:conditions=>["id=?",params[:id]]) send_file @exam_file.get_filename if @exam_file end end def new_report init if @user_logged.class.to_s=="Doctor" @page_body="fields_report" @page_title="New Report" @form="new" @report=Report.new if request.post? 104 @report=Report.new(params[:report]) @report.user_id=@user_logged.id @report.event_id=params[:id] @report.created_at=Time.now [email protected] if @report.save event.state="reported" event.save end render :partial=>"/view_report",:collection=>Report.find(:all,:conditions=>["eve nt_id=?",event.id]) #redirect_to(:controller=>:patient,:action=>:view,:id=>event.patient_id,: section=>"visits") end else redirect_to(:controller=>:doctor,:action=>:permission_denied) end end def new_message init if @user_logged.class.to_s=="Doctor" #@page_body="fields_message" @page_title="New Message" @form="new" @forum=Forum.new if request.post? @forum=Forum.new(params[:forum]) @forum.user_id=@user_logged.id @forum.event_id=params[:id] @forum.save render :partial=>"/view_message",:collection=>Forum.find(:all,:conditions=>["eve nt_id=?",@forum.event_id]) end else redirect_to(:controller=>:doctor,:action=>:permission_denied) end end def get_form_report init if @user_logged.class.to_s=="Doctor" @report=Report.new render (:partial=>'fields_report', :object=>params[:id]) end end def get_form_message init if @user_logged.class.to_s=="Doctor" @report=Report.new render (:partial=>'fields_message', :object=>params[:id]) end end 105 end class IndexController < ApplicationController def index init @page_body="index" @page_title="Home" @page="index" #@reports=Report.find(:all,:include=>{:event=>:user},:conditions=>"user=? "+@user_logged.id) if @user_logged if(@user_logged.type.to_s=="Administrator") @users=User.find(:all,:conditions=>"creator_id=#{@user_logged.id}",:order =>"id desc") elsif(@user_logged.type.to_s=="Doctor") @events_specialization=Event.find(:all, :include=>:patient, :joins=>"right join visits on events.id=visits.event_id and visits.exam_id in (select exam_id from exams_users where exams_users.user_id=#{@user_logged.id})", :conditions=>"events.state<>'close' and events.state<>'reported' and events.user_id<>#{@user_logged.id}", :order=>"events.id") else @events=Event.find(:all,:conditions=>"events.state<>'close' and user_id=#{@user_logged.id}",:order=>"id desc") end end end def get_events_specialization init @events_specialization=Event.find(:all, :include=>:patient, :joins=>"right join visits on events.id=visits.event_id and visits.exam_id in (select exam_id from exams_users where exams_users.user_id=#{@user_logged.id})", :conditions=>"events.state<>'close' and events.state<>'reported' and events.user_id<>#{@user_logged.id}", :order=>"events.id") if @user_logged render :partial=>"/index/events_specialization" end def get_last_events init @events=Event.find(:all,:conditions=>"events.state<>'close' user_id=#{@user_logged.id}",:order=>"id desc") render :partial=>"/index/last_events" end and 106 def get_last_users init @users=User.find(:all,:conditions=>["creator_id=#{@user_logged.id} and type like ?",(params[:type] ? params[:type] : '%')],:order=>"id desc") if @user_logged render :partial=>"/index/last_users" end def get_last_topics init @topics=Forum.find(:all,:include=>[:event],:conditions=>"forums.user_id=# {@user_logged.id}",:order=>"forums.id desc") if @user_logged render :partial=>"/index/last_topics" end def get_last_patients init @patients=Patient.find(:all,:conditions=>"user_id=#{@user_logged.id}",:or der=>"id desc") if @user_logged render :partial=>"/index/last_patients" end end class PatientController < ApplicationController #upload_status_for :new_visit def new init @page_body="fields_info" @page_title="New Patient" @form="new" @patient=Patient.new if request.post? @patient=Patient.new(params[:patient]) @patient.born_location_id=set_city(params[:born]) @patient.residence_location_id=set_city(params[:residence]) params[:residence] @patient.user_id=session[:user_id] if @patient.save @page="new_patient" if redirect_to({:controller=>:patient,:action=>:view,:id=>@patient.id,:secti on=>"info"}) else @page="new_patient" render :partial=>"/patient/fields_info" end end end def search 107 init list="" if (params[:name] and params[:surname] and params[:born_date]) @patients=Patient.find(:all,:include=>:born_location,:conditions=>"name like \"#{params[:name]}\" and surname like \"#{params[:surname]}\" and born_date=\"#{Date.parse(params[:born_date])}\" #{params[:born_city] and params[:born_city]!="" ? "and locations.full_address=\"#{params[:born_city]}\" " : ""}") for i in @patients list+="<a href='#{@root_directory}/patient/view/#{i.id}?section=info'>#{i.id} #{i.residence_address} #{i.residence_location_id ? i.residence_location.full_address : ""}</a>" end list="" if @patients.length==0 end render :text=>list end def view init @page_body="view" @patient=params[:id] ? Patient.find(:first,:include=>[{:anamnesis_family_pathologies=>:pathology },{:anamnesis_allergologies=>:allergy},:born_location,:residence_location ],:conditions=>["patients.id=?",params[:id]]) : Patient.new @form="new" case params[:section] when "info" if @patient.id @partial_view="view_info" else @page="new_patient" @partial_view="fields_info" end when "allergology" @partial_view=(@patient.anamnesis_allergologies.length>0 or @user_logged.class.to_s!="Requester") ? "view_allergologies" : "fields_allergology" when "familiar" @partial_view=(@patient.anamnesis_familiar or @user_logged.class.to_s!="Requester") ? "view_familiar" : "fields_familiar" when "family_pathologies" @partial_view=(@patient.anamnesis_family_pathologies.length>0 or @user_logged.class.to_s!="Requester") ? "view_family_pathologies" : "fields_family_pathology" when "pathologicals" @partial_view=(@patient.anamnesis_pathologicals.length>0 or @user_logged.class.to_s!="Requester") ? "view_pathologicals" : "fields_pathological" when "physiological" @partial_view=(@patient.anamnesis_physiological or @user_logged.class.to_s!="Requester") ? "view_physiological" : "fields_physiological" when "visits" @partial_view=(@patient.events.length>0 or @user_logged.class.to_s!="Requester") ? "view_visits" : "fields_visit" 108 end @[email protected] ? #{@patient.surname}" : "New Patient" @page_body=@partial_view end "Patient: #{@patient.name} def get_informations init @page_body="view" if not params[:event_id] @patient=if params[:id] p=Patient.find(params[:id]) if p.events.size>0 c=Patient.find(:first,:include=>[:events,{:anamnesis_family_pathologies=> :pathology},{:anamnesis_allergologies=>:allergy},:born_location,:residenc e_location],:conditions=>["events.state<>'close' and patients.id=?",params[:id]]) end c ? c : p else Patient.new end else @patient=Patient.find(:first, :include=>{:events=>:visits}, :conditions=>["visits.exam_id in (select exam_id from exams_users where exams_users.user_id=#{@user_logged.id}) and events.id=?",params[:event_id]]) end @form="new" case params[:section] when "info" if @patient.id @partial_view="view_info" else @page="new_patient" @partial_view="fields_info" end when "allergology" @partial_view=(@patient.anamnesis_allergologies.length>0 or @user_logged.class.to_s!="Requester") ? "view_allergologies" : "fields_allergology" when "familiar" @partial_view=(@patient.anamnesis_familiar or @user_logged.class.to_s!="Requester") ? "view_familiar" : "fields_familiar" when "family_pathologies" @partial_view=(@patient.anamnesis_family_pathologies.length>0 or @user_logged.class.to_s!="Requester") ? "view_family_pathologies" : "fields_family_pathology" when "pathologicals" @partial_view=(@patient.anamnesis_pathologicals.length>0 or @user_logged.class.to_s!="Requester") ? "view_pathologicals" : "fields_pathological" when "physiological" 109 @partial_view=(@patient.anamnesis_physiological or @user_logged.class.to_s!="Requester") ? "view_physiological" : "fields_physiological" when "visits" @partial_view=(@patient.events.length>0 or @user_logged.class.to_s!="Requester") ? "view_visits" : "fields_visit" end render :partial =>@partial_view end def new_visit init @page_body="fields_visit" @page_title="New Visit" @form="new" @visit=Visit.new @ecg=Ecg.new @laboratory_exam=LaboratoryExam.new if request.post? @visit=Visit.new(params[:visit]) @visit.visit_date=Time.now @visit.patient_id=params[:id] @visit.user_id=session[:user_id] @visit.laboratory_exam=LaboratoryExam.new(params[:laboratory_exam]) if params[:data_laboratory_exam] @visit.ecg=Ecg.new(params[:ecg]) if params[:data_ecg] if params[:event_id] @visit.event_id=params[:event_id] else @event=Event.new(:user_id=>session[:user_id],:patient_id=>params[:id]) @event.state="open" @event.created_at=Time.now @visit.event=@event end j=0 for i in 0..(params[:visit][:exam_file].size-1) if params[:visit][:exam_file][i.to_s][:uploaded_data]!="" #ExamFile.create(params[:visit][:exam_file][i.to_s]) @visit.exam_files[j]=ExamFile.new(params[:visit][:exam_file][i.to_s]) j+=1 end end if @visit.event and @visit.save redirect_to(:controller=>:patient,:action=>:view,:id=>params[:id],:sectio n=>"visits") else render(:controller=>:patient,:action=>:new) end end end def new_allergology init @page_body="fields_allergology" @page_title="New allergology" 110 @form="new" @allergology=AnamnesisAllergology.new if request.post? @allergology=AnamnesisAllergology.new(params[:anamnesis_allergology]) @allergology.patient_id=params[:id] @[email protected] if @allergology.save render :partial =>'view_allergologies' else render :partial =>'fields_allergology' end end end def new_familiar init @page_body="fields_familiar" @page_title="New Familar" @form="new" @anamnesis_familiar=AnamnesisFamiliar.new if request.post? @anamnesis_familiar=AnamnesisFamiliar.new(params[:anamnesis_familiar]) @anamnesis_familiar.patient_id=params[:id] @patient=@anamnesis_familiar.patient if @anamnesis_familiar.save render :partial=>"/patient/view_familiar" #redirect_to(:controller=>:patient,:action=>:view,:id=>params[:id],:secti on=>"familiar") else render :partial=>"/patient/fields_familiar" #render(:controller=>:patient,:action=>:new) end end end def new_pathological init @page_body="fields_pathological" @page_title="New anamnesis pathological" @form="new" @anamnesis_pathological=AnamnesisPathological.new if request.post? @anamnesis_pathological=AnamnesisPathological.new(params[:anamnesis_patho logical]) @anamnesis_pathological.patient_id=params[:id] @anamnesis_pathological.location_id=set_city(params[:pathological]) @patient=@anamnesis_pathological.patient if @anamnesis_pathological.save render :partial=>"/patient/view_pathologicals" #redirect_to(:controller=>:patient,:action=>:view,:id=>params[:id],:secti on=>"pathologicals") else render :partial=>"/patient/fields_pathological" 111 #render(:controller=>:patient,:action=>:new) end end end def new_family_pathology init @page_body="fields_family_pathology" @page_title="New anamnesis family pathology" @form="new" @anamnesis_family_pathology=AnamnesisFamilyPathology.new if request.post? @anamnesis_family_pathology=AnamnesisFamilyPathology.new(params[:anamnesi s_family_pathology]) @anamnesis_family_pathology.patient_id=params[:id] if @anamnesis_family_pathology.save @patient=@anamnesis_family_pathology.patient render :partial =>'/patient/view_family_pathologies' else # render :partial=>"/patient/fields_family_pathology" render :partial =>'/patient/fields_family_pathology' #render(:controller=>:patient,:action=>:new) end end end def new_physiological init @page_body="fields_physiological" @page_title="New anamnesis physiological" @form="new" @anamnesis_physiological=AnamnesisPhysiological.new if request.post? @anamnesis_physiological=AnamnesisPhysiological.new(params[:anamnesis_phy siological]) @anamnesis_physiological.patient_id=params[:id] if @anamnesis_physiological.save @patient=@anamnesis_physiological.patient render :partial=>"/patient/view_physiological" #redirect_to(:controller=>:patient,:action=>:view,:id=>params[:id],:secti on=>"physiological") else render :partial=>"/patient/fields_physiological" #render(:controller=>:patient,:action=>:new) end end end def edit init @patient=Patient.find(:first,:conditions=>"patients.id=#{params[:id]}") if params[:id] 112 @anamnesis_allergology=AnamnesisAllergology.find(:first,:conditions=>"pat ient_id=#{params[:id]}") if params[:section]=="allergology" @anamnesis_familiar=AnamnesisFamiliar.find(:first,:conditions=>"patient_i d=#{params[:id]}") if params[:section]=="familiar" @anamnesis_family_pathology=AnamnesisFamilyPathology.find(:first,:conditi ons=>"patient_id=#{params[:id]}") if params[:section]=="family_pathology" @anamnesis_pathological=AnamnesisPathological.find(:first,:conditions=>"p atient_id=#{params[:id]}") if params[:section]=="pathological" @anamnesis_physiological=AnamnesisPhysiological.find(:first,:conditions=> "patient_id=#{params[:id]}") if params[:section]=="physiological" @page_body="edit" @form="edit" end def edit_info init @patient=Patient.find(:first,:conditions=>"patients.id=#{params[:id]}") if params[:id] @page_body="edit" @form="edit" if request.post? @patient.born_location_id=set_city(params[:born]) @patient.residence_location_id=set_city(params[:residence]) if params[:residence] if @patient.update_attributes(params[:patient]) render :partial=>"/patient/view_info" else render :partial=>"/patient/fields_info" end end end def edit_allergology init @page_body="edit" @form="edit" if request.post? @anamnesis_allergology=AnamnesisAllergology.find(:first,:conditions=>["id =?",params[:id]]) if @anamnesis_allergology.update_attributes(params[:anamnesis_allergology]) @patient=@anamnesis_allergology.patient render :partial=>"/patient/view_allergologies" #redirect_to(:controller=>:patient,:action=>:view,:id=>@anamnesis_allergo logy.patient_id,:section=>"allergology") else render :partial=>"/patient/fields_allergology" end end end 113 def edit_familiar init @page_body="edit" @form="edit" if request.post? @anamnesis_familiar=AnamnesisFamiliar.find(:first,:conditions=>["id=?",pa rams[:id]]) @patient=@anamnesis_familiar.patient if @anamnesis_familiar.update_attributes(params[:anamnesis_familiar]) render :partial=>"/patient/view_familiar" #redirect_to(:controller=>:patient,:action=>:view,:id=>@anamnesis_familia r.patient_id,:section=>"familiar") else render :partial=>"/patient/edit_familiar" end end end def edit_family_pathology init @page_body="edit" @form="edit" if request.post? @anamnesis_family_pathology=AnamnesisFamilyPathology.find(:first,:conditi ons=>["id=?",params[:id]]) if @anamnesis_family_pathology.update_attributes(params[:anamnesis_family_pa thology]) @patient=@anamnesis_family_pathology.patient render :partial=>"/patient/view_family_pathologies" else render :partial=>"/patient/fields_family_pathology" end end end def edit_pathological init @page_body="edit" @form="edit" if request.post? @anamnesis_pathological=AnamnesisPathological.find(:first,:conditions=>"i d=#{params[:id]}") #@anamnesis_pathological.city_id=params[:city_id].to_i if params[:city_id] @anamnesis_pathological.location_id=set_city(params[:pathological]) if params[:pathological] if @anamnesis_pathological.update_attributes(params[:anamnesis_pathological] ) render :partial=>"/patient/view_pathological",:object=>@anamnesis_pathological 114 #redirect_to(:controller=>:patient,:action=>:view,:id=>@anamnesis_patholo gical.patient_id,:section=>"pathologicals") else render :partial=>"/patient/fields_pathological" end end end def edit_physiological init @page_body="edit" @form="edit" if request.post? @anamnesis_physiological=AnamnesisPhysiological.find(:first,:conditions=> "id=#{params[:id]}") if @anamnesis_physiological.update_attributes(params[:anamnesis_physiologica l]) @patient=@anamnesis_physiological.patient render :partial=>"/patient/view_physiological",:object=>@anamnesis_physiological #redirect_to(:controller=>:patient,:action=>:view,:id=>@anamnesis_physiol ogical.patient_id,:section=>"physiological") else render :partial=>"/patient/fields_physiological" end end end def close_event init event=Event.find(params[:id]) event.state="close" event.closed_at=Date.today event.save redirect_to(:controller=>:patient,:action=>:view,:id=>event.patient_id,:s ection=>"visits") end def request_second_opinion init event=Event.find(params[:id]) event.state="request another" event.save redirect_to(:controller=>:patient,:action=>:view,:id=>event.patient_id,:s ection=>"visits") end def change_event_state init event=Event.find(params[:event_id]) event.state=params[:state] event.closed_at=Time.now if params[:state]=="close" event.save 115 @page_body="view" @patient=Patient.find(:first,:include=>[{:anamnesis_family_pathologies=>: pathology},{:anamnesis_allergologies=>:allergy},:born_location,:residence _location],:conditions=>["patients.id=?",event.patient_id]) render :partial=>"/patient/view_visits" end def get_form_patient init @page_body="fields_info" @page_title="New Patient" @form="new" @patient=Patient.new render :partial=> "/patient/fields_info" end def get_anamnesi_allergology render :partial => "/patient/view_allergology", :object=>AnamnesisAllergology.find(:first,:conditions=>["id=?",params[:id ]]) end def get_anamnesi_family_pathology render :partial => "/patient/view_family_pathology", :object=>AnamnesisFamilyPathology.find(:first,:conditions=>["id=?",params [:id]]) end def get_anamnesi_pathological render :partial => "/patient/view_pathological", :object=>AnamnesisPathological.find(:first,:conditions=>["id=?",params[:i d]]) end end class UserController < ApplicationController def new init @no_submenu=1 if(@user_logged and @user_logged.class.to_s=='Administrator') @page_body="form" @page_title="New User" @page="new_user" @form="new" @user=User.new if request.post? @user=User.new(params[:user]) @user.location_id=set_city(params[:location]) params[:location] @user.creator_id=session[:user_id] #@user.city_id=params[:city_id] @user.type=params[:user][:type] if params[:exams] @exams=Exam.find(params[:exams]) if 116 @user.exams=@exams end if @user.save flash[:notice] = "Registration Complete!" #Inserire procedura per inviare l'email con i dati utente redirect_to(:controller=>:user,:action=>:view,:id=>@user.id) else render(:controller=>:user,:action=>:new) end end elsif(not @user_logged) redirect_to(:action=>:login) else render :text=>@user_logged.type.to_s end end def edit init @page_body="form" @page_title="Edit User" @page="edit_user" @form="edit" @user=User.new if params[:id] and request.post? @user=User.find(:first,:conditions=>["id=?",params[:id]]) @user.password=params[:user][:password] @user.first_login=0 if @user.save page="view" end redirect_to(:controller=>:user,:action=>(page ? page "edit"),:id=>params[:id]) end : end def permission_denied init end def mail_sent @page_title="Registration complete" @page_body="mail_sent" end def login @page_body="login" @page_title="Login" if @user_logged redirect_to(:controller => "index") else if request.post? session[:user_id] = nil @user=User.new(params[:user]) [email protected]_login if user session[:user_id] = user.id 117 if params[:user]['enable_cookie']=="1" cookies[:user_id] = { :value => user.id.to_s, :expires => 30.days.from_now } cookies[:user_password] = { :value => user.hashed_password, :expires => 30.days.from_now } end if user.first_login==1 redirect_to(:controller => :user,:action=>:view,:id=>user.id) else redirect_to(:controller => "index") end else flash[:notice] = "Invalid user/password combination" end end end end def logout session[:user_id] = nil cookies.delete :user_id cookies.delete :user_password flash[:notice] = "Logged out" redirect_to(:controller => "user",:action=>"login") end def view init @page_body="view" @page_title="" @user=User.find(:first,:conditions=>["id=?",params[:id]]) if @user.first_login==1 and @[email protected] @page_body="form" @form="edit" end end def get_profile init @page_body="view" @page_title="" @user=User.find(:first,:conditions=>["id=?",params[:id]]) if @user.first_login==1 @page_body="form" @form="edit" end render :partial=>"view" end def get_form_user init @no_submenu=1 @page_body="form" @page_title="New User" @page="new_user" @form="new" @user=User.new render :partial=> "/user/form" 118 end def get_form_login @no_submenu=1 @page_body="login" @page_title="Login" render :partial=> "/user/login" end def get_form_password @page_body="form" @page_title="Edit User" @page="edit_user" @form="edit" @user=User.find(:first,:conditions=>["id=?",params[:id]]) render :partial=> "/user/form" end def get_miro_info render :partial=>"/user/miro_info" end def delete init if @user_logged and @user_logged.type.to_s=="Administrator" if request.post? and params[:users] for i in params[:users].values User.delete(i) end end end redirect_to(:controller=>:index,:action=>'index') end end 119 Appendice B: MODELS class Administrator < User end class AnamnesisFamilyPathology < ActiveRecord::Base belongs_to :patient belongs_to :pathology attr_accessible :patient_id,:pathology_id,:mother,:father,:grandfather_paternal,:grandfat her_maternal,:grandmother_paternal,:grandmother_maternal,:uncles_paternal ,:uncles_maternal,:aunts_paternal,:aunts_maternal,:brothers,:sisters,:con sort,:childs,:daughters validates_presence_of :patient_id,:pathology_id end class AnamnesisFamiliar < ActiveRecord::Base belongs_to :patient attr_accessible :patient_id,:father_alive,:father_blood_group,:mother_alive,:mother_blood _group,:allergy_id,:notes validates_presence_of :patient_id end class AnamnesisAllergology < ActiveRecord::Base belongs_to :patient belongs_to :allergy attr_accessible :patient_id,:notes,:beginning_date,:ending_date,:allergy_id validates_presence_of :patient_id,:allergy_id end class Allergy < ActiveRecord::Base has_many :anamnesis_allergologies end class Patient < ActiveRecord::Base has_many :anamnesis_pathologicals has_many :anamnesis_allergologies has_one :anamnesis_familiar has_many :anamnesis_family_pathologies has_one :anamnesis_physiological has_many :visits has_many :events belongs_to :structure 120 belongs_to :born_location ,:class_name => 'Location',:foreign_key => 'born_location_id' belongs_to :residence_location,:class_name => 'Location',:foreign_key => 'residence_location_id' attr_accessible :name,:surname,:born_date,:sex,:born_location_id,:residence_address,:resi dence_number,:residence_location_id,:residence_cap,:marital_status,:profe ssion,:instruction,:dead,:dead_date,:telephone_number,:mail,:structure_id validates_presence_of :name,:surname,:born_date,:born_location end class Visit < ActiveRecord::Base belongs_to :patient has_many :exam_files belongs_to :exam has_one :laboratory_exam has_one :ecg belongs_to :event attr_accessible :patient_id,:visit_date,:exam_id,:symptoms,:target_exam,:height,:weight,: least_arterial_pressure,:maximal_arterial_pressure,:cardiac_frequency, :conclusions, :target_exam validates_presence_of :patient_id,:visit_date,:exam_id end require "digest/sha1" class User < ActiveRecord::Base belongs_to :lab has_many :events has_many :forum ,:dependent =>:destroy has_many :reports ,:dependent =>:destroy has_and_belongs_to_many :exams,:foreign_key :association_foreign_key => 'exam_id' has_one :user_id belongs_to :location => 'user_id', validates_uniqueness_of :username validates_presence_of :name, :surname,:password,:username,:mail,:type validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[az]{2,})$/i,:if=>:mail? validates_length_of :username, :minimum=>4, :too_short=>"please enter at least %d character",:if=>:username? validates_length_of :password, :minimum=>6, :too_short=>"please enter at least %d character",:if=>:password? attr_accessible :username,:password,:mail,:name,:surname,:specialization,:address,:cap,:t elephone_number,:fax,:type,:location_id,:created_at,:creator_id,:first_lo gin attr_accessor :password attr_accessor :enable_cookie def before_create self.hashed_password = User.hash_password(self.password) end 121 def after_create @password = nil end def before_update self.hashed_password = User.hash_password(self.password) self.password and self.password!="" end def after_update @password = nil end if def validate_login User.login(self.username, self.password) end def self.login(username,password) hashed_password = hash_password(password || "") find(:first,:conditions => ["username = ? and hashed_password = ?",username, hashed_password]) end def self.validate_login_with_cookie(id, hashed_password) find(:first,:conditions => ["id = ? and hashed_password = ?",id, hashed_password]) end def self.range_from_now time today=Date.today date=Date.parse(time.strftime('%Y/%m/%d')) if today-date==0 "Today, "+time.strftime('%H:%M') elsif today-date==1 time.strftime('Yesterday, %H:%M') elsif today.month==date.month and today.year==date.year time.strftime("%a, %d %H:%M") else time.strftime("%a, %d %b %y %H:%M") end end private def self.hash_password(password) Digest::SHA1.hexdigest(password) end end class Structure < ActiveRecord::Base has_many :requesters has_many :users 122 validates_presence_of :name,:location_id attr_accessible :name,:location_id,:address,:telephone_number end class Requester < User belongs_to :structure attr_accessible :structure_id end class Report < ActiveRecord::Base belongs_to :event belongs_to :user attr_accessible :created_at,:event_id,:user_id,:body validates_presence_of :event_id,:user_id,:body end class Pathology < ActiveRecord::Base has_many :anamnesis_family_pathology has_many :anamnesis_pathological end class Location < ActiveRecord::Base has_many :users has_many :patients has_many :anamnesis_pathologicals end class LaboratoryExam < ActiveRecord::Base belongs_to :visit attr_accessible :visit_id,:wbc,:rbc,:hb,:ht,:mcv,:plt,:creatinina,:azotemia,:ast,:alt,:so dium,:potassium,:calcium,:pt,:pt_inr,:fibrinogeno,:ves,:pcr,:d_dimero,:ck ,:ck_mb,:ph,:pco2,:po2,:so2,:hco3,:sbe,:be end class Forum < ActiveRecord::Base belongs_to :event belongs_to :user attr_accessible :user_id,:created_at,:event_id,:message validates_presence_of :user_id,:event_id,:message end class Exam < ActiveRecord::Base has_many :events 123 has_many :visits has_and_belongs_to_many :users,:foreign_key :association_foreign_key => 'user_id' validates_presence_of :name end class ExamFile < ActiveRecord::Base belongs_to :visit has_attachment :storage => 'exam_files',:max_size => 100.megabytes validates_as_attachment => 'exam_id', :file_system,:path_prefix => def full_filename(thumbnail = nil) file_system_path = self.attachment_options[:path_prefix] File.join(RAILS_ROOT, file_system_path, thumbnail_name_for(nil, self.id)) end def get_filename(thumbnail = nil) file_system_path = "/home/mironrails/tesi/exam_files" @thumb = self.thumbnail unless (@thumb = thumbnail) #checks if thumbnails info already exist File.join(file_system_path,thumbnail_name_for(@thumb, self.id.to_s)) end def thumbnail_name_for(thumbnail = nil, asset = nil) extension = filename.scan(/\.\w+$/) # extracts extension return "#{asset}#{extension}" # change the filename to fit your needs end end class Event < ActiveRecord::Base belongs_to :user belongs_to :exam belongs_to :patient has_many :visits has_many :reports has_many :forums attr_accessible :patient_id,:created_at,:closed_at,:user_id,:state validates_presence_of :user_id,:state,:patient_id end class Ecg < ActiveRecord::Base belongs_to :visit attr_accessible :visit_id,:rhythm,:frequency,:morphology_p,:interval_pr,:morphology_qrs,: duration_qrs,:morphology_st,:axis_qrs,:morphology_t,:interval_qtc,:conclu sions 124 end class Doctor < User validates_presence_of :username,:password,:mail end class AnamnesisPhysiological < ActiveRecord::Base belongs_to :patient attr_accessible :patient_id,:alvo_notes,:fecal_incontinence_notes,:dyuresis_notes,:urinar y_inconsistency_notes,:digestion_notes,:sleep_notes,:blood_group,:blood_g roup_partner,:blood_group_notes validates_presence_of :patient_id end class AnamnesisPathological < ActiveRecord::Base belongs_to :patient belongs_to :location belongs_to :pathology attr_accessible :patient_id,:pathology_id,:onset_age_yy,:state_id,:flag_hospitalization,: entry_date, :resignations_date,:diagnosis,:hospital,:location_id,:resignations_letter validates_presence_of :patient_id,:pathology_id end 125 BIBLIOGRAFIA Mokabyte : http://www.mokabyte.it , rivista dedicata a Java Wikipedia : http://www.wikipedia.org , enciclopedia libera scritta dagli utenti del web Ruby : http://www.ruby-lang.org , sito ufficiale di Ruby Ruby on Rails : http://www.rubyonrails.org/ , sito ufficiale di Ruby on Rails Prototype : http://www.prototypejs.org/ , sito ufficiale del framework Prototype Ubuntu : http://help.ubuntu.com , guida per l’installazione di Ubuntu server e di Ruby on Rails Apache : http://www.apache.org/ , Application Server Apache MySql : http://www-it.mysql.com/ , sito ufficiale del DBMS MySql SSH : http://www.oscene.net/site/sysadmin/networking/howto-configurare-neldettaglio-ssh Mantis : http://www.mantisbt.org/ , sito ufficiale del Bug Tracker Mantis Christophe Portenuve : “Prototype and script.aculo.us” , consultazione informazioni sul framework Prototype Dave Thomas : “Programming Ruby” , consultazione informazioni su Ruby Jules J. Berman : “Ruby Programming for Medicine and Biology “, consultazione informazioni avanzate su Ruby e la telemedicina Dave Thomas, David Heinemeier Hansson : “Sviluppare applicazioni Web con Rails”, informazioni avanzate su Ruby on Rails Bruce Tate : “From Java to Ruby”, consultazione differenze tra Java e Ruby Scott Raimond : “Ajax on Rails” , informazioni sull’utilizzo di Ajax con Rails Danny Goodman : “Javascript Bible” , consultazione informazioni su Javascript Andre Lewis, Michael Purvis,Jeffrey Sambells, e Cameron Turner : “Google Maps Applications with Rails and Ajax”, informazione sull’utilizzo dell’API Google Maps in Rails Brian Pfaffenberger, Steven M. Schafer,Charles White, Bill Karow : “HTML,XHTML e CSS Bible” 126