Skip to content
This repository has been archived by the owner on Feb 27, 2021. It is now read-only.

Commit

Permalink
Added percentiles
Browse files Browse the repository at this point in the history
  • Loading branch information
endafarrell committed Mar 10, 2013
1 parent fb2c830 commit a6a221c
Show file tree
Hide file tree
Showing 19 changed files with 411 additions and 31 deletions.
4 changes: 4 additions & 0 deletions webapp/src/main/java/endafarrell/orla/OrlaJsonWriter.java
Expand Up @@ -15,4 +15,8 @@ public interface OrlaJsonWriter {
void writeGlucoseReadings(OutputStream outputStream, DateTime from, DateTime to) throws IOException;

void writeGlucoseOverlays(OutputStream outputStream, DateTime from, DateTime to) throws IOException;

void writeHourlyBasalAsJson(OutputStream outputStream) throws IOException;

void writeHourlyPercentiles(OutputStream outputStream, DateTime from, DateTime to) throws IOException;
}
@@ -0,0 +1,19 @@
package endafarrell.orla.api.home;

import endafarrell.orla.api.OrlaHttpServlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = {"/api/home/hourlyBasal"})
public class HourlyBasalServlet extends OrlaHttpServlet {

@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType("application/json");
orla.writeHourlyBasalAsJson(res.getOutputStream());
}
}
@@ -0,0 +1,22 @@
package endafarrell.orla.api.home;

import endafarrell.orla.api.OrlaHttpServlet;
import org.apache.commons.lang3.tuple.Pair;
import org.joda.time.DateTime;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(urlPatterns = {"/api/home/hourlyPercentiles"})
public class HourlyPercentilesServlet extends OrlaHttpServlet {

@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType("application/json");
Pair<DateTime, DateTime> fromTo = fromTo(req);
orla.writeHourlyPercentiles(res.getOutputStream(), fromTo.getLeft(), fromTo.getRight());
}
}
Expand Up @@ -2,7 +2,6 @@

import endafarrell.orla.api.OrlaHttpServlet;
import endafarrell.orla.service.processor.ProcessResults;
import org.apache.commons.lang.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
Expand Down
11 changes: 11 additions & 0 deletions webapp/src/main/java/endafarrell/orla/service/Filter.java
Expand Up @@ -83,6 +83,17 @@ public static List<Event> percentiles(Collection<Event> events, int lower, int h
return percentiles;
}

public static Map<Integer, Double> percentiles(List<Double> values, Set<Integer> percentiles) {
double[] vals = Convert.todoubleArray(values);
Map<Integer, Double> ps = new HashMap<Integer, Double>(percentiles.size());
Percentile percentile = new Percentile();
percentile.setData(vals);
for (Integer percentileLevel : percentiles) {
ps.put(percentileLevel, percentile.evaluate(percentileLevel));
}
return ps;
}

public static List<Event> only(Collection<Event> events, Unit unit) {
ArrayList<Event> eventList = new ArrayList<Event>(events.size());
for (Event event : events) {
Expand Down
70 changes: 70 additions & 0 deletions webapp/src/main/java/endafarrell/orla/service/OrlaImpl.java
Expand Up @@ -6,6 +6,7 @@
import endafarrell.orla.OrlaException;
import endafarrell.orla.monitoring.OrlaMonitor;
import endafarrell.orla.service.data.*;
import endafarrell.orla.service.data.jackson.PairSerializer;
import endafarrell.orla.service.data.persistence.Archiver;
import endafarrell.orla.service.data.persistence.Database;
import endafarrell.orla.service.data.persistence.FileSystemArchiver;
Expand All @@ -16,13 +17,23 @@
import endafarrell.orla.service.processor.TwitterProcessor;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.map.MapperConfig;
import org.codehaus.jackson.map.Module;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.module.SimpleModule;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.JsonNodeFactory;
import org.codehaus.jackson.node.ObjectNode;
import org.codehaus.jackson.type.TypeReference;
import org.joda.time.DateTime;
import org.xml.sax.SAXException;
import twitter4j.internal.org.json.JSONException;
import twitter4j.internal.org.json.JSONObject;

import javax.servlet.http.Part;
import javax.xml.parsers.ParserConfigurationException;
Expand Down Expand Up @@ -285,6 +296,65 @@ public void writeGlucoseOverlays(OutputStream outputStream, DateTime from, DateT
outputStream.write(arrayNode.toString().getBytes());
}

public void writeHourlyBasalAsJson(OutputStream outputStream) throws IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println("»OrlaImpl.writeHourlyBasalAsJson(outputStream)");
List<Event> eventList = getEvents();
List<PumpBasalProfileConfig> basalProfileConfigs = Filter.only(eventList, PumpBasalProfileConfig.class);
PumpBasalProfileConfig lastBasalProfile = basalProfileConfigs.get(basalProfileConfigs.size() - 1);
String hourlyString = lastBasalProfile.getText();
ArrayNode hours = JsonNodeFactory.instance.arrayNode();
JsonNode hourlyJson = mapper.readTree(hourlyString);
System.out.println(hourlyJson);
for (int hour = 0; hour < 24; hour++) {
double basalRate = 0;
basalRate = hourlyJson.get(Integer.toString(hour)).asDouble();
hours.add(basalRate);
}
ObjectNode objectNode = JsonNodeFactory.instance.objectNode();
objectNode.put("daily_total", lastBasalProfile.getValue().doubleValue());
objectNode.put("hours", hours);
mapper.writeValue(outputStream, objectNode);
}

public void writeHourlyPercentiles(OutputStream outputStream, DateTime from, DateTime to) throws IOException {
List<Event> eventList = getEventsList(from, to, false);
List<BloodGlucoseEvent> bgList = Filter.only(eventList, BloodGlucoseEvent.class);
Map<Integer, List<Double>> hoursBgList = new HashMap<Integer, List<Double>>(24);
for (int hour=0; hour < 24; hour++){
hoursBgList.put(hour, new ArrayList<Double>(150));
}
for(BloodGlucoseEvent bg: bgList) {
hoursBgList.get(bg.getStartTime().getHourOfDay()).add(bg.value.doubleValue());
}

Set<Integer> percentileLevels = Sets.newHashSet(15,25,50,75,85);

SortedMap<Integer, List<Pair<Integer, Double>>> data = new TreeMap<Integer, List<Pair<Integer, Double>>>();
for (Integer pl: percentileLevels) {
ArrayList<Pair<Integer, Double>> hourlyPercentiles = new ArrayList<Pair<Integer, Double>>(24);
for (int i=0; i<24; i++) {
hourlyPercentiles.add(null);
}
data.put(pl, hourlyPercentiles);
}

for (Integer hour: hoursBgList.keySet()) {
List<Double> hourBgList = hoursBgList.get(hour);
Map<Integer, Double> percentiles = Filter.percentiles(hourBgList, percentileLevels);

for(Integer pl: percentileLevels){
data.get(pl).set(hour, new ImmutablePair(hour, percentiles.get(pl)));
}
}
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("Pair<Integer,Double> as [L,R]", new Version(0,0,1,null));
module.addSerializer(new PairSerializer(Pair.class));
mapper.registerModule(module);
mapper.writeValue(outputStream, data);

}

public String getHealthGraphAuthorisation() throws OrlaException {
System.out.println("»OrlaImpl.getHealthGraphAuthorisation()");
try {
Expand Down
@@ -1,6 +1,5 @@
package endafarrell.orla.service.data;

import endafarrell.orla.service.Convert;
import endafarrell.orla.service.OrlaDateTimeFormat;
import endafarrell.orla.service.OrlaObject;
import org.codehaus.jackson.JsonNode;
Expand Down
@@ -0,0 +1,23 @@
package endafarrell.orla.service.data.jackson;

import org.apache.commons.lang3.tuple.Pair;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.ser.std.SerializerBase;

import java.io.IOException;

public class PairSerializer<Integer,Double> extends SerializerBase<Pair<Integer,Double>> {
public PairSerializer(Class<Pair<Integer, Double>> t) {
super(t);
}

@Override
public void serialize(Pair<Integer, Double> value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
jgen.writeStartArray();
jgen.writeNumber((java.lang.Integer) value.getLeft());
jgen.writeNumber((java.lang.Double) value.getRight());
jgen.writeEndArray();
}
}
63 changes: 62 additions & 1 deletion webapp/src/main/webapp/graph.jsp
Expand Up @@ -5,6 +5,7 @@
<script type="text/javascript" src="<%=application.getContextPath()%>/js/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="<%=application.getContextPath()%>/js/jquery.tmpl.min.js"></script>
<script type="text/javascript" src="<%=application.getContextPath()%>/js/jquery.flot.js"></script>
<script type="text/javascript" src="<%=application.getContextPath()%>/js/jquery.flot.fillbetween.js"></script>
<script type="text/javascript" src="<%=application.getContextPath()%>/js/orla.js"></script>
<link rel="stylesheet" type="text/css" media="all" href="<%=application.getContextPath()%>/css/orla.css">
<script type="text/javascript">
Expand All @@ -18,7 +19,7 @@
<div class="graph">
<h3>Glucose readings</h3>
<p>These are the bG readings in this time-frame.</p>
<div id="ph0" style="width: 90%; height: 300px"></div>
<div id="ph0" style="width: 85%; height: 300px"></div>
</div>
<div class="graph">
<h3>Mean bG</h3>
Expand All @@ -32,6 +33,19 @@
readings here suggest stability.</p>
<div id="pha" style="width: 85%; height: 300px"></div>
</div>

<div class="graph">
<h3>Hourly basal rates</h3>
<p>Here are the most recent hourly basal rates: totalling <span id="basalTotal"></span> IU/day.</p>
<div id="hourlyBasal"></div>
<div id="phh" style="width: 85%; height: 300px"></div>
</div>
<div class="graph">
<h3>Hourly percentile values</h3>
<p>Here are percentiled values for each hour</p>
<div id="php" style="width: 85%; height: 300px"></div>
</div>

<div class="graph">
<h3>Daily overlay</h3>
<p>Here are the daily readings overlaid for this time-frame.</p>
Expand Down Expand Up @@ -88,11 +102,58 @@
plots.push($.plot($("#pho"), overlays,
$.extend(true,{},
flotOptions, {
grid:{markings: nightAreas },
yaxes:[{tickFormatter: bGFormatter, tickDecimals:1}],
legend:{show: false}
})));
});
$.getJSON("<%=application.getContextPath()%>/api/home/hourlyBasal" + window.location.search, function (model) {
var basal = [];
var html = '<table width="85%">';
for (var index=0; index<6; index+=1){
html += '<tr>';
for (var subindex=0; subindex<24; subindex+=6){
var hour = index+subindex;
html+='<td width="25%" style="text-align: right">'+(hour)+":00 - "+(hour+1)+":00 " + (model.hours[hour]).toFixed(1) + " IU/hr</td>";
}
html += '</tr>'
}
html+='</table>';
$('#hourlyBasal').html(html);
$('#basalTotal').html(model["daily_total"]);
for (var index = 0; index < model.hours.length; index++) {
var dt = new Date();
dt.setMinutes(0);
dt.setHours(index - (dt.getTimezoneOffset()/60));
basal.push([dt, model.hours[index]]);
}
plots.push($.plot($("#phh"), [{ data: basal, label: "Hourly basal rates", lines: { show: true, steps: true }}],
$.extend(true,{},
flotOptions, {
yaxes:[{tickFormatter: bolusRateFormatter, tickDecimals:1}]}
)));
});
$.getJSON("<%=application.getContextPath()%>/api/home/hourlyPercentiles" + window.location.search, function (model) {
var dataset = [
{ label: 'Glucose percentiles', data: model['50'], lines: { show: true }, color: "rgb(255,50,50)" },
{ id: 'bg15%', data: model['15'], lines: { show: true, lineWidth: 0, fill: false }, color: "rgb(255,50,50)" },
{ id: 'bg25%', data: model['25'], lines: { show: true, lineWidth: 0, fill: 0.2 }, color: "rgb(255,50,50)", fillBetween: 'bg15%' },
{ id: 'bg50%', data: model['50'], lines: { show: true, lineWidth: 0.5, fill: 0.4, shadowSize: 0 }, color: "rgb(255,50,50)", fillBetween: 'bg25%' },
{ id: 'bg75%', data: model['75'], lines: { show: true, lineWidth: 0, fill: 0.4 }, color: "rgb(255,50,50)", fillBetween: 'bg50%' },
{ id: 'bg85%', data: model['85'], lines: { show: true, lineWidth: 0, fill: 0.2 }, color: "rgb(255,50,50)", fillBetween: 'bg75%' }
];
plots.push($.plot($("#php"), dataset, $.extend(true, {},
flotOptions, {
xaxis: { mode: null, min: 0, max: 23, ticks:[2,4,6,8,10,12,14,16,18,20,22] },
yaxes: [{tickFormatter: bGFormatter, tickDecimals:1}],
legend: { position: 'se' }
})));
});
setTimeout(function(){
for (var index = 0; index < plots.length; index++){
plots[index].getPlaceholder().bind("plothover", function (event, pos, item) {
Expand Down
4 changes: 2 additions & 2 deletions webapp/src/main/webapp/index.jsp
@@ -1,4 +1,4 @@
<%@ page import="org.joda.time.DateTime,endafarrell.orla.service.OrlaDateTimeFormat"%><%
<%
String to = OrlaDateTimeFormat.PRETTY_yyyyMMdd.print(DateTime.now());
String from = OrlaDateTimeFormat.PRETTY_yyyyMMdd.print(DateTime.now().minusWeeks(12));
%><!DOCTYPE HTML>
Expand Down Expand Up @@ -33,7 +33,7 @@
<span class="unit">g carbs</span>
{{else clazz=="BloodGlucoseEvent"}}
<span class="time">${hhmm}</span>
<span class="bG">${value}</span>
<span class="bG">${value.toFixed(1)}</span>
<span class="unit">mmol/L</span>
{{else clazz=="TwitterEvent"}}
<span class="time">${hhmm}</span>
Expand Down

0 comments on commit a6a221c

Please sign in to comment.