Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
{note}

No longer maintained.  Reimplemented as the ??[docs:Flowsheet Module]

{note}


h2. Tutors

Primary mentor: [Sy Haas|http://archive.openmrs.org/wiki/User:Syhaas~sy], Backup mentor: [Paul Biondich|http://archive.openmrs.org/wiki/User:Paul]


\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=2]\]

h2. Abstract

One of the simplest and most powerful ~paul]


h2. Abstract

One of the simplest and most powerful ways that a clinician  visualizes data is by reviewing all results in reverse chronological  order. While the data visualization "widget" shows graphical depictions  of specific variables (e.g., trend lines), this "Flowsheet" project is  more concerned with succinctly visualizing all available data for a  single patient and, therefore, is more likely to take the form of web  pages that render data in tables or lists. For example, a physician may  be interested in finding the most recent results for a patient's kidney  function. This could involve displaying many laboratory results,  including serum sodium, potassium, bicarbonate, blood urea nitrogen, and  creatinine levels; blood counts; and ultrasound results. For example,  these could be displayed in lists and/or tables, starting with the most  recent values.

[http://archive.openmrs.org/wiki/Image:Flowsheet.jpg]
_Example of how one might render flowsheet data_

Some ideas of how such a feature would be implemented:
* Start with a simple reverse chronological dump of patient data  (e.g., select a patient and show all existing observations in reverse  chronologic order)
* Allow clicking on a result to bring up more information about  that result for the current patient (e.g., clicking on a patient's  weight would bring up a dialog showing more information about that  specific result along with additional information about weight and,  possibly, a graph of weights for the patient)
* Create a simple search box to quickly navigate to data
* Add a date range selector to filter down to data within a given date rage

This project would require:
* Strong HTML, CSS, and JSP, and Java skills
* Learning how to make an OpenMRS module and work within the OpenMRS web application framework
* Comfort with Java and experience with (or willingness to learn) Hibernate and Spring
* Likely working with Velocity templates and/or AJAX/JavaScript libraries

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=3]\]

h2. Target

Successful completion of this project would at the minimum include:
* A new OpenMRS flowsheet module for longitudinal data review
* The ability for a user to select a patient and view their data in reverse chronological order
* Clicking on results brings up a dialog with more information  relevant to the result (additional details of the specific result,  relevant links, graph of all values for the patient, etc.)
* The ability to search for results by name and/or filter results by date range

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=4]\]

h2. Extra credit

* Begin to handle complex results such as sets and complex observations
* Handle abnormal flags
* Display some data in tabular form
* Render in pages vs. long form

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=5]\]

h2. Mockup

[http://archive.openmrs.org/wiki/Image:GSoC_2009_Ideas_-_Flowsheet_mockup1.png]\\

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=6]\]

h2


h2. Planning

Here's some thoughts on how we are planning to implement the project and the progress.
* Installing this module will create a new tab in the patient dashboard {color:#ff0000}*\--\- Done{*}{color}
* As the initial step, all the data related to a patient will be listed in reverse chronological order
** GWT Integration to the module {color:#ff0000}*\--\- Done{*}{color}
** Representing UI objects in the GWT client side {color:#ff0000}*\--\- Done{*}{color}
** Choosing GXT/GWT widgets for displaying the data {color:#ff0000}*\--\- Done{*}{color}
** etc
* A date range selector will be added to filter data to particular period {color:#ff0000}*\--\- Done{*}{color}

(The above tasks are initially planned for mid-term goals and completed far before mid-term)
* Filter by concept type {color:#ff0000}*\--\- Done{*}{color}
* When clicked on an entry of record, it will pop up a window showing all the details of that particular observation {color:#ff0000}*\--\- Done{*}{color}
* The appropriate graphical view of an observation will be dynamically generated (eg: Graph etc) {color:#ff0000}*\--\- Done{*}{color}
* Addition of a flowsheet in the pop-up window {color:#ff0000}*\--\- Done{*}{color}
* Addition of abnormal flags etc {color:#ff0000}*\--\- Done{*}{color}
* Create a search box to search data by different filters {color:#ff0000}*\--\- Done{*}{color}

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=7]\]

h3. Enhancements

* *UI enhancements*
** Creating a double-slider widget for date range selection {color:#ff0000}*\--\- Done{*}{color}
** Layout data to reduce unnecessary space and display obs details on wider space {color:#ff0000}*\--\- Done{*}{color}
** Improving the Line Chart for numeric obs, by reducing extra\- range space {color:#ff0000}*\--\- Done{*}{color}
** Highlighting the clicked obs entry in the flowsheet in the pop-up {color:#ff0000}*\--\- Done{*}{color}
** Include critical and normal values for numeric obs in the flowsheet {color:#ff0000}*\--\- Done{*}{color}
** Include a online-resource section to the pop-up {color:#ff0000}*\--\- Done{*}{color}
** According to the data in the pop-up reduce the space for  line-chart by showing a thumbnail view ( expanding it should show the  chart in bigger window)
* Improving the data transfer speed {color:#ff0000}*\--\- Done for now{*}{color}
\\

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=8]\]


h3. Documentation

* Module Documentation {color:#ff0000}*\--\- Done{*}{color}
* Source code documentation {color:#ff0000}*\--\- Done{*}{color}

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=9]\]

h3. Future Enhancements - Long after the summer

* Optimization for large data set
* Interactive graph with the ability to zoom in and out
* Addition to more features to the pop-up window
* Addition of more links for resources related to the specific concept of the obs, to the pop-up window

In summary there will be a NEW way to view longitudinal data.

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=10]\]




h2. GWT-RPC Integration to the Module

[http://archive.openmrs.org/wiki/Image:GWTDiagram.png]


\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=11]\]

h3. The Class Structures and Configurations

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=12]\]

h4
h4. Service Interfacepackage org.openmrs.module.flowsheet.gwt.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("../../../moduleServlet/flowsheet/flowsheetService")
public interface FlowsheetService extends RemoteService{	//method definitions        //String\[\] getObsData(String patientId);        // ....
}
The synchronous Service interface lies in the client side of the GWT code. In this module, it resides under the package _src/org.openmrs.module.flowsheet.gwt.client_. Any Service interface should extends the *com.google.gwt.user.client.rpc.RemoteService* interface. All the RPC method calls should be defined in the service interface. The *@RemoteServiceRelativePath* annotation specifies the relative path of the Service. In this case, the compiled GWT code resides under <_/moduleResources/flowsheet/<generated-folder>_. So if the relative path is given as _/moduleServlet/flowsheet/flowsheetService_, it will end up with [http://localhost:8080/openmrs/moduleResources/flowsheet/generated-folder/moduleServlet/flowsheet/flowsheetService|http://localhost:8080/openmrs/moduleResources/flowsheet/generated-folder/moduleServlet/flowsheet/flowsheetService].  To avoid that, the location _../../../moduleServlet/flowsheet/flowsheetService_ is used. This will end up in the correct location of the service&nbsp;: [http://localhost:8080/openmrs/moduleServlet/flowsheet/flowsheetService|http://localhost:8080/openmrs/moduleServlet/flowsheet/flowsheetService]


\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=13]\]

h4. AsyncService Interfacepackage org.openmrs.module.flowsheet.gwt.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface FlowsheetServiceAsync {
//method definitions
void getObsData(String patientId, AsyncCallback<String\[\]> callback);
// ....
}
To make a remote call from the call, a asynchronous service interface  should be defined in the client side. The interface should use the  suffix _Async_ with the name of the Service interface. The asynchronous method requires the caller to pass a callback object to  get notified when the asynchronous call completes. Therefore the  asynchronous methods do not have return types; void is returned by  default. The method should have an additional parameter of the type _AsyncCallback<return-type-of-the-service-method>_ .

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=14]\]

h4
h4. Service Implementation Classpackage org.openmrs.module.flowsheet.gwt.server;

import org.openmrs.module.flowsheet.gwt.client.FlowsheetService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class FlowsheetServiceImpl extends RemoteServiceServlet implements FlowsheetService {
//method implementations
public String\[\] getObsData(String patientId) {           /* Implementation goes here */        }
// ....
}
Server side processing occurs in the service implementation class. This calls should has a suffix _Impl_ to the service interface name. In addition it should extend *com.google.gwt.user.server.rpc.RemoteServiceServlet* class and implement the service interface. This implementation class  should implement all the methods defined in the service interface.  This class resides in the server-side of the GWT code. In the case of  this module, it resides under _web/src_ folder inside the package _org.openmrs.module.flowsheet.gwt.server_. This is the actual servlet which is accessed via the url [http://localhost:8080/openmrs/moduleServlet/flowsheet/flowsheetService|http://localhost:8080/openmrs/moduleServlet/flowsheet/flowsheetService] when the remote call is invoked in from the client. This servlet should be defined in the *config.xml* of the OpenMRS module as follows.        <servlet>
<servlet-name>flowsheetService</servlet-name>
<servlet-class>
org.openmrs.module.flowsheet.gwt.server.FlowsheetServiceImpl
</servlet-class>
</servlet>
\\

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=15]\]

h4. GWT Module Configuration File

In addition to these three main classes, the GWT module configuration file resides inside the folder _src/org.openmrs.module.flowsheet.gwt_.It is named as _<Module-name>.gwt.xml_ and will have the following format.<?xml version="1.0" encoding="UTF-8"?>
<\!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.2//EN"
"http://google-web-toolkit.googlecode.com/svn/tags/1.6.2/distro-source/core/src/gwt-module.dtd">
<module rename-to='mywebapp'>
<\!-\- Inherit the core Web Toolkit stuff.                        \-->
<inherits name='com.google.gwt.user.User'/>

<\!-\- Inherit the default GWT style sheet.         \-->
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<\!-\- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> \-->
<\!-\- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     \-->

<\!-\- Other module inherits                                      \-->

<\!-\- Specify the app entry point class.                         \-->
<entry-point class='org.openmrs.module.flowsheet.gwt.client.Flowsheet'/>
</module>


\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=16]\]

h4. GWT App Entry Point Class

When the GWT module is invoked, the processing starts at a enrty  point class. This class is defined in the GWT module xml file inside the _<entry-point>_ tag.The entry point class resides under _src/org.openmrs.module.flowsheet.gwt.client_. This entry point class should implement *com.google.gwt.core.client.EntryPoint* interface and should implement the *onModuleLoad()* method which is called when a GWT module loads. All the GWT UI code should resides inside this class. The code below shows the skeleton of the Entry Point class.package org.openmrs.module.flowsheet.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.\*
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.*;

public class Flowsheet implements EntryPoint {
private VerticalPanel mainPanel = new VerticalPa>nel();
private Label resultLabel = new Label();
private FlexTable table = new FlexTable();
/\* more code here \*/
public void onModuleLoad() {

mainPanel.add(resultLabel);
table.setBorderWidth(2);
mainPanel.add(table);
RootPanel.get("webapp").add(mainPanel);
/\* more code here \*/

String patientId = com.google.gwt.user.client.Window.Location.
getParameter("patientId");
/\* The asynchronous call \*/
FlowsheetServiceAsync serviceAsync = GWT.create(FlowsheetService.class);
AsyncCallback<String\[\]> callback = new AsyncCallback<String\[\]>() {
public void onFailure(Throwable caught) {				resultLabel.setText(caught.getMessage());			}

public void onSuccess(String\[\] result) {				populateData(result);			}
};
serviceAsync.getObsData(patientId, callback);
}

private void populateData(String\[\] data) {	  	/* more code here */	}
}

\\

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=17]\]

h4. Addition to the build.xml to compile the GWT module

The following target additions should be made to the build.xml of the  OpenMRS module to make it possible to compile the GWT module.       <target name="gwtc" depends="javac" description="GWT compile to JavaScript">
<java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler">
<classpath>
<pathelement location="src"/>
<path refid="project.class.path"/>
</classpath>
<\!-\- add jvmarg \-Xss16M or similar if you see a StackOverflowError \-->
<jvmarg value="-Xmx256M"/>
<\!-\- Additional arguments like \-style PRETTY or \-logLevel DEBUG \-->
<arg line="${gwt.args}"/>
<arg value="org.openmrs.module.flowsheet.gwt.Flowsheet"/>
</java>
</target>

<target name="javac" depends="libs" description="Compile java source">
<mkdir dir="web/module/resources/war/WEB-INF/classes"/>
<javac srcdir="src" includes="**" encoding="utf-8"
destdir="web/module/resources/war/WEB-INF/classes"
source="1.5" target="1.5" nowarn="true"
debug="true" debuglevel="lines,vars,source">
<classpath refid="project.class.path"/>
</javac>
<copy todir="web/module/resources/war/WEB-INF/classes">
<fileset dir="src" excludes="\**/*.java"/>
</copy>
</target>
<target name="libs" description="Copy libs to WEB-INF/lib">
<mkdir dir="web/module/resources/war/WEB-INF/lib" />
<copy todir="web/module/resources/war/WEB-INF/lib" file="${gwt.sdk}/gwt-servlet.jar" />
<copy todir="web/module/resources/war/WEB-INF/lib" file="lib-common/openmrs-api-1.7.0.13514-dev.jar" />
<copy todir="web/module/resources/war/WEB-INF/lib" file="lib-common/web-openmrs-api-1.7.0.13476-dev.jar" />

<\!-\- Add any additional server libs that need to be copied \-->
</target>
When compiled, the GWT framework will generate the JavaScript file and  resources under a folder generated. This generated folder is placed  under _web/modules/resources_ and the JavaScript file is accessed via _<script type="text/javascript" language="javascript"  src="/openmrs/moduleResources/flowsheet/mywebapp/mywebapp.nocache.js"> </script>_ in the JSP page of the module.
\\
\\

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=18]\]

h3. Representation of the Transfer Objects in the Client-Side

GWT allows the contents of a data object to be moved out of from one  application and transmitted to another application via serialization.  The data transfer objects should be serialized by either implementing  java.io.Serializable interface or the IsSerializable interface provided  by GWT.

The objects which are to be used in the GWT UI code, should have a  representation in the client side in order to translated in to  JavaScript. Since the main objects to be transfered are Obs, Encounters  and Concept, they are represented in the client side as shown in the  class diagram below.

[http://archive.openmrs.org/wiki/Image:
!Transfer_Objects_Class_Diagram.png]\\
\\

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=19]\]

h2. References

\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=20]\]|border=1!



h2. References


h3. Student Proposal

[Student Proposal|http://archive.openmrs.org/wiki/Longitudinal_Data_Viewer/Student_Proposal]


\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=21]\]projects:Longitudinal Data Viewer Student Proposal]




h3. Blog Entries

[http://umashanthi.blogspot.com/search/label/OpenMRS|http://umashanthi.blogspot.com/search/label/OpenMRS/]


\[[edit|http://archive.openmrs.org/index.php?title=Longitudinal_Data_Viewer&action=edit&section=22]\]

h3. Documentation

[docs:Flowsheet Module Documentation|http://archive.openmrs.org/wiki/Flowsheet_Module]