Initial Upload

This commit is contained in:
Matt Batchelder
2025-12-02 10:32:59 -05:00
commit 05ce0da296
2240 changed files with 467811 additions and 0 deletions

View 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')
];
}
}