Initial Upload
This commit is contained in:
394
lib/Report/TimeConnected.php
Normal file
394
lib/Report/TimeConnected.php
Normal file
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022-2024 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Xibo\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Factory\DisplayFactory;
|
||||
use Xibo\Factory\DisplayGroupFactory;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Service\ReportServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Class TimeConnected
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
class TimeConnected implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait;
|
||||
|
||||
/**
|
||||
* @var DisplayFactory
|
||||
*/
|
||||
private $displayFactory;
|
||||
|
||||
/**
|
||||
* @var DisplayGroupFactory
|
||||
*/
|
||||
private $displayGroupFactory;
|
||||
|
||||
/**
|
||||
* @var ReportServiceInterface
|
||||
*/
|
||||
private $reportService;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->displayFactory = $container->get('displayFactory');
|
||||
$this->displayGroupFactory = $container->get('displayGroupFactory');
|
||||
$this->reportService = $container->get('reportService');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'timeconnected-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'timeconnected-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm()
|
||||
{
|
||||
$groups = [];
|
||||
$displays = [];
|
||||
|
||||
foreach ($this->displayGroupFactory->query(['displayGroup'], ['isDisplaySpecific' => -1]) as $displayGroup) {
|
||||
/* @var \Xibo\Entity\DisplayGroup $displayGroup */
|
||||
|
||||
if ($displayGroup->isDisplaySpecific == 1) {
|
||||
$displays[] = $displayGroup;
|
||||
} else {
|
||||
$groups[] = $displayGroup;
|
||||
}
|
||||
}
|
||||
|
||||
return new ReportForm(
|
||||
'timeconnected-report-form',
|
||||
'timeconnected',
|
||||
'Display',
|
||||
[
|
||||
'displays' => $displays,
|
||||
'displayGroups' => $groups,
|
||||
'fromDateOneDay' => Carbon::now()->subSeconds(86400)->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
],
|
||||
__('Select a type and an item (i.e., layout/media/tag)')
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$data['hiddenFields'] = '{}';
|
||||
$data['reportName'] = 'timeconnected';
|
||||
|
||||
$groups = [];
|
||||
$displays = [];
|
||||
foreach ($this->displayGroupFactory->query(['displayGroup'], ['isDisplaySpecific' => -1]) as $displayGroup) {
|
||||
/* @var \Xibo\Entity\DisplayGroup $displayGroup */
|
||||
|
||||
if ($displayGroup->isDisplaySpecific == 1) {
|
||||
$displays[] = $displayGroup;
|
||||
} else {
|
||||
$groups[] = $displayGroup;
|
||||
}
|
||||
}
|
||||
$data['displays'] = $displays;
|
||||
$data['displayGroups'] = $groups;
|
||||
|
||||
return [
|
||||
'template' => 'timeconnected-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$groupByFilter = $sanitizedParams->getString('groupByFilter');
|
||||
$displayGroupIds = $sanitizedParams->getIntArray('displayGroupIds');
|
||||
$hiddenFields = json_decode($sanitizedParams->getString('hiddenFields'), true);
|
||||
|
||||
$filterCriteria['displayGroupIds'] = $displayGroupIds;
|
||||
$filterCriteria['filter'] = $filter;
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
$filterCriteria['groupByFilter'] = $groupByFilter;
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
$filterCriteria['groupByFilter'] = $groupByFilter;
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
$filterCriteria['groupByFilter'] = $groupByFilter;
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
$filterCriteria['groupByFilter'] = $groupByFilter;
|
||||
}
|
||||
|
||||
$filterCriteria['sendEmail'] = $sanitizedParams->getCheckbox('sendEmail');
|
||||
$filterCriteria['nonusers'] = $sanitizedParams->getString('nonusers');
|
||||
|
||||
// Return
|
||||
return [
|
||||
'filterCriteria' => json_encode($filterCriteria),
|
||||
'schedule' => $schedule
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
return sprintf(__('%s report for Display', ucfirst($sanitizedParams->getString('filter'))));
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function restructureSavedReportOldJson($result)
|
||||
{
|
||||
return $result;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportResults($json, $savedReport)
|
||||
{
|
||||
$metadata = [
|
||||
'periodStart' => $json['metadata']['periodStart'],
|
||||
'periodEnd' => $json['metadata']['periodEnd'],
|
||||
'generatedOn' => Carbon::createFromTimestamp($savedReport->generatedOn)
|
||||
->format(DateFormatHelper::getSystemFormat()),
|
||||
'title' => $savedReport->saveAs,
|
||||
];
|
||||
|
||||
// Report result object
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$json['table'],
|
||||
$json['recordsTotal'],
|
||||
$json['chart']
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getResults(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
// Get an array of display id this user has access to.
|
||||
$displayIds = $this->getDisplayIdFilter($sanitizedParams);
|
||||
|
||||
// From and To Date Selection
|
||||
// --------------------------
|
||||
// The report uses a custom range filter that automatically calculates the from/to dates
|
||||
// depending on the date range selected.
|
||||
$fromDt = $sanitizedParams->getDate('fromDt');
|
||||
$toDt = $sanitizedParams->getDate('toDt');
|
||||
|
||||
// Use the group by filter provided
|
||||
// NB: this differs from the Summary Report where we set the group by according to the range selected
|
||||
$groupByFilter = $sanitizedParams->getString('groupByFilter');
|
||||
|
||||
//
|
||||
// Get Results!
|
||||
// with keys "result", "periods", "periodStart", "periodEnd"
|
||||
// -------------
|
||||
$result = $this->getTimeDisconnectedMySql($fromDt, $toDt, $groupByFilter, $displayIds);
|
||||
|
||||
//
|
||||
// Output Results
|
||||
// --------------
|
||||
if ($this->getUser()->isSuperAdmin()) {
|
||||
$sql = 'SELECT displayId, display FROM display WHERE 1 = 1';
|
||||
if (count($displayIds) > 0) {
|
||||
$sql .= ' AND displayId IN (' . implode(',', $displayIds) . ')';
|
||||
}
|
||||
}
|
||||
|
||||
$timeConnected = [];
|
||||
$displays = [];
|
||||
$i = 0;
|
||||
$key = 0;
|
||||
foreach ($this->store->select($sql, []) as $row) {
|
||||
$displayId = intval($row['displayId']);
|
||||
$displayName = $row['display'];
|
||||
|
||||
// Set the display name for the displays in this row.
|
||||
$displays[$key][$displayId] = $displayName;
|
||||
|
||||
// Go through each period
|
||||
foreach ($result['periods'] as $resPeriods) {
|
||||
//
|
||||
$temp = $resPeriods['customLabel'];
|
||||
if (empty($timeConnected[$temp][$displayId]['percent'])) {
|
||||
$timeConnected[$key][$temp][$displayId]['percent'] = 100;
|
||||
}
|
||||
if (empty($timeConnected[$temp][$displayId]['label'])) {
|
||||
$timeConnected[$key][$temp][$displayId]['label'] = $resPeriods['customLabel'];
|
||||
}
|
||||
|
||||
foreach ($result['result'] as $res) {
|
||||
if ($res['displayId'] == $displayId && $res['customLabel'] == $resPeriods['customLabel']) {
|
||||
$timeConnected[$key][$temp][$displayId]['percent'] = 100 - round($res['percent'], 2);
|
||||
$timeConnected[$key][$temp][$displayId]['label'] = $resPeriods['customLabel'];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$i++;
|
||||
if ($i >= 3) {
|
||||
$i = 0;
|
||||
$key++;
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
||||
// No grid
|
||||
// Return data to build chart/table
|
||||
// This will get saved to a json file when schedule runs
|
||||
return new ReportResult(
|
||||
[
|
||||
'periodStart' => Carbon::createFromTimestamp($fromDt->toDateTime()->format('U'))->format(DateFormatHelper::getSystemFormat()),
|
||||
'periodEnd' => Carbon::createFromTimestamp($toDt->toDateTime()->format('U'))->format(DateFormatHelper::getSystemFormat()),
|
||||
],
|
||||
[
|
||||
'timeConnected' => $timeConnected,
|
||||
'displays' => $displays
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* MySQL distribution report
|
||||
* @param Carbon $fromDt The filter range from date
|
||||
* @param Carbon $toDt The filter range to date
|
||||
* @param string $groupByFilter Grouping, byhour, bydayofweek and bydayofmonth
|
||||
* @param array $displayIds
|
||||
* @return array
|
||||
*/
|
||||
private function getTimeDisconnectedMySql($fromDt, $toDt, $groupByFilter, $displayIds)
|
||||
{
|
||||
|
||||
if ($groupByFilter == 'bydayofmonth') {
|
||||
$customLabel = 'Y-m-d (D)';
|
||||
} else {
|
||||
$customLabel = 'Y-m-d g:i A';
|
||||
}
|
||||
|
||||
// Create periods covering the from/to dates
|
||||
// -----------------------------------------
|
||||
try {
|
||||
$periods = $this->getTemporaryPeriodsTable($fromDt, $toDt, $groupByFilter, 'temp_periods', $customLabel);
|
||||
} catch (InvalidArgumentException $invalidArgumentException) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
$periods2 = $this->getTemporaryPeriodsTable($fromDt, $toDt, $groupByFilter, 'temp_periods2', $customLabel);
|
||||
} catch (InvalidArgumentException $invalidArgumentException) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Join in stats
|
||||
// -------------
|
||||
$query = '
|
||||
SELECT periods.id,
|
||||
periods.start,
|
||||
periods.end,
|
||||
periods.label,
|
||||
periods.customLabel,
|
||||
display,
|
||||
displayId,
|
||||
SUM(duration) AS downtime,
|
||||
periods.end - periods.start AS periodDuration,
|
||||
SUM(duration) / (periods.end - periods.start) * 100 AS percent
|
||||
FROM ' . $periods2 . ' AS periods
|
||||
INNER JOIN (
|
||||
SELECT id,
|
||||
label,
|
||||
customLabel,
|
||||
display,
|
||||
displayId,
|
||||
GREATEST(periods.start, down.start) AS actualStart,
|
||||
LEAST(periods.end, down.end) AS actualEnd,
|
||||
LEAST(periods.end, down.end) - GREATEST(periods.start, down.start) AS duration,
|
||||
periods.end - periods.start AS periodDuration,
|
||||
(LEAST(periods.end, down.end) - GREATEST(periods.start, down.start)) / (periods.end - periods.start) * 100 AS percent
|
||||
FROM ' . $periods . ' AS periods
|
||||
LEFT OUTER JOIN (
|
||||
SELECT start,
|
||||
IFNULL(end, UNIX_TIMESTAMP()) AS end,
|
||||
displayevent.displayId,
|
||||
display.display
|
||||
FROM displayevent
|
||||
INNER JOIN display
|
||||
ON display.displayId = displayevent.displayId
|
||||
WHERE `displayevent`.eventTypeId = 1
|
||||
';
|
||||
|
||||
// Displays
|
||||
if (count($displayIds) > 0) {
|
||||
$query .= ' AND display.displayID IN (' . implode(',', $displayIds) . ') ';
|
||||
}
|
||||
|
||||
$query .= '
|
||||
) down
|
||||
ON down.start < periods.`end`
|
||||
AND down.end > periods.`start`
|
||||
) joined
|
||||
ON joined.customLabel = periods.customLabel
|
||||
GROUP BY periods.id,
|
||||
periods.start,
|
||||
periods.end,
|
||||
periods.label,
|
||||
periods.customLabel,
|
||||
joined.display,
|
||||
joined.displayId
|
||||
ORDER BY id, display
|
||||
';
|
||||
|
||||
return [
|
||||
'result' => $this->getStore()->select($query, []),
|
||||
'periods' => $this->getStore()->select('SELECT * from ' . $periods, []),
|
||||
'periodStart' => $fromDt->format('Y-m-d H:i:s'),
|
||||
'periodEnd' => $toDt->format('Y-m-d H:i:s')
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user