Browse files

Merge pull request #20 from sboschman/master

loess interpolatie voor de grafieken
  • Loading branch information...
2 parents 14b079b + f8e92a5 commit 2afe924f3a5ef3c0b8b931b157d435535c8a5035 @dashorst committed Mar 20, 2012
View
6 pom.xml
@@ -214,6 +214,12 @@
<artifactId>svnkit</artifactId>
<version>1.3.5</version>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-math</artifactId>
+ <version>2.2</version>
+ </dependency>
</dependencies>
<build>
<resources>
View
10 src/main/java/nl/topicus/onderwijs/dashboard/datasources/LastServerCheckTime.java
@@ -0,0 +1,10 @@
+package nl.topicus.onderwijs.dashboard.datasources;
+
+import java.util.Date;
+
+import nl.topicus.onderwijs.dashboard.modules.DataSource;
+import nl.topicus.onderwijs.dashboard.modules.DataSourceSettings;
+
+@DataSourceSettings(label = "Last servercheck", htmlClass = "date", type = Date.class)
+public interface LastServerCheckTime extends DataSource<Date> {
+}
View
4 src/main/java/nl/topicus/onderwijs/dashboard/modules/RandomDataRepositoryImpl.java
@@ -150,7 +150,9 @@ public Object invoke(Object proxy, Method method, Object[] args)
.milliseconds(Math.round(Math.random() * 100000000));
else if (settings.type().equals(String.class))
value = "random";
- else if (settings.type().equals(WeatherReport.class)) {
+ else if (settings.type().equals(Date.class)) {
+ value = new Date();
+ } else if (settings.type().equals(WeatherReport.class)) {
value = createRandomWeather();
} else if (settings.type().equals(Dot.class)
&& settings.list()) {
View
30 src/main/java/nl/topicus/onderwijs/dashboard/modules/plots/DataSourcePlotSeries.java
@@ -2,10 +2,8 @@
import java.io.Serializable;
import java.util.Date;
-import java.util.Iterator;
import nl.topicus.onderwijs.dashboard.keys.Key;
-import nl.topicus.onderwijs.dashboard.modules.DashboardRepository;
import nl.topicus.onderwijs.dashboard.modules.DataSource;
import nl.topicus.wqplot.data.AbstractSeries;
@@ -14,18 +12,14 @@
Serializable {
private static final long serialVersionUID = 1L;
private Key key;
- private Class<D> dataSource;
private T minValue;
private T maxValue;
- public DataSourcePlotSeries(Key key, Class<D> dataSource) {
+ public DataSourcePlotSeries(Key key) {
this.key = key;
- this.dataSource = dataSource;
}
- public void addEntry(DashboardRepository repository) {
- D source = repository.getData(dataSource).get(key);
- T value = source == null ? null : source.getValue();
+ public void addEntry(Date time, T value) {
if (value != null) {
if (minValue == null
|| minValue.doubleValue() > value.doubleValue())
@@ -34,7 +28,7 @@ public void addEntry(DashboardRepository repository) {
|| maxValue.doubleValue() < value.doubleValue())
maxValue = value;
}
- addEntry(new DataSourcePlotSeriesEntry<T>(new Date(), value));
+ addEntry(new DataSourcePlotSeriesEntry<T>(time, value));
}
public Key getKey() {
@@ -49,19 +43,9 @@ public T getMaxValue() {
return maxValue;
}
- /**
- * Removes any data with date older than ttlDate.
- *
- * @param ttlDate
- */
- public void cleanupEntries(Date ttlDate) {
- Iterator<DataSourcePlotSeriesEntry<T>> iter = getData().iterator();
- while (iter.hasNext()) {
- DataSourcePlotSeriesEntry<T> entry = iter.next();
- if (entry.getKey().before(ttlDate))
- iter.remove();
- else
- return; //stop if date is after ttlDate, the series is ordered chronologically.
- }
+ public void clear() {
+ minValue = null;
+ maxValue = null;
+ getData().clear();
}
}
View
78 src/main/java/nl/topicus/onderwijs/dashboard/modules/plots/DataSourceSeries.java
@@ -0,0 +1,78 @@
+package nl.topicus.onderwijs.dashboard.modules.plots;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import nl.topicus.onderwijs.dashboard.datasources.LastServerCheckTime;
+import nl.topicus.onderwijs.dashboard.keys.Key;
+import nl.topicus.onderwijs.dashboard.modules.DashboardRepository;
+import nl.topicus.onderwijs.dashboard.modules.DataSource;
+
+public class DataSourceSeries<T extends Number, D extends DataSource<T>>
+ implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private Key key;
+ private Class<D> dataSource;
+ private LinkedList<DataSourceSeriesEntry<T>> data = new LinkedList<DataSourceSeriesEntry<T>>();
+
+ public DataSourceSeries(Key key, Class<D> dataSource) {
+ this.key = key;
+ this.dataSource = dataSource;
+ }
+
+ public Key getKey() {
+ return key;
+ }
+
+ public Class<D> getDataSource() {
+ return dataSource;
+ }
+
+ public List<DataSourceSeriesEntry<T>> getData() {
+ return Collections.unmodifiableList(data);
+ }
+
+ public DataSourceSeriesEntry<T> getFirstEntry() {
+ return data.getFirst();
+ }
+
+ public DataSourceSeriesEntry<T> getLastEntry() {
+ return data.isEmpty() ? null : data.getLast();
+ }
+
+ public void addEntry(DashboardRepository repository) {
+ LastServerCheckTime time = repository
+ .getData(LastServerCheckTime.class).get(key);
+ Date timeValue = time == null ? null : time.getValue();
+ if (timeValue == null)
+ return;
+
+ D source = repository.getData(dataSource).get(key);
+ T value = source == null ? null : source.getValue();
+
+ if ((data.isEmpty() || data.getLast().getKey().before(timeValue))
+ && value != null)
+ data.add(new DataSourceSeriesEntry<T>(timeValue, value));
+ }
+
+ /**
+ * Removes any data with date older than ttlDate.
+ *
+ * @param ttlDate
+ */
+ public void cleanupEntries(Date ttlDate) {
+ Iterator<DataSourceSeriesEntry<T>> iter = data.iterator();
+ while (iter.hasNext()) {
+ DataSourceSeriesEntry<T> entry = iter.next();
+ if (entry.getKey().before(ttlDate))
+ iter.remove();
+ else
+ return; // stop if date is after ttlDate, the series is ordered
+ // chronologically.
+ }
+ }
+}
View
33 src/main/java/nl/topicus/onderwijs/dashboard/modules/plots/DataSourceSeriesEntry.java
@@ -0,0 +1,33 @@
+package nl.topicus.onderwijs.dashboard.modules.plots;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public class DataSourceSeriesEntry<T extends Number> implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private Date key;
+
+ private T value;
+
+ public DataSourceSeriesEntry(Date key, T value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public Date getKey() {
+ return key;
+ }
+
+ public void setKey(Date key) {
+ this.key = key;
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ public void setValue(T value) {
+ this.value = value;
+ }
+}
View
129 src/main/java/nl/topicus/onderwijs/dashboard/modules/plots/PlotService.java
@@ -2,6 +2,7 @@
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -19,16 +20,23 @@
import nl.topicus.onderwijs.dashboard.modules.ServiceConfiguration;
import nl.topicus.onderwijs.dashboard.web.WicketApplication;
+import org.apache.commons.math.ArgumentOutsideDomainException;
+import org.apache.commons.math.MathException;
+import org.apache.commons.math.analysis.interpolation.LoessInterpolator;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@ServiceConfiguration(interval = 1, unit = TimeUnit.MINUTES, runInRandomMode = true)
public class PlotService extends AbstractService {
+ private Map<PlotKey, DataSourceSeries<?, ?>> data = new HashMap<PlotKey, DataSourceSeries<?, ?>>();
private Map<PlotKey, DataSourcePlotSeries<?, ?>> series = new HashMap<PlotKey, DataSourcePlotSeries<?, ?>>();
private WicketApplication application;
+ private LoessInterpolator loessInterpolator;
+
@Autowired
public PlotService(ISettings settings) {
super(settings);
@@ -41,6 +49,7 @@ public void setApplication(WicketApplication application) {
@Override
public void onConfigure(DashboardRepository repository) {
+ loessInterpolator = new LoessInterpolator();
for (Project curProject : repository.getKeys(Project.class)) {
if (repository.getData(AverageRequestTime.class).containsKey(
curProject))
@@ -55,39 +64,121 @@ public void onConfigure(DashboardRepository repository) {
private <T extends Number, D extends DataSource<T>> void addSeries(
Project project, Class<D> dataSource) {
- series.put(new PlotKey(project, dataSource),
- new DataSourcePlotSeries<T, D>(project, dataSource));
+ PlotKey key = new PlotKey(project, dataSource);
+ data.put(key, new DataSourceSeries<T, D>(project, dataSource));
+ series.put(key, new DataSourcePlotSeries<T, D>(project));
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Number, D extends DataSource<T>> List<DataSourcePlotSeries<T, D>> getSeries(
+ Class<D> dataSource) {
+ List<DataSourcePlotSeries<T, D>> ret = new ArrayList<DataSourcePlotSeries<T, D>>();
+ for (Project curProject : application.getRepository().getKeys(
+ Project.class)) {
+ ret.add((DataSourcePlotSeries<T, D>) series.get(new PlotKey(
+ curProject, dataSource)));
+ }
+ return ret;
}
@Override
public void refreshData() {
+ cleanupDataEntries();
+
+ updateDataEntries();
+
+ updateAllPlotSeries();
+ }
+
+ private void cleanupDataEntries() {
Map<Key, Map<String, ?>> serviceSettings = getSettings()
.getServiceSettings(PlotService.class);
-
- for (DataSourcePlotSeries<?, ?> curSeries : series.values()) {
- if (serviceSettings.containsKey(curSeries.getKey())
- && serviceSettings.get(curSeries.getKey()).containsKey(
+ for (DataSourceSeries<?, ?> curData : data.values()) {
+ if (serviceSettings.containsKey(curData.getKey())
+ && serviceSettings.get(curData.getKey()).containsKey(
"timeToLive")) {
int dataTTL = Integer.parseInt(serviceSettings
- .get(curSeries.getKey()).get("timeToLive").toString());
+ .get(curData.getKey()).get("timeToLive").toString());
Calendar ttlDate = Calendar.getInstance();
ttlDate.add(Calendar.SECOND, 0 - dataTTL);
-
- curSeries.cleanupEntries(ttlDate.getTime());
+ curData.cleanupEntries(ttlDate.getTime());
}
- curSeries.addEntry(application.getRepository());
}
}
- @SuppressWarnings("unchecked")
- public <T extends Number, D extends DataSource<T>> List<DataSourcePlotSeries<T, D>> getSeries(
- Class<D> dataSource) {
- List<DataSourcePlotSeries<T, D>> ret = new ArrayList<DataSourcePlotSeries<T, D>>();
- for (Project curProject : application.getRepository().getKeys(
- Project.class)) {
- ret.add((DataSourcePlotSeries<T, D>) series.get(new PlotKey(
- curProject, dataSource)));
+ private void updateDataEntries() {
+ for (DataSourceSeries<?, ?> curData : data.values()) {
+ curData.addEntry(application.getRepository());
}
- return ret;
+ }
+
+ private void updateAllPlotSeries() {
+ for (DataSourceSeries<?, ?> curData : data.values()) {
+ DataSourcePlotSeries<Integer, ?> curSeries = (DataSourcePlotSeries<Integer, ?>) series
+ .get(new PlotKey(curData.getKey(), curData.getDataSource()));
+ updatePlotSeries(curSeries, curData);
+ }
+ }
+
+ private void updatePlotSeries(DataSourcePlotSeries<Integer, ?> curSeries,
+ DataSourceSeries<?, ?> curData) {
+ if (!updatePlotSeriesWithLoessInterpolatorData(curSeries, curData)) {
+ updatePlotSeriesWithOriginalData(curSeries, curData);
+ }
+ }
+
+ private void updatePlotSeriesWithOriginalData(
+ DataSourcePlotSeries<Integer, ?> curSeries,
+ DataSourceSeries<?, ?> curData) {
+ curSeries.clear();
+ for (DataSourceSeriesEntry<?> entry : curData.getData()) {
+ curSeries.addEntry(entry.getKey(), (Integer) entry.getValue());
+ }
+ }
+
+ private boolean updatePlotSeriesWithLoessInterpolatorData(
+ DataSourcePlotSeries<Integer, ?> curSeries,
+ DataSourceSeries<?, ?> curData) {
+ if (curData.getData().size() < 10) {
+ return false;
+ }
+
+ Date last = curData.getLastEntry().getKey();
+ double[] xvals = new double[curData.getData().size()];
+ double[] yvals = new double[curData.getData().size()];
+ for (int i = 0; i < curData.getData().size(); i++) {
+ xvals[i] = new Long(curData.getData().get(i).getKey().getTime());
+ if (curData.getData().get(i).getValue() != null) {
+ yvals[i] = curData.getData().get(i).getValue().doubleValue();
+ }
+ }
+
+ PolynomialSplineFunction psf = null;
+ try {
+ psf = loessInterpolator.interpolate(xvals, yvals);
+ } catch (MathException e) {
+ e.printStackTrace();
+ return false;
+ }
+ if (psf != null) {
+ curSeries.clear();
+ Date time = curData.getFirstEntry().getKey();
+ do {
+ try {
+ double v = psf.value(time.getTime());
+ curSeries.addEntry(time, Double.valueOf(v).intValue());
+
+ } catch (ArgumentOutsideDomainException e) {
+ e.printStackTrace();
+ return false;
+ }
+ Calendar c = Calendar.getInstance();
+ c.setTime(time);
+ c.add(Calendar.SECOND, 10);
+ time = c.getTime();
+ } while (time.before(last));
+ return true;
+ }
+ return false;
}
}
View
14 ...modules/topicus/VocusStatusRetriever.java → ...modules/topicus/CobraStatusRetriever.java
@@ -18,6 +18,7 @@
import nl.topicus.onderwijs.dashboard.config.ISettings;
import nl.topicus.onderwijs.dashboard.datasources.ApplicationVersion;
import nl.topicus.onderwijs.dashboard.datasources.AverageRequestTime;
+import nl.topicus.onderwijs.dashboard.datasources.LastServerCheckTime;
import nl.topicus.onderwijs.dashboard.datasources.NumberOfServers;
import nl.topicus.onderwijs.dashboard.datasources.NumberOfServersOffline;
import nl.topicus.onderwijs.dashboard.datasources.NumberOfUsers;
@@ -41,17 +42,17 @@
@Service
@ServiceConfiguration(interval = 30, unit = TimeUnit.SECONDS)
-public class VocusStatusRetriever extends AbstractService implements
+public class CobraStatusRetriever extends AbstractService implements
TopicusApplicationStatusProvider {
private static final Logger log = LoggerFactory
- .getLogger(VocusStatusRetriever.class);
+ .getLogger(CobraStatusRetriever.class);
private Map<Key, TopicusApplicationStatus> statusses = new HashMap<Key, TopicusApplicationStatus>();
private Map<String, Alert> oldAlerts = new HashMap<String, Alert>();
@Autowired
- public VocusStatusRetriever(ISettings settings) {
+ public CobraStatusRetriever(ISettings settings) {
super(settings);
}
@@ -63,7 +64,7 @@ public TopicusApplicationStatus getStatus(Key project) {
@Override
public void onConfigure(DashboardRepository repository) {
Map<Key, Map<String, ?>> serviceSettings = getSettings()
- .getServiceSettings(VocusStatusRetriever.class);
+ .getServiceSettings(CobraStatusRetriever.class);
for (Key project : serviceSettings.keySet()) {
repository.addDataSource(project, NumberOfUsers.class,
new NumberOfUsersImpl(project, this));
@@ -85,6 +86,8 @@ public void onConfigure(DashboardRepository repository) {
new AlertsImpl(project, this));
repository.addDataSource(project, NumberOfUsersPerServer.class,
new NumberOfUsersPerServerImpl(project, this));
+ repository.addDataSource(project, LastServerCheckTime.class,
+ new LastServerCheckTimeImpl(project, this));
}
}
@@ -93,7 +96,7 @@ public void onConfigure(DashboardRepository repository) {
public void refreshData() {
HashMap<Key, TopicusApplicationStatus> newStatusses = new HashMap<Key, TopicusApplicationStatus>();
Map<Key, Map<String, ?>> serviceSettings = getSettings()
- .getServiceSettings(VocusStatusRetriever.class);
+ .getServiceSettings(CobraStatusRetriever.class);
for (Map.Entry<Key, Map<String, ?>> configEntry : serviceSettings
.entrySet()) {
@@ -217,6 +220,7 @@ private void fetchSessionAndRequestData(TopicusServerStatus server,
}
}
}
+ server.setTime(new Date());
}
private void fetchApplicationVersion(TopicusServerStatus server,
View
23 src/main/java/nl/topicus/onderwijs/dashboard/modules/topicus/LastServerCheckTimeImpl.java
@@ -0,0 +1,23 @@
+package nl.topicus.onderwijs.dashboard.modules.topicus;
+
+import java.util.Date;
+
+import nl.topicus.onderwijs.dashboard.datasources.LastServerCheckTime;
+import nl.topicus.onderwijs.dashboard.keys.Key;
+
+class LastServerCheckTimeImpl implements LastServerCheckTime {
+ private final TopicusApplicationStatusProvider provider;
+ private final Key project;
+
+ public LastServerCheckTimeImpl(Key project,
+ TopicusApplicationStatusProvider provider) {
+ this.project = project;
+ this.provider = provider;
+ }
+
+ @Override
+ public Date getValue() {
+ TopicusApplicationStatus status = provider.getStatus(project);
+ return status == null ? null : status.getLastCheckTime();
+ }
+}
View
10 ...s/topicus/VocusOuderportaalRetriever.java → ...topicus/SomOnderwijsportaalRetriever.java
@@ -42,24 +42,24 @@
@Service
@ServiceConfiguration(interval = 30, unit = TimeUnit.SECONDS)
-public class VocusOuderportaalRetriever extends AbstractService implements
+public class SomOnderwijsportaalRetriever extends AbstractService implements
TopicusApplicationStatusProvider {
private static final Logger log = LoggerFactory
- .getLogger(VocusOuderportaalRetriever.class);
+ .getLogger(SomOnderwijsportaalRetriever.class);
private Map<Key, TopicusApplicationStatus> statusses = new HashMap<Key, TopicusApplicationStatus>();
private Map<String, Alert> oldAlerts = new HashMap<String, Alert>();
@Autowired
- public VocusOuderportaalRetriever(ISettings settings) {
+ public SomOnderwijsportaalRetriever(ISettings settings) {
super(settings);
}
@Override
public void onConfigure(DashboardRepository repository) {
Map<Key, Map<String, ?>> serviceSettings = getSettings()
- .getServiceSettings(VocusOuderportaalRetriever.class);
+ .getServiceSettings(SomOnderwijsportaalRetriever.class);
for (Key project : serviceSettings.keySet()) {
repository.addDataSource(project, NumberOfUsers.class,
new NumberOfUsersImpl(project, this));
@@ -94,7 +94,7 @@ public TopicusApplicationStatus getStatus(Key project) {
public void refreshData() {
HashMap<Key, TopicusApplicationStatus> newStatusses = new HashMap<Key, TopicusApplicationStatus>();
Map<Key, Map<String, ?>> serviceSettings = getSettings()
- .getServiceSettings(VocusOuderportaalRetriever.class);
+ .getServiceSettings(SomOnderwijsportaalRetriever.class);
for (Map.Entry<Key, Map<String, ?>> configEntry : serviceSettings
.entrySet()) {
View
12 src/main/java/nl/topicus/onderwijs/dashboard/modules/topicus/TopicusApplicationStatus.java
@@ -3,6 +3,7 @@
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import nl.topicus.onderwijs.dashboard.datatypes.Alert;
@@ -117,6 +118,17 @@ public Long getUptime() {
return ret;
}
+ public Date getLastCheckTime() {
+ Date ret = null;
+ for (TopicusServerStatus curServer : getOnlineServers()) {
+ if (curServer.getTime() != null) {
+ if (ret == null || curServer.getTime().after(ret))
+ ret = curServer.getTime();
+ }
+ }
+ return ret;
+ }
+
public List<Dot> getServerStatusses() {
List<Dot> ret = new ArrayList<Dot>();
for (TopicusServerStatus curServer : getServers()) {
View
10 src/main/java/nl/topicus/onderwijs/dashboard/modules/topicus/TopicusServerStatus.java
@@ -2,6 +2,7 @@
import java.io.Serializable;
import java.io.StringWriter;
+import java.util.Date;
import nl.topicus.onderwijs.dashboard.datatypes.DotColor;
@@ -11,6 +12,7 @@
public class TopicusServerStatus implements Serializable {
private static final long serialVersionUID = 1L;
+ private Date lastCheckTime;
private String version;
private int numberOfUsers;
private int numberOfErrors;
@@ -26,6 +28,14 @@ public TopicusServerStatus(String code, String url) {
this.url = url;
}
+ public Date getTime() {
+ return lastCheckTime;
+ }
+
+ public void setTime(Date time) {
+ this.lastCheckTime = time;
+ }
+
public String getVersion() {
return version == null ? "n/a" : version;
}
View
2 ...main/java/nl/topicus/onderwijs/dashboard/plotsources/NumberOfUsersPerApplicationPlot.java
@@ -35,7 +35,7 @@ public JQPlot createPlot(String id) {
setDefaultOptions(options);
setSeriesLabels(options, series);
- setAxisMinAndMax(options, series, 500);
+ setAxisMinAndMax(options, series, 200);
return ret;
}
}
View
2 .../java/nl/topicus/onderwijs/dashboard/plotsources/RequestsPerMinutePerApplicationPlot.java
@@ -36,7 +36,7 @@ public JQPlot createPlot(String id) {
setDefaultOptions(options);
setSeriesLabels(options, series);
- setAxisMinAndMax(options, series, 500);
+ setAxisMinAndMax(options, series, 200);
return ret;
}
}

0 comments on commit 2afe924

Please sign in to comment.