partkeepr

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

commit d211813e3fec053a4355cd1b268367cdd5d27c69
parent feb3b00de534e972221397dfb5a80c93de8b77f7
Author: Felicitus <felicitus@felicitus.org>
Date:   Sat,  7 Nov 2015 18:24:01 +0100

Migrated statistic chart

Diffstat:
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Statistics/StatisticsChart.js | 270++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Statistics/StatisticsChartPanel.js | 135+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Asrc/PartKeepr/FrontendBundle/Resources/public/js/Models/StatisticSample.js | 8++++++++
Msrc/PartKeepr/FrontendBundle/Resources/views/index.html.twig | 1+
Msrc/PartKeepr/StatisticBundle/Controller/StatisticController.php | 32+++++++++++++++++++++++++++++++-
Asrc/PartKeepr/StatisticBundle/Entity/StatisticSnapshot.php | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/PartKeepr/StatisticBundle/Entity/StatisticSnapshotUnit.php | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/PartKeepr/StatisticBundle/Services/StatisticService.php | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 564 insertions(+), 188 deletions(-)

diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Statistics/StatisticsChart.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Statistics/StatisticsChart.js @@ -1,149 +1,169 @@ Ext.define('PartKeepr.StatisticsChart', { - extend: 'Ext.chart.CartesianChart', - animate: true, + extend: 'Ext.chart.CartesianChart', + animate: true, shadow: true, - + style: 'border: 1px solid #AAA;background-color: white;box-shadow: 5px 5px 0px #aaa', legend: { position: 'right' }, theme: 'Base', - series: [{ - type: 'line', - highlight: { - size: 7, - radius: 7 - }, - axis: 'left', - xField: 'start', - yField: 'parts', - tips: { - trackMouse: true, - width: 170, - height: 28, - renderer: function(storeItem, item) { - this.setTitle(Ext.Date.format(storeItem.get('start'), "Y-m-d") + ": " + storeItem.get("parts") +" " + i18n("Parts")); + series: [ + { + type: 'line', + highlight: { + size: 7, + radius: 7 + }, + axis: 'left', + xField: 'start', + yField: 'parts', + style: { + lineWidth: 4 + }, + marker: { + radius: 4 + }, + tooltip: { + trackMouse: true, + renderer: function (tip, item) + { + tip.update( + Ext.Date.format(item.get('start'), "Y-m-d") + ": " + item.get("parts") + " " + i18n( + "Parts")); + } + }, + title: i18n("Parts"), + markerConfig: { + type: 'cross', + size: 4, + radius: 4, + 'stroke-width': 0 + } + }, { + type: 'line', + style: { + lineWidth: 4 + }, + marker: { + radius: 4 + }, + highlight: { + size: 7, + radius: 7 + }, + tooltip: { + trackMouse: true, + renderer: function (tip, item) + { + tip.update(Ext.Date.format(item.get('start'), "Y-m-d") + ": " + item.get( + "categories") + " " + i18n("Categories")); + } + }, + axis: 'left', + title: i18n("Categories"), + smooth: true, + xField: 'start', + yField: 'categories', + markerConfig: { + type: 'circle', + size: 4, + radius: 4, + 'stroke-width': 0 } - }, - title: i18n("Parts"), - markerConfig: { - type: 'cross', - size: 4, - radius: 4, - 'stroke-width': 0 } - }, { - type: 'line', - highlight: { - size: 7, - radius: 7 - }, - tips: { - trackMouse: true, - width: 170, - height: 28, - renderer: function(storeItem, item) { - this.setTitle(Ext.Date.format(storeItem.get('start'), "Y-m-d") + ": " + storeItem.get("categories") +" " + i18n("Categories")); + ], + axes: [ + { + type: 'numeric', + minimum: 0, + position: 'left', + fields: ['parts', 'categories'], + title: i18n("Count"), + minorTickSteps: 1, + grid: { + odd: { + opacity: 1, + fill: '#eee', + stroke: '#bbb', + 'stroke-width': 0.5 + }, + even: { + opacity: 1, + stroke: '#bbb', + 'stroke-width': 0.5 + } } - }, - axis: 'left', - title: i18n("Categories"), - smooth: true, - xField: 'start', - yField: 'categories', - markerConfig: { - type: 'circle', - size: 4, - radius: 4, - 'stroke-width': 0 + }, + { + type: 'time', + dateFormat: 'Y-m-d', + position: 'bottom', + aggregateOp: "avg", + fields: ['start'], + title: i18n("Date"), + grid: true } - }], + ], + store: { + model: 'PartKeepr.StatisticSample', + proxy: { + type: 'ajax', + reader: { + type: 'json', + rootProperty: '' + }, + url: PartKeepr.getBasePath() + "/api/statistics/sampled", + extraParams: { + "start": "2011-01-01 00:00:00", + "end": "2011-12-01 23:59:59" + } + }, + autoLoad: false + }, initComponent: function () { - - /** - * Defines the first axis, which indicates the count. - */ - this.axis1 = { - type: 'Numeric', - minimum: 0, - position: 'left', - fields: ['parts', 'categories'], - title: i18n("Count"), - minorTickSteps: 1, - grid: { - odd: { - opacity: 1, - fill: '#eee', - stroke: '#bbb', - 'stroke-width': 0.5 - }, - even: { - opacity: 1, - stroke: '#bbb', - 'stroke-width': 0.5 - } - } - }; - - /** - * Defines the second axis, which indicates the time. - */ - this.axis2 = { - type: 'Time', - dateFormat: 'Y-m-d', - position: 'bottom', - aggregateOp: "avg", - fields: ['start'], - title: i18n("Date"), - grid: true - }; - - this.axes = [ this.axis1, this.axis2 ]; - - 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 23:59:59" - }, - headers: { - session :PartKeepr.getApplication().getSession() - } - }, - autoLoad: false - }); - - this.callParent(); + this.mask = new Ext.LoadMask({ + msg: i18n("Loading…"), + target: this + }); + this.callParent(); + + this.store.on("beforeload", Ext.bind(this.onBeforeLoad, this)); + this.store.on("load", Ext.bind(this.onLoad, this)); }, /** * Sets the start date for the chart. Does not trigger a reload of the dataset. * @param date A valid date object */ - setStart: function (date) { - if (!(date instanceof Date)) { return; } - this.store.getProxy().extraParams.startDateTime = Ext.Date.format(date, "Y-m-d H:i:s"); + setStart: function (date) + { + if (!(date instanceof Date)) { + return; + } + this.store.getProxy().extraParams.start = Ext.Date.format(date, "Y-m-d H:i:s"); }, /** * Sets the end date for the chart. Does not trigger a reload of the dataset. * @param date A valid date object */ - setEnd: function (date) { - if (!(date instanceof Date)) { return; } - - // Always set the end date to the end of the day - date.setHours(23); - date.setMinutes(59); - date.setSeconds(59); - - this.store.getProxy().extraParams.endDateTime = Ext.Date.format(date, "Y-m-d H:i:s"); + setEnd: function (date) + { + if (!(date instanceof Date)) { + return; + } + + // Always set the end date to the end of the day + date.setHours(23); + date.setMinutes(59); + date.setSeconds(59); + + this.store.getProxy().extraParams.end = Ext.Date.format(date, "Y-m-d H:i:s"); + }, + onBeforeLoad: function () { + this.mask.show(); + }, + onLoad: function () { + this.mask.hide(); + } }); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Statistics/StatisticsChartPanel.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Statistics/StatisticsChartPanel.js @@ -1,73 +1,84 @@ Ext.define('PartKeepr.StatisticsChartPanel', { - extend: 'Ext.form.Panel', + extend: 'Ext.form.Panel', title: i18n("Statistics Chart"), layout: 'anchor', bodyStyle: 'background:#DBDBDB;padding: 15px;', - - initComponent: function () { - this.chart = Ext.create("PartKeepr.StatisticsChart", { anchor: '100% -60' }); - - this.dateSelector1 = Ext.create("Ext.form.field.Date", { - style: 'margin-top: 10px', - fieldLabel: i18n("From"), - listeners: { - change: Ext.bind(this.onDateChanged, this) - } - }); - - this.dateSelector2 = Ext.create("Ext.form.field.Date", { - fieldLabel: i18n("To"), - listeners: { - change: Ext.bind(this.onDateChanged, this) - } - }); - - this.items = [ this.chart, this.dateSelector1, this.dateSelector2 ]; - - this.reloadDates(); - - this.callParent(); + + initComponent: function () + { + this.chart = Ext.create("PartKeepr.StatisticsChart", {anchor: '100% -60'}); + + this.dateSelector1 = Ext.create("Ext.form.field.Date", { + style: 'margin-top: 10px', + fieldLabel: i18n("From"), + listeners: { + change: Ext.bind(this.onDateChanged, this) + } + }); + + this.dateSelector2 = Ext.create("Ext.form.field.Date", { + fieldLabel: i18n("To"), + listeners: { + change: Ext.bind(this.onDateChanged, this) + } + }); + + this.items = [this.chart, this.dateSelector1, this.dateSelector2]; + + this.reloadDates(); + + this.callParent(); }, - onDateChanged: function () { - this.chart.setStart(this.dateSelector1.getValue()); - this.chart.setEnd(this.dateSelector2.getValue()); - this.chart.store.load(); + onDateChanged: function () + { + this.chart.setStart(this.dateSelector1.getValue()); + this.chart.setEnd(this.dateSelector2.getValue()); + this.chart.store.load(); }, - reloadDates: function () { - var call = new PartKeepr.ServiceCall("Statistic", "getStatisticRange"); - call.setHandler(Ext.bind(this.onReloadDates, this)); - call.doCall(); + reloadDates: function () + { + var options = { + url: PartKeepr.getBasePath() + "/api/statistics/range", + method: "GET", + callback: Ext.bind(this.onReloadDates, this) + }; + + Ext.Ajax.request(options); }, - onReloadDates: function (data) { - if (data.data.start === null || data.data.end === null) { - Ext.Msg.alert( - i18n("Unable to retrieve the statistic data"), - i18n("The system was unable to retrieve the statistic data. The most probable cause is that the CreateStatisticSnapshot.php cronjob is not running.")); - return; - } - - var start = Ext.Date.parse(data.data.start, "Y-m-d H:i:s"); - var end = Ext.Date.parse(data.data.end, "Y-m-d H:i:s"); - - this.dateSelector1.setMinValue(start); - this.dateSelector1.setMaxValue(end); - this.dateSelector1.suspendEvents(); - - this.dateSelector1.setValue(start); - this.dateSelector1.resumeEvents(); - - - this.dateSelector2.setMinValue(start); - this.dateSelector2.setMaxValue(end); - - this.dateSelector2.suspendEvents(); - this.dateSelector2.setValue(end); - this.dateSelector2.resumeEvents(); - - this.chart.setStart(start); - this.chart.setEnd(end); - this.chart.store.load(); + onReloadDates: function (options, success, response) + { + var data = Ext.decode(response.responseText); + + if (data.startDate === null || data.endDate === null) { + Ext.Msg.alert( + i18n("Unable to retrieve the statistic data"), + i18n( + "The system was unable to retrieve the statistic data. The most probable cause is that the CreateStatisticSnapshot.php cronjob is not running.")); + return; + } + + var start = Ext.Date.parse(data.startDate, "Y-m-d H:i:s"); + var end = Ext.Date.parse(data.endDate, "Y-m-d H:i:s"); + + this.dateSelector1.setMinValue(start); + this.dateSelector1.setMaxValue(end); + this.dateSelector1.suspendEvents(); + + this.dateSelector1.setValue(start); + this.dateSelector1.resumeEvents(); + + + this.dateSelector2.setMinValue(start); + this.dateSelector2.setMaxValue(end); + + this.dateSelector2.suspendEvents(); + this.dateSelector2.setValue(end); + this.dateSelector2.resumeEvents(); + + this.chart.setStart(start); + this.chart.setEnd(end); + this.chart.store.load(); }, statics: { iconCls: 'web-icon chart_bar', diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Models/StatisticSample.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Models/StatisticSample.js @@ -0,0 +1,8 @@ +Ext.define("PartKeepr.StatisticSample", { + extend: "Ext.data.Model", + fields: [ + { name: 'start', type: 'date', dateFormat: 'Y-m-d H:i:s'}, + { name: 'parts', type: 'int', useNull: true }, + { name: 'categories', type: 'int', useNull: true } + ] +}); diff --git a/src/PartKeepr/FrontendBundle/Resources/views/index.html.twig b/src/PartKeepr/FrontendBundle/Resources/views/index.html.twig @@ -75,6 +75,7 @@ '@PartKeeprFrontendBundle/Resources/public/js/Models/ProjectReport.js' '@PartKeeprFrontendBundle/Resources/public/js/Models/ProjectReportList.js' '@PartKeeprFrontendBundle/Resources/public/js/Models/SystemInformationRecord.js' + '@PartKeeprFrontendBundle/Resources/public/js/Models/StatisticSample.js' '@PartKeeprFrontendBundle/Resources/public/js/Util/Crypto/isaac.js' '@PartKeeprFrontendBundle/Resources/public/js/Util/Crypto/bcrypt.js' '@PartKeeprFrontendBundle/Resources/public/js/Util/Crypto/core.js' diff --git a/src/PartKeepr/StatisticBundle/Controller/StatisticController.php b/src/PartKeepr/StatisticBundle/Controller/StatisticController.php @@ -1,14 +1,16 @@ <?php namespace PartKeepr\StatisticBundle\Controller; +use FOS\RestBundle\Controller\Annotations\QueryParam; use FOS\RestBundle\Controller\Annotations\View; use FOS\RestBundle\Controller\FOSRestController; +use FOS\RestBundle\Request\ParamFetcher; use Sensio\Bundle\FrameworkExtraBundle\Configuration as Routing; class StatisticController extends FOSRestController { /** - * Exports the given data to a given format + * Returns the current system statistics * * @Routing\Route("/api/statistics/current", defaults={"method" = "get","_format" = "json"}) * @View() @@ -31,5 +33,33 @@ class StatisticController extends FOSRestController return $aData; } + /** + * Returns the sampled statistics for a given period + * + * @QueryParam(name="start") + * @QueryParam(name="end") + * @Routing\Route("/api/statistics/sampled", defaults={"method" = "get","_format" = "json"}) + * @View() + * + * @param ParamFetcher $paramFetcher + * @return array + */ + public function getSampledStatisticAction (ParamFetcher $paramFetcher) { + $start = new \DateTime($paramFetcher->get("start")); + $end = new \DateTime($paramFetcher->get("end")); + return $this->get("partkeepr.statistic.service")->getSampledStatistics($start, $end); + } + + /** + * Returns the range in which statistics are available + * + * @Routing\Route("/api/statistics/range", defaults={"method" = "get","_format" = "json"}) + * @View() + * + * @return array + */ + public function getStatisticRangeAction () { + return $this->get("partkeepr.statistic.service")->getStatisticRange(); + } } diff --git a/src/PartKeepr/StatisticBundle/Entity/StatisticSnapshot.php b/src/PartKeepr/StatisticBundle/Entity/StatisticSnapshot.php @@ -0,0 +1,114 @@ +<?php +namespace PartKeepr\StatisticBundle\Entity; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Mapping as Mapping; + + +/** + * @Mapping\Entity() + */ +class StatisticSnapshot { + /** + * @Mapping\Id + * @Mapping\Column(type="integer") + * @Mapping\GeneratedValue(strategy="AUTO") + * @var integer + */ + private $id; + + /** + * Defines the date when this snapshot has been taken + * @Mapping\Column(type="datetime") + * @var \DateTime + */ + private $dateTime; + + /** + * Defines the amount of different parts in the database + * @Mapping\Column(type="integer") + * @var int + */ + private $parts; + + /** + * Defines the amount of categories + * @Mapping\Column(type="integer") + * @var int + */ + private $categories; + + /** + * Holds all defined units in the database + * @Mapping\OneToMany(targetEntity="PartKeepr\StatisticBundle\Entity\StatisticSnapshotUnit",mappedBy="statisticSnapshot",cascade={"persist", "remove"}) + */ + private $units; + + /** + * Creates a new statistic snapshot + */ + public function __construct () { + $this->units = new ArrayCollection(); + $this->setDateTime(new \DateTime()); + } + + /** + * Sets the date+time for the snapshot + * @param \DateTime $dateTime The date+time for the snapshot + */ + public function setDateTime (\DateTime $dateTime) { + $this->dateTime = $dateTime; + } + + /** + * Returns the date+time for the snapshot + * @return \DateTime The date+time for the snapshot + */ + public function getDateTime () { + return $this->dateTime; + } + + /** + * Sets the amount of overall parts for the snapshot + * @param int $parts The amount of parts + */ + public function setParts ($parts) { + $this->parts = $parts; + } + + /** + * Returns the amount of overall parts for the snapshot + * @return int The amount of parts + */ + public function getParts () { + return $this->parts; + } + + /** + * Sets the amount of categories for the snapshot + * @param int $categories The amount of categories + */ + public function setCategories ($categories) { + $this->categories = $categories; + } + + /** + * Returns the amount of categories + * @return int The amount of categories + */ + public function getCategories () { + return $this->categories; + } + + /** + * Returns the ID of this snapshot + * @return int The ID of this snapshot + */ + public function getId () { + return $this->id; + } + + public function getUnits () { + return $this->units; + } +} diff --git a/src/PartKeepr/StatisticBundle/Entity/StatisticSnapshotUnit.php b/src/PartKeepr/StatisticBundle/Entity/StatisticSnapshotUnit.php @@ -0,0 +1,96 @@ +<?php +namespace PartKeepr\StatisticBundle\Entity; + +use Doctrine\ORM\Mapping as Mapping; +use PartKeepr\PartBundle\Entity\PartMeasurementUnit; + +/** + * @Mapping\Entity + */ +class StatisticSnapshotUnit { + /** + * @Mapping\Id + * @Mapping\Column(type="integer") + * @Mapping\GeneratedValue(strategy="AUTO") + * @var integer + */ + private $id; + + /** + * @Mapping\ManyToOne(targetEntity="PartKeepr\StatisticBundle\Entity\StatisticSnapshot") + * The statistic snapshot this entity belongs to + * @var StatisticSnapshot + */ + private $statisticSnapshot; + + /** + * @Mapping\ManyToOne(targetEntity="PartKeepr\PartBundle\Entity\PartMeasurementUnit") + * The statistic snapshot this entity belongs to + * @var StatisticSnapshot + */ + private $partUnit; + + /** + * The stockLevel for the unit + * @Mapping\Column(type="integer") + * @var int + */ + private $stockLevel; + + /** + * Sets the statistic snapshot this entity belongs to + * @param StatisticSnapshot $snapshot The snapshot + */ + public function setStatisticSnapshot (StatisticSnapshot $snapshot) { + $this->statisticSnapshot = $snapshot; + } + + /** + * Returns the snapshot this entity belongs to + * @return StatisticSnapshot The snapshot + */ + public function getStatisticSnapshot () { + return $this->statisticSnapshot; + } + + /** + * + * Sets the part unit for this entity + * @param PartMeasurementUnit $unit The part unit + */ + public function setPartUnit (PartMeasurementUnit $unit) { + $this->partUnit = $unit; + } + + /** + * Returns the part unit for this entity + * @return PartMeasurementUnit The part unit + */ + public function getPartUnit () { + return $this->partUnit; + } + + /** + * Returns the ID of this statistic snapshot unit + * @return int The ID + */ + public function getId () { + return $this->id; + } + + /** + * Sets the stock level for this unit snapshot + * @param int $stockLevel + */ + public function setStockLevel ($stockLevel) { + $this->stockLevel = $stockLevel; + } + + /** + * Returns the stock level for this unit snapshot + * @return int The stock level + */ + public function getStockLevel () { + return $this->stockLevel; + } +} diff --git a/src/PartKeepr/StatisticBundle/Services/StatisticService.php b/src/PartKeepr/StatisticBundle/Services/StatisticService.php @@ -2,6 +2,7 @@ namespace PartKeepr\StatisticBundle\Services; use Doctrine\ORM\EntityManager; +use PartKeepr\PartBundle\Entity\PartMeasurementUnit; class StatisticService { @@ -35,6 +36,7 @@ class StatisticService /** * Returns the part category count + * * @return int */ public function getPartCategoryCount() @@ -70,6 +72,7 @@ class StatisticService /** * Returns the part counts per part unit + * * @return array An array of arrays with the keys "name" and "stockLevel" */ public function getUnitCounts() @@ -79,4 +82,97 @@ class StatisticService return $this->entityManager->createQuery($dql)->getArrayResult(); } + + /** + * Returns the range of all recorded statistic snapshots. + */ + public function getStatisticRange () { + $dql = "SELECT MIN(sts.dateTime) AS startDate, MAX(sts.dateTime) AS endDate FROM PartKeepr\\StatisticBundle\\Entity\\StatisticSnapshot sts"; + $query = $this->entityManager->createQuery($dql); + + return $query->getSingleResult(); + } + + /** + * Gets the sampled statistics + * + * @param \DateTime $startDate + * @param \DateTime $endDate + * @param int $sampleSize + * + * @return array + */ + public function getSampledStatistics(\DateTime $startDate, \DateTime $endDate, $sampleSize = 25) + { + if ($startDate->getTimestamp() > $endDate->getTimestamp()) { + // Swap both times + list($startDate, $endDate) = array($endDate, $startDate); + } + + $intervalSize = intval(($endDate->getTimestamp() - $startDate->getTimestamp()) / $sampleSize); + + $queryStartTime = clone $startDate; + $queryEndTime = clone $startDate; + $queryEndTime->add(new \DateInterval("PT".$intervalSize."S")); + + $partUnitQuery = "SELECT pu FROM PartKeepr\PartBundle\Entity\PartMeasurementUnit pu"; + $query = $this->entityManager->createQuery($partUnitQuery); + + $aPartUnits = $query->getResult(); + + $dql = "SELECT AVG(sts.parts) AS parts, AVG(sts.categories) AS categories FROM PartKeepr\StatisticBundle\Entity\StatisticSnapshot sts WHERE sts.dateTime >= :start AND sts.dateTime <= :end"; + $mainQuery = $this->entityManager->createQuery($dql); + + $dql = "SELECT AVG(stsu.stockLevel) AS stockLevel FROM PartKeepr\StatisticBundle\Entity\StatisticSnapshotUnit stsu JOIN stsu.statisticSnapshot sts WHERE sts.dateTime >= :start AND sts.dateTime <= :end AND stsu.partUnit = :partUnit"; + $subQuery = $this->entityManager->createQuery($dql); + + $aRecords = array(); + + for ($i = 0; $i < $sampleSize; $i++) { + $mainQuery->setParameter("start", $queryStartTime); + $mainQuery->setParameter("end", $queryEndTime); + + $result = $mainQuery->getResult(); + + $record = $result[0]; + + if ($record["parts"] !== null) { + $record["parts"] = floatval($record["parts"]); + } + + if ($record["categories"] !== null) { + $record["categories"] = floatval($record["categories"]); + } + + foreach ($aPartUnits as $partUnit) { + /** + * @var $partUnit PartMeasurementUnit + */ + $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"); + + if ($record["parts"] !== null) { + $aRecords[] = $record; + } + + + $queryStartTime->add(new \DateInterval("PT".$intervalSize."S")); + $queryEndTime->add(new \DateInterval("PT".$intervalSize."S")); + } + + return $aRecords; + } }