implemented functions for statistics in charts (sum, avg, min, max, cnt)

- extended 93_DbLog.pm for these statisticfunctions, handling PostgreSQL, MYSQL and SQLITE DBs

git-svn-id: svn://svn.code.sf.net/p/fhem/code/trunk@2947 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
johannnes
2013-03-18 20:11:20 +00:00
parent 4923450556
commit c4890a03ed
6 changed files with 226 additions and 19 deletions

View File

@@ -1,3 +1,12 @@
Update vom 18.3.2013
* Implementation einer Statistikfunktion für Charts, die für ein beliebiges Reading
* Summen
* Mittelwerte
* Maximalwerte
* Minimalwerte
* Insgesamte Anzahl
über einen definierbaren Zeitraum abfragt und darstellt
* Erweiterung in 93_DbLog.pm für die o.g. Statistikfunktionen für PostgreSQL, MYSQL und SQLITE
Update vom 16.3.2013
* Implementation einer clientseitigen Generalisierung
* Implementation eines Trees im linken Bereich des Frontends, über den Geräte gesichtet und gewählt werden können

View File

@@ -761,6 +761,7 @@ sub jsonError($) {
################################################################
sub prepareSql(@_) {
my ($hash, @a) = @_;
my $starttime = $_[5];
$starttime =~ s/_/ /;
my $endtime = $_[6];
@@ -773,14 +774,80 @@ sub prepareSql(@_) {
my $jsonChartConfig = $_[12];
my $pagingstart = $_[13];
my $paginglimit = $_[14];
my ($sql, $jsonstring, $countsql);
my $dbmodel = $hash->{DBMODEL};
my ($sql, $jsonstring, $countsql, $hourstats, $daystats, $weekstats, $monthstats, $yearstats);
if ($dbmodel eq "POSTGRESQL") {
### POSTGRESQL Queries for Statistics ###
### hour:
$hourstats = "SELECT to_char(timestamp, 'YYYY-MM-DD HH24:00:00') AS TIMESTAMP, SUM(VALUE::float) AS SUM, AVG(VALUE::float) AS AVG, MIN(VALUE::float) AS MIN, MAX(VALUE::float) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY 1 ORDER BY 1;";
### day:
$daystats = "SELECT to_char(timestamp, 'YYYY-MM-DD 00:00:00') AS TIMESTAMP, SUM(VALUE::float) AS SUM, AVG(VALUE::float) AS AVG, MIN(VALUE::float) AS MIN, MAX(VALUE::float) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY 1 ORDER BY 1;";
### week:
$weekstats = "SELECT date_trunc('week',timestamp) AS TIMESTAMP, SUM(VALUE::float) AS SUM, AVG(VALUE::float) AS AVG, MIN(VALUE::float) AS MIN, MAX(VALUE::float) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY 1 ORDER BY 1;";
### month:
$monthstats = "SELECT to_char(timestamp, 'YYYY-MM-01 00:00:00') AS TIMESTAMP, SUM(VALUE::float) AS SUM, AVG(VALUE::float) AS AVG, MIN(VALUE::float) AS MIN, MAX(VALUE::float) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY 1 ORDER BY 1;";
### year:
$yearstats = "SELECT to_char(timestamp, 'YYYY-01-01 00:00:00') AS TIMESTAMP, SUM(VALUE::float) AS SUM, AVG(VALUE::float) AS AVG, MIN(VALUE::float) AS MIN, MAX(VALUE::float) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY 1 ORDER BY 1;";
} elsif ($dbmodel eq "MYSQL") {
### MYSQL Queries for Statistics ###
### hour:
$hourstats = "SELECT date_format(timestamp, '%Y-%m-%d %H:00:00') AS TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY 1 ORDER BY 1;";
### day:
$daystats = "SELECT date_format(timestamp, '%Y-%m-%d 00:00:00') AS TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY 1 ORDER BY 1;";
### week:
$weekstats = "SELECT date_format(timestamp, '%Y-%m-%d 00:00:00') AS TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY date_format(timestamp, '%Y-%u 00:00:00') ORDER BY 1;";
### month:
$monthstats = "SELECT date_format(timestamp, '%Y-%m-01 00:00:00') AS TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY 1 ORDER BY 1;";
### year:
$yearstats = "SELECT date_format(timestamp, '%Y-01-01 00:00:00') AS TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY 1 ORDER BY 1;";
} elsif ($hash->{DBMODEL} eq "SQLITE") {
### SQLITE Queries for Statistics ###
### hour:
$hourstats = "SELECT TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY strftime('%Y-%m-%d %H:00:00', TIMESTAMP);";
### day:
$daystats = "SELECT TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY strftime('%Y-%m-%d 00:00:00', TIMESTAMP);";
### week:
$weekstats = "SELECT TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY strftime('%Y-%W 00:00:00', TIMESTAMP);";
### month:
$monthstats = "SELECT TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY strftime('%Y-%m 00:00:00', TIMESTAMP);";
### year:
$yearstats = "SELECT TIMESTAMP, SUM(VALUE) AS SUM, AVG(VALUE) AS AVG, MIN(VALUE) AS MIN, MAX(VALUE) AS MAX, COUNT(VALUE) AS COUNT FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime' GROUP BY strftime('%Y 00:00:00', TIMESTAMP);";
} else {
$sql = "errordb";
}
if($userquery eq "getreadings") {
$sql = "SELECT distinct(reading) FROM history WHERE device = '".$device."'";
} elsif($userquery eq "getdevices") {
$sql = "SELECT distinct(device) FROM history";
} elsif($userquery eq "timerange") {
$sql = "SELECT ".$xaxis.", VALUE FROM history WHERE READING = '$yaxis' AND DEVICE = '$device' AND TIMESTAMP Between '$starttime' AND '$endtime';";
} elsif($userquery eq "hourstats") {
$sql = $hourstats;
} elsif($userquery eq "daystats") {
$sql = $daystats;
} elsif($userquery eq "weekstats") {
$sql = $weekstats;
} elsif($userquery eq "monthstats") {
$sql = $monthstats;
} elsif($userquery eq "yearstats") {
$sql = $yearstats;
} elsif($userquery eq "savechart") {
$sql = "INSERT INTO frontend (TYPE, NAME, VALUE) VALUES ('savedchart', '$savename', '$jsonChartConfig')";
} elsif($userquery eq "deletechart") {
@@ -809,7 +876,10 @@ sub chartQuery($@) {
if ($sql eq "error") {
return jsonError("Could not setup SQL String. Maybe the Database is busy, please try again!");
} elsif ($sql eq "errordb") {
return jsonError("The Database Type is not supported!");
}
my ($hash, @a) = @_;
my $dbhf= $hash->{DBHF};

View File

@@ -42,16 +42,16 @@ UPD 2013-03-08_07:13:10 563 www/frontend/index.html
UPD 2013-03-06_11:11:22 236 www/frontend/README.txt
UPD 2013-03-08_01:44:54 613 www/frontend/app/userconfig.js
UPD 2013-03-06_11:11:22 1856 www/frontend/app/app.js
UPD 2013-03-16_06:58:22 25147 www/frontend/app/view/LineChartPanel.js
UPD 2013-03-18_06:58:48 27330 www/frontend/app/view/LineChartPanel.js
UPD 2013-03-16_06:15:53 7586 www/frontend/app/view/Viewport.js
UPD 2013-03-16_06:15:46 4279 www/frontend/app/view/DevicePanel.js
UPD 2013-03-06_11:11:22 2503 www/frontend/app/view/TableDataGridPanel.js
UPD 2013-03-06_11:11:22 1310 www/frontend/app/view/LineChartView.js
UPD 2013-03-16_11:58:22 50747 www/frontend/app/controller/ChartController.js
UPD 2013-03-18_06:58:22 54163 www/frontend/app/controller/ChartController.js
UPD 2013-03-16_11:15:36 10115 www/frontend/app/controller/MainController.js
UPD 2013-03-06_11:11:22 202 www/frontend/app/model/ReadingsModel.js
UPD 2013-03-06_11:11:22 338 www/frontend/app/model/SavedChartsModel.js
UPD 2013-03-16_06:58:22 1809 www/frontend/app/model/ChartModel.js
UPD 2013-03-18_05:43:31 2173 www/frontend/app/model/ChartModel.js
UPD 2013-03-06_11:11:22 198 www/frontend/app/model/DeviceModel.js
UPD 2013-03-06_11:11:22 685 www/frontend/app/model/TableDataModel.js
UPD 2013-03-06_11:11:22 432 www/frontend/app/store/ChartStore.js

View File

@@ -61,6 +61,10 @@ Ext.define('FHEM.controller.ChartController', {
selector: 'combobox[name=yaxiscombo]',
ref: 'yaxiscombo' //this.getYaxiscombo()
},
{
selector: 'combobox[name=yaxisstatisticscombo]',
ref: 'yaxisstatisticscombo' //this.getYaxisstatisticscombo()
},
{
selector: 'combobox[name=yaxiscolorcombo]',
ref: 'yaxiscolorcombo' //this.getYaxiscombo()
@@ -225,7 +229,7 @@ Ext.define('FHEM.controller.ChartController', {
/**
* Triggers a request to FHEM Module to get the data from Database
*/
requestChartData: function() {
requestChartData: function(stepchangecalled) {
var me = this;
//getting the necessary values
@@ -234,6 +238,7 @@ Ext.define('FHEM.controller.ChartController', {
yaxis = me.getYaxiscombo().getValue(),
yaxiscolorcombo = me.getYaxiscolorcombo().getValue(),
yaxisfillcheck = me.getYaxisfillcheck().checked,
yaxisstatistics = me.getYaxisstatisticscombo().getValue(),
y2device = me.getDevice2combo().getValue(),
y2axis = me.getY2axiscombo().getValue(),
y2axiscolorcombo = me.getY2axiscolorcombo().getValue(),
@@ -286,7 +291,7 @@ Ext.define('FHEM.controller.ChartController', {
//check if timerange or dynamic time should be used
dynamicradio.eachBox(function(box, idx){
var date = new Date();
if (box.checked) {
if (box.checked && stepchangecalled !== true) {
if (box.inputValue === "year") {
starttime = Ext.Date.parse(date.getUTCFullYear() + "-01-01", "Y-m-d");
dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s');
@@ -304,18 +309,18 @@ Ext.define('FHEM.controller.ChartController', {
//monday starts with 0 till sat with 5, sund with -1
var dayoffset = date.getDay() - 1,
monday,
sunday;
nextmonday;
if (dayoffset >= 0) {
monday = Ext.Date.add(date, Ext.Date.DAY, -dayoffset);
} else {
//we have a sunday
monday = Ext.Date.add(date, Ext.Date.DAY, 1);
monday = Ext.Date.add(date, Ext.Date.DAY, -6);
}
sunday = Ext.Date.add(monday, Ext.Date.DAY, 6);
nextmonday = Ext.Date.add(monday, Ext.Date.DAY, 7);
starttime = monday;
dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s');
endtime = sunday;
endtime = nextmonday;
dbendtime = Ext.Date.format(endtime, 'Y-m-d_H:i:s');
} else if (box.inputValue === "day") {
@@ -339,6 +344,8 @@ Ext.define('FHEM.controller.ChartController', {
} else {
Ext.Msg.alert("Error", "Could not setup the dynamic time.");
}
me.getStarttimepicker().setValue(starttime);
me.getEndtimepicker().setValue(endtime);
}
});
@@ -379,12 +386,33 @@ Ext.define('FHEM.controller.ChartController', {
}
};
view.series.add(y1series);
if (proxy) {
var url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+';
var url;
if (yaxisstatistics === "none") {
url += '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+';
url +=device + '+timerange+' + xaxis + '+' + yaxis;
url += '&XHR=1';
} else { //setup url to get statistics
url += '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+';
url +=device;
if (yaxisstatistics.indexOf("hour") === 0) {
url += '+hourstats+';
} else if (yaxisstatistics.indexOf("day") === 0) {
url += '+daystats+';
} else if (yaxisstatistics.indexOf("week") === 0) {
url += '+weekstats+';
} else if (yaxisstatistics.indexOf("month") === 0) {
url += '+monthstats+';
} else if (yaxisstatistics.indexOf("year") === 0) {
url += '+yearstats+';
}
url += xaxis + '+' + yaxis;
url += '&XHR=1';
}
proxy.url = url;
store.load();
store.on("load", function() {
@@ -664,6 +692,33 @@ Ext.define('FHEM.controller.ChartController', {
//remove the old max values of y axis to get a dynamic range
delete view.axes.get(0).maximum;
if (yaxisstatistics.indexOf("none") > 0) {
y1series.yField = 'VALUE';
view.axes.get(0).maximum = store.max('VALUE');
} else if (yaxisstatistics.indexOf("sum") > 0) {
y1series.yField = 'SUM';
view.axes.get(0).maximum = store.max('SUM');
view.axes.get(0).setTitle("SUM " + yaxis);
} else if (yaxisstatistics.indexOf("average") > 0) {
y1series.yField = 'AVG';
view.axes.get(0).maximum = store.max('AVG');
view.axes.get(0).setTitle("AVG " + yaxis);
} else if (yaxisstatistics.indexOf("min") > 0) {
y1series.yField = 'MIN';
view.axes.get(0).maximum = store.max('MIN');
view.axes.get(0).setTitle("MIN " + yaxis);
} else if (yaxisstatistics.indexOf("max") > 0) {
y1series.yField = 'MAX';
view.axes.get(0).maximum = store.max('MAX');
view.axes.get(0).setTitle("MAX " + yaxis);
} else if (yaxisstatistics.indexOf("count") > 0) {
y1series.yField = 'COUNT';
view.axes.get(0).maximum = store.max('COUNT');
view.axes.get(0).setTitle("COUNT " + yaxis);
}
view.series.add(y1series);
view.redraw();
me.getLinechartview().setLoading(false);
}, this, {single: true});
@@ -815,13 +870,12 @@ Ext.define('FHEM.controller.ChartController', {
me.getEndtimepicker().setValue(starttime);
var newstarttime = Ext.Date.add(starttime, Ext.Date.MILLI, -timediff);
me.getStarttimepicker().setValue(newstarttime);
me.requestChartData();
me.requestChartData(true);
} else if (btn.name === "stepforward") {
me.getStarttimepicker().setValue(endtime);
var newendtime = Ext.Date.add(endtime, Ext.Date.MILLI, timediff);
me.getEndtimepicker().setValue(newendtime);
me.requestChartData();
me.requestChartData(true);
}
}
@@ -849,6 +903,7 @@ Ext.define('FHEM.controller.ChartController', {
yaxiscolorcombo = me.getYaxiscolorcombo().getDisplayValue(),
yaxisfillcheck = me.getYaxisfillcheck().checked,
yaxisstatistics = me.getYaxisstatisticscombo().getValue(),
y2device = me.getDevice2combo().getValue(),
y2axis = me.getY2axiscombo().getValue(),
y2axiscolorcombo = me.getY2axiscolorcombo().getDisplayValue(),
@@ -879,7 +934,7 @@ Ext.define('FHEM.controller.ChartController', {
generalizationfactor = Ext.ComponentQuery.query('combobox[name=genfactor]')[0].getValue(),
view = this.getLinechartview();
//setting the starttime parameter in the chartconfig to the string of the radiofield, gets parsed on load
//setting the start / endtime parameter in the chartconfig to the string of the radiofield, gets parsed on load
if (this.getStarttimepicker().isDisabled()) {
dynamicradio.eachBox(function(box, idx) {
if (box.checked) {
@@ -897,6 +952,10 @@ Ext.define('FHEM.controller.ChartController', {
jsonConfig += '"generalizationfactor":"' + generalizationfactor + '",';
}
if (yaxisstatistics !== "none") {
jsonConfig += '"yaxisstatistics":"' + yaxisstatistics + '",';
}
jsonConfig += '"y2device":"' + y2device + '",';
jsonConfig += '"y2axis":"' + y2axis + '","y2axiscolorcombo":"' + y2axiscolorcombo + '",';
jsonConfig += '"y2axisfillcheck":"' + y2axisfillcheck + '","y3axis":"' + y3axis + '",';
@@ -1043,7 +1102,7 @@ Ext.define('FHEM.controller.ChartController', {
});
} else {
var start = chartdata.starttime.replace("_", " "),
end = chartdata.endtime.replace("_", " ");
end = chartdata.endtime.replace("_", " ");
this.getStarttimepicker().setValue(start);
this.getEndtimepicker().setValue(end);
}
@@ -1059,6 +1118,12 @@ Ext.define('FHEM.controller.ChartController', {
genbox.setValue(false);
}
if (chartdata.yaxisstatistics && chartdata.yaxisstatistics !== "") {
this.getYaxisstatisticscombo().setValue(chartdata.yaxisstatistics);
} else {
this.getYaxisstatisticscombo().setValue("none");
}
this.requestChartData();
this.getLinechartpanel().setTitle(name);
} else {

View File

@@ -38,6 +38,26 @@ Ext.define('FHEM.model.ChartModel', {
convert: function(v,record) {
return record.parseToNumber(v, 3, record);
}
},
{
name: 'SUM',
type: 'float'
},
{
name: 'AVG',
type: 'float'
},
{
name: 'MIN',
type: 'float'
},
{
name: 'MAX',
type: 'float'
},
{
name: 'COUNT',
type: 'integer'
}
],
parseToNumber: function(value, idx, rec) {

View File

@@ -58,6 +58,38 @@ Ext.define('FHEM.view.LineChartPanel', {
me.comboReadings2Store = Ext.create('FHEM.store.ReadingsStore');
me.comboReadings3Store = Ext.create('FHEM.store.ReadingsStore');
me.comboStatisticsStore = Ext.create('Ext.data.Store', {
fields: ['name', 'value'],
data : [
{'name':'None','value':'none'},
{'name':'Hour Sum', 'value':'hoursum'},
{'name':'Hour Average', 'value':'houraverage'},
{'name':'Hour Min','value':'hourmin'},
{'name':'Hour Max','value':'hourmax'},
{'name':'Hour Count','value':'hourcount'},
{'name':'Day Sum', 'value':'daysum'},
{'name':'Day Average', 'value':'dayaverage'},
{'name':'Day Min','value':'daymin'},
{'name':'Day Max','value':'daymax'},
{'name':'Day Count','value':'daycount'},
{'name':'Week Sum', 'value':'weeksum'},
{'name':'Week Average', 'value':'weekaverage'},
{'name':'Week Min','value':'weekmin'},
{'name':'Week Max','value':'weekmax'},
{'name':'Week Count','value':'weekcount'},
{'name':'Month Sum', 'value':'monthsum'},
{'name':'Month Average', 'value':'monthaverage'},
{'name':'Month Min','value':'monthmin'},
{'name':'Month Max','value':'monthmax'},
{'name':'Month Count','value':'monthcount'},
{'name':'Year Sum', 'value':'yearsum'},
{'name':'Year Average', 'value':'yearaverage'},
{'name':'Year Min','value':'yearmin'},
{'name':'Year Max','value':'yearmax'},
{'name':'Year Count','value':'yearcount'}
]
});
var chartSettingPanel = Ext.create('Ext.form.Panel', {
title: 'Chart Settings - Click me to edit',
name: 'chartformpanel',
@@ -123,6 +155,17 @@ Ext.define('FHEM.view.LineChartPanel', {
name: 'yaxisfillcheck',
boxLabel: 'Fill'
},
{
xtype: 'combobox',
name: 'yaxisstatisticscombo',
fieldLabel: 'Statistics',
labelWidth: 70,
inputWidth: 120,
store: me.comboStatisticsStore,
displayField: 'name',
valueField: 'value',
value: me.comboStatisticsStore.getAt(0)
},
{
xtype: 'combobox',
name: 'device2combo',