partkeepr

fork of partkeepr
git clone https://git.e1e0.net/partkeepr.git
Log | Files | Refs | Submodules | README | LICENSE

commit 905dfd256de8c2731b07418a5aabc18bc44f9489
parent 8c53762f6b9125e0379858344b5697110f01d9d9
Author: Felicitus <felicitus@felicitus.org>
Date:   Fri,  9 Sep 2011 09:28:34 +0200

Added initial chart support

Diffstat:
Mfrontend/js/Components/MenuBar.js | 25++++++++++++++++++++++---
Afrontend/js/Components/Statistics/StatisticsChartPanel.js | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afrontend/js/Models/StatisticSample.js | 9+++++++++
Mfrontend/service.php | 8+++++++-
Msrc/de/RaumZeitLabor/PartKeepr/Statistic/StatisticService.php | 125++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
5 files changed, 239 insertions(+), 21 deletions(-)

diff --git a/frontend/js/Components/MenuBar.js b/frontend/js/Components/MenuBar.js @@ -33,8 +33,17 @@ Ext.define('PartKeepr.MenuBar', { icon: "resources/fugue-icons/icons/ruler.png" },{ text: i18n("Statistics"), - handler: this.showStatistics, - icon: 'resources/silkicons/chart_bar.png' + icon: 'resources/silkicons/chart_bar.png', + menu: [ + { + text: i18n("Summary"), + handler: this.showStatisticsSummary, + icon: 'resources/silkicons/chart_bar.png' + },{ + text: i18n("Chart"), + handler: this.showStatisticsChart, + icon: 'resources/silkicons/chart_bar.png' + }] },{ text: i18n("Units"), handler: this.editUnits, @@ -106,7 +115,7 @@ Ext.define('PartKeepr.MenuBar', { PartKeepr.getApplication().addItem(j); j.show(); }, - showStatistics: function () { + showStatisticsSummary: function () { var j = Ext.create("PartKeepr.CurrentStatisticsPanel", { iconCls: 'icon-chart-bar', closable: true @@ -116,6 +125,16 @@ Ext.define('PartKeepr.MenuBar', { PartKeepr.getApplication().addItem(j); j.show(); }, + showStatisticsChart: function () { + var j = Ext.create("PartKeepr.StatisticsChartPanel", { + iconCls: 'icon-chart-bar', + closable: true + }); + + + PartKeepr.getApplication().addItem(j); + j.show(); + }, editStorageLocations: function () { var j = Ext.create("PartKeepr.StorageLocationEditorComponent", { title: i18n("Storage Locations"), diff --git a/frontend/js/Components/Statistics/StatisticsChartPanel.js b/frontend/js/Components/Statistics/StatisticsChartPanel.js @@ -0,0 +1,92 @@ +Ext.define('PartKeepr.StatisticsChartPanel', { + extend: 'Ext.chart.Chart', + shadow: true, + title: i18n("Statistics Chart"), + legend: { + position: 'right' + }, + background: { + fill: '#fff' + }, + axes: [{ + type: 'Numeric', + minimum: 0, + position: 'left', + fields: ['parts', 'categories'], + title: i18n("Count"), + minorTickSteps: 1, + grid: { + odd: { + opacity: 1, + fill: '#ddd', + stroke: '#bbb', + 'stroke-width': 0.5 + } + } + }, { + type: 'Time', + dateFormat: 'Y-m-d', + position: 'bottom', + fields: ['start'], + title: i18n("Date") + }], + series: [{ + type: 'line', + highlight: { + size: 7, + radius: 7 + }, + axis: 'left', + xField: 'start', + yField: 'parts', + title: i18n("Parts"), + markerConfig: { + type: 'cross', + size: 4, + radius: 4, + 'stroke-width': 0 + } + }, { + type: 'line', + highlight: { + size: 7, + radius: 7 + }, + axis: 'left', + title: i18n("Categories"), + smooth: true, + xField: 'start', + yField: 'categories', + markerConfig: { + type: 'circle', + size: 4, + radius: 4, + 'stroke-width': 0 + } + }], + initComponent: function () { + this.store = Ext.create("Ext.data.Store", { + model: 'PartKeepr.StatisticSample', + proxy: { + type: 'ajax', + reader: { + type: 'json', + root: 'response.data' + }, + url : 'service.php', + extraParams: { + "service": "Statistic", + "call": "getSampledStatistics", + "startDateTime": "2011-01-01 00:00:00", + "endDateTime": "2011-12-01 00:00:00" + }, + headers: { + session :PartKeepr.getApplication().getSession() + } + }, + autoLoad: true + }); + + this.callParent(); + } +});+ \ No newline at end of file diff --git a/frontend/js/Models/StatisticSample.js b/frontend/js/Models/StatisticSample.js @@ -0,0 +1,8 @@ +Ext.define("PartKeepr.StatisticSample", { + extend: "Ext.data.Model", + fields: [ + { name: 'start', type: 'date'}, + { name: 'parts', type: 'int'}, + { name: 'categories', type: 'int'} + ] +});+ \ No newline at end of file diff --git a/frontend/service.php b/frontend/service.php @@ -13,7 +13,13 @@ PartKeepr::initialize(""); try { $request = $_REQUEST; - $request["parameters"] = json_decode($_REQUEST["parameters"], true); + + if (array_key_exists("parameters", $_REQUEST)) { + $request["parameters"] = json_decode($_REQUEST["parameters"], true); + } else { + $request["parameters"] = $_REQUEST; + } + $response = array(); $response["status"] = "ok"; diff --git a/src/de/RaumZeitLabor/PartKeepr/Statistic/StatisticService.php b/src/de/RaumZeitLabor/PartKeepr/Statistic/StatisticService.php @@ -9,29 +9,120 @@ use de\RaumZeitLabor\PartKeepr\PartKeepr, de\RaumZeitLabor\PartKeepr\PartUnit\PartUnitManager; class StatisticService extends Service { - public function getCurrentStats () { + public function getCurrentStats () { - $aData = array(); - $aData["partCount"] = PartManager::getInstance()->getPartCount(); - $aData["categoryCount"] = PartCategoryManager::getInstance()->getCategoryCount(); - $aData["totalPrice"] = PartManager::getInstance()->getTotalPrice(); - $aData["averagePrice"] = PartManager::getInstance()->getAveragePrice(); - $aData["partsWithPrice"] = PartManager::getInstance()->getPartCount(true); - $aData["partsWithoutPrice"] = $aData["partCount"] - $aData["partsWithPrice"]; - - $result = PartUnitManager::getInstance()->getUnitCounts(); + $aData = array(); + $aData["partCount"] = PartManager::getInstance()->getPartCount(); + $aData["categoryCount"] = PartCategoryManager::getInstance()->getCategoryCount(); + $aData["totalPrice"] = PartManager::getInstance()->getTotalPrice(); + $aData["averagePrice"] = PartManager::getInstance()->getAveragePrice(); + $aData["partsWithPrice"] = PartManager::getInstance()->getPartCount(true); + $aData["partsWithoutPrice"] = $aData["partCount"] - $aData["partsWithPrice"]; + + $result = PartUnitManager::getInstance()->getUnitCounts(); + + $aUnits = array(); + + foreach ($result as $row) { + $aUnits[] = array( + "name" => $row[0]->getName(), + "stockLevel" => $row["stockLevel"]); + } + + $aData["units"] = $aUnits; + + return $aData; + } - $aUnits = array(); + /** + * Returns sampled statistics from the database. + * + * This call takes a start and an end time, and calculates a set of statistics + * for each interval. + * + * The sampleSize, which has a default of 50, specifies how many single statistic + * points in the given date interval will be returned. + * + * This function interpolates the statistics if there are not enough statistic samples available. + */ + public function getSampledStatistics () { + $fooStart = microtime(true); + + $this->requireParameter("startDateTime"); + $this->requireParameter("endDateTime"); + + + $start = \DateTime::createFromFormat("Y-m-d H:i:s", $this->getParameter("startDateTime")); + $end = \DateTime::createFromFormat("Y-m-d H:i:s", $this->getParameter("endDateTime")); + + if ($this->hasParameter("sampleSize")) { + $sampleSize = $this->getParameter("sampleSize"); + } else { + $sampleSize = 50; + } + + $intervalSize = intval(($end->getTimestamp() - $start->getTimestamp()) / $sampleSize); + + $queryStartTime = clone $start; + $queryEndTime = clone $start; + $queryEndTime->add(new \DateInterval("PT".$intervalSize."S")); + + $partUnitQuery = "SELECT pu FROM de\RaumZeitLabor\PartKeepr\Part\PartUnit pu"; + $query = PartKeepr::getEM()->createQuery($partUnitQuery); + + $aPartUnits = $query->getResult(); + + $aRecords = array(); + + $dql = "SELECT AVG(sts.parts) AS parts, AVG(sts.categories) AS categories FROM de\RaumZeitLabor\PartKeepr\Statistic\StatisticSnapshot sts WHERE sts.dateTime >= :start AND sts.dateTime <= :end"; + $mainQuery = PartKeepr::getEM()->createQuery($dql); + + $dql = "SELECT AVG(stsu.stockLevel) AS stockLevel FROM de\RaumZeitLabor\PartKeepr\Statistic\StatisticSnapshotUnit stsu JOIN stsu.statisticSnapshot sts WHERE sts.dateTime >= :start AND sts.dateTime <= :end AND stsu.partUnit = :partUnit"; + $subQuery = PartKeepr::getEM()->createQuery($dql); + + for ($i=0;$i<$sampleSize;$i++) { + + + $mainQuery->setParameter("start", $queryStartTime); + $mainQuery->setParameter("end", $queryEndTime); + + $result = $mainQuery->getResult(); - foreach ($result as $row) { - $aUnits[] = array( - "name" => $row[0]->getName(), - "stockLevel" => $row["stockLevel"]); + $record = $result[0]; + + if ($record["parts"] !== null) { + $record["parts"] = floatval($record["parts"]); + } + + if ($record["categories"] !== null) { + $record["categories"] = floatval($record["categories"]); } - $aData["units"] = $aUnits; + foreach ($aPartUnits as $partUnit) { + $subQuery->setParameter("start", $queryStartTime); + $subQuery->setParameter("end", $queryEndTime); + $subQuery->setParameter("partUnit", $partUnit); + + $aResult = $subQuery->getResult(); + + if ($aResult[0]["stockLevel"] !== null) { + $record["units"][$partUnit->getName()] = floatval($aResult[0]["stockLevel"]); + } else { + $record["units"][$partUnit->getName()] = null; + } + + } + + $record["start"] = $queryStartTime->format("Y-m-d H:i:s"); + + $aRecords[] = $record; - return $aData; + $queryStartTime->add(new \DateInterval("PT".$intervalSize."S")); + $queryEndTime->add(new \DateInterval("PT".$intervalSize."S")); } + + + return array("status" => "ok", "data" => $aRecords); + } } \ No newline at end of file