Initial Upload
This commit is contained in:
457
lib/Report/ApiRequests.php
Normal file
457
lib/Report/ApiRequests.php
Normal file
@@ -0,0 +1,457 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 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\Controller\DataTablesDotNetTrait;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Factory\ApplicationRequestsFactory;
|
||||
use Xibo\Factory\AuditLogFactory;
|
||||
use Xibo\Factory\LogFactory;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Support\Exception\AccessDeniedException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
class ApiRequests implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait, DataTablesDotNetTrait;
|
||||
|
||||
/** @var LogFactory */
|
||||
private $logFactory;
|
||||
|
||||
/** @var AuditLogFactory */
|
||||
private $auditLogFactory;
|
||||
|
||||
/** @var ApplicationRequestsFactory */
|
||||
private $apiRequestsFactory;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->logFactory = $container->get('logFactory');
|
||||
$this->auditLogFactory = $container->get('auditLogFactory');
|
||||
$this->apiRequestsFactory = $container->get('apiRequestsFactory');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'apirequests-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'apirequests-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm(): ReportForm
|
||||
{
|
||||
return new ReportForm(
|
||||
'apirequests-report-form',
|
||||
'apirequests',
|
||||
'Audit',
|
||||
[
|
||||
'fromDate' => Carbon::now()->startOfMonth()->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams): array
|
||||
{
|
||||
$data = [];
|
||||
$data['reportName'] = 'apirequests';
|
||||
|
||||
return [
|
||||
'template' => 'apirequests-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams): array
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$filterCriteria['userId'] = $sanitizedParams->getInt('userId');
|
||||
$filterCriteria['type'] = $sanitizedParams->getString('type');
|
||||
$filterCriteria['scheduledReport'] = true;
|
||||
|
||||
$filterCriteria['filter'] = $filter;
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
}
|
||||
|
||||
$filterCriteria['sendEmail'] = $sanitizedParams->getCheckbox('sendEmail');
|
||||
$filterCriteria['nonusers'] = $sanitizedParams->getString('nonusers');
|
||||
|
||||
// Return
|
||||
return [
|
||||
'filterCriteria' => json_encode($filterCriteria),
|
||||
'schedule' => $schedule
|
||||
];
|
||||
}
|
||||
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams): string
|
||||
{
|
||||
return sprintf(
|
||||
__('%s API requests %s log report for User'),
|
||||
ucfirst($sanitizedParams->getString('filter')),
|
||||
ucfirst($sanitizedParams->getString('type'))
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function restructureSavedReportOldJson($json)
|
||||
{
|
||||
return $json;
|
||||
}
|
||||
|
||||
/** @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,
|
||||
'logType' => $json['metadata']['logType']
|
||||
];
|
||||
|
||||
// Report result object
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$json['table'],
|
||||
$json['recordsTotal'],
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getResults(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
if (!$this->getUser()->isSuperAdmin()) {
|
||||
throw new AccessDeniedException();
|
||||
}
|
||||
|
||||
//
|
||||
// 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.
|
||||
$reportFilter = $sanitizedParams->getString('reportFilter');
|
||||
|
||||
// Use the current date as a helper
|
||||
$now = Carbon::now();
|
||||
|
||||
// This calculation will be retained as it is used for scheduled reports
|
||||
switch ($reportFilter) {
|
||||
case 'yesterday':
|
||||
$fromDt = $now->copy()->startOfDay()->subDay();
|
||||
$toDt = $now->copy()->startOfDay();
|
||||
break;
|
||||
|
||||
case 'lastweek':
|
||||
$fromDt = $now->copy()->locale(Translate::GetLocale())->startOfWeek()->subWeek();
|
||||
$toDt = $fromDt->copy()->addWeek();
|
||||
break;
|
||||
|
||||
case 'lastmonth':
|
||||
$fromDt = $now->copy()->startOfMonth()->subMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
break;
|
||||
|
||||
case 'lastyear':
|
||||
$fromDt = $now->copy()->startOfYear()->subYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
break;
|
||||
|
||||
case '':
|
||||
default:
|
||||
// fromDt will always be from start of day ie 00:00
|
||||
$fromDt = $sanitizedParams->getDate('fromDt') ?? $now->copy()->startOfDay();
|
||||
$toDt = $sanitizedParams->getDate('toDt') ?? $now;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$type = $sanitizedParams->getString('type');
|
||||
|
||||
$metadata = [
|
||||
'periodStart' => Carbon::createFromTimestamp($fromDt->toDateTime()->format('U'))
|
||||
->format(DateFormatHelper::getSystemFormat()),
|
||||
'periodEnd' => Carbon::createFromTimestamp($toDt->toDateTime()->format('U'))
|
||||
->format(DateFormatHelper::getSystemFormat()),
|
||||
'logType' => $type,
|
||||
];
|
||||
|
||||
if ($type === 'audit') {
|
||||
$params = [
|
||||
'fromDt' => $fromDt->format('U'),
|
||||
'toDt' => $toDt->format('U'),
|
||||
];
|
||||
|
||||
$sql = 'SELECT
|
||||
`auditlog`.`logId`,
|
||||
`auditlog`.`logDate`,
|
||||
`user`.`userName`,
|
||||
`auditlog`.`message`,
|
||||
`auditlog`.`objectAfter`,
|
||||
`auditlog`.`entity`,
|
||||
`auditlog`.`entityId`,
|
||||
`auditlog`.userId,
|
||||
`auditlog`.ipAddress,
|
||||
`auditlog`.requestId,
|
||||
`application_requests_history`.applicationId,
|
||||
`application_requests_history`.url,
|
||||
`application_requests_history`.method,
|
||||
`application_requests_history`.startTime,
|
||||
`oauth_clients`.name AS applicationName
|
||||
FROM `auditlog`
|
||||
INNER JOIN `user`
|
||||
ON `user`.`userId` = `auditlog`.`userId`
|
||||
INNER JOIN `application_requests_history`
|
||||
ON `application_requests_history`.`requestId` = `auditlog`.`requestId`
|
||||
INNER JOIN `oauth_clients`
|
||||
ON `oauth_clients`.id = `application_requests_history`.applicationId
|
||||
WHERE `auditlog`.logDate BETWEEN :fromDt AND :toDt
|
||||
';
|
||||
|
||||
if ($sanitizedParams->getInt('userId') !== null) {
|
||||
$sql .= ' AND `auditlog`.`userId` = :userId';
|
||||
$params['userId'] = $sanitizedParams->getInt('userId');
|
||||
}
|
||||
|
||||
if ($sanitizedParams->getInt('requestId') !== null) {
|
||||
$sql .= ' AND `auditlog`.`requestId` = :requestId';
|
||||
$params['requestId'] = $sanitizedParams->getInt('requestId');
|
||||
}
|
||||
|
||||
// Sorting?
|
||||
$sortOrder = $this->gridRenderSort($sanitizedParams);
|
||||
|
||||
if (is_array($sortOrder)) {
|
||||
$sql .= ' ORDER BY ' . implode(',', $sortOrder);
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$auditRecord = $this->auditLogFactory->create()->hydrate($row);
|
||||
$auditRecord->setUnmatchedProperty(
|
||||
'applicationId',
|
||||
$row['applicationId']
|
||||
);
|
||||
|
||||
$auditRecord->setUnmatchedProperty(
|
||||
'applicationName',
|
||||
$row['applicationName']
|
||||
);
|
||||
|
||||
$auditRecord->setUnmatchedProperty(
|
||||
'url',
|
||||
$row['url']
|
||||
);
|
||||
|
||||
$auditRecord->setUnmatchedProperty(
|
||||
'method',
|
||||
$row['method']
|
||||
);
|
||||
|
||||
// decode for grid view, leave as json for email/preview.
|
||||
if (!$sanitizedParams->getCheckbox('scheduledReport')) {
|
||||
$auditRecord->objectAfter = json_decode($auditRecord->objectAfter);
|
||||
}
|
||||
|
||||
$auditRecord->logDate = Carbon::createFromTimestamp($auditRecord->logDate)
|
||||
->format(DateFormatHelper::getSystemFormat());
|
||||
|
||||
$rows[] = $auditRecord;
|
||||
}
|
||||
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
count($rows),
|
||||
);
|
||||
} else if ($type === 'debug') {
|
||||
$params = [
|
||||
'fromDt' => $fromDt->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDt' => $toDt->format(DateFormatHelper::getSystemFormat()),
|
||||
];
|
||||
|
||||
$sql = 'SELECT
|
||||
`log`.`logId`,
|
||||
`log`.`logDate`,
|
||||
`log`.`runNo`,
|
||||
`log`.`channel`,
|
||||
`log`.`page`,
|
||||
`log`.`function`,
|
||||
`log`.`type`,
|
||||
`log`.`message`,
|
||||
`log`.`userId`,
|
||||
`log`.`requestId`,
|
||||
`user`.`userName`,
|
||||
`application_requests_history`.applicationId,
|
||||
`application_requests_history`.url,
|
||||
`application_requests_history`.method,
|
||||
`application_requests_history`.startTime,
|
||||
`oauth_clients`.name AS applicationName
|
||||
FROM `log`
|
||||
INNER JOIN `user`
|
||||
ON `user`.`userId` = `log`.`userId`
|
||||
INNER JOIN `application_requests_history`
|
||||
ON `application_requests_history`.`requestId` = `log`.`requestId`
|
||||
INNER JOIN `oauth_clients`
|
||||
ON `oauth_clients`.id = `application_requests_history`.applicationId
|
||||
WHERE `log`.logDate BETWEEN :fromDt AND :toDt
|
||||
';
|
||||
|
||||
if ($sanitizedParams->getInt('userId') !== null) {
|
||||
$sql .= ' AND `log`.`userId` = :userId';
|
||||
$params['userId'] = $sanitizedParams->getInt('userId');
|
||||
}
|
||||
|
||||
if ($sanitizedParams->getInt('requestId') !== null) {
|
||||
$sql .= ' AND `log`.`requestId` = :requestId';
|
||||
$params['requestId'] = $sanitizedParams->getInt('requestId');
|
||||
}
|
||||
|
||||
// Sorting?
|
||||
$sortOrder = $this->gridRenderSort($sanitizedParams);
|
||||
|
||||
if (is_array($sortOrder)) {
|
||||
$sql .= ' ORDER BY ' . implode(',', $sortOrder);
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$logRecord = $this->logFactory->createEmpty()->hydrate($row, ['htmlStringProperties' => ['message']]);
|
||||
$logRecord->setUnmatchedProperty(
|
||||
'applicationId',
|
||||
$row['applicationId']
|
||||
);
|
||||
|
||||
$logRecord->setUnmatchedProperty(
|
||||
'applicationName',
|
||||
$row['applicationName']
|
||||
);
|
||||
|
||||
$logRecord->setUnmatchedProperty(
|
||||
'url',
|
||||
$row['url']
|
||||
);
|
||||
|
||||
$logRecord->setUnmatchedProperty(
|
||||
'method',
|
||||
$row['method']
|
||||
);
|
||||
|
||||
$rows[] = $logRecord;
|
||||
}
|
||||
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
count($rows),
|
||||
);
|
||||
} else {
|
||||
$params = [
|
||||
'fromDt' => $fromDt->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDt' => $toDt->format(DateFormatHelper::getSystemFormat()),
|
||||
];
|
||||
|
||||
$sql = 'SELECT
|
||||
`application_requests_history`.applicationId,
|
||||
`application_requests_history`.requestId,
|
||||
`application_requests_history`.userId,
|
||||
`application_requests_history`.url,
|
||||
`application_requests_history`.method,
|
||||
`application_requests_history`.startTime,
|
||||
`oauth_clients`.name AS applicationName,
|
||||
`user`.`userName`
|
||||
FROM `application_requests_history`
|
||||
INNER JOIN `user`
|
||||
ON `user`.`userId` = `application_requests_history`.`userId`
|
||||
INNER JOIN `oauth_clients`
|
||||
ON `oauth_clients`.id = `application_requests_history`.applicationId
|
||||
WHERE `application_requests_history`.startTime BETWEEN :fromDt AND :toDt
|
||||
';
|
||||
|
||||
if ($sanitizedParams->getInt('userId') !== null) {
|
||||
$sql .= ' AND `application_requests_history`.`userId` = :userId';
|
||||
$params['userId'] = $sanitizedParams->getInt('userId');
|
||||
}
|
||||
|
||||
// Sorting?
|
||||
$sortOrder = $this->gridRenderSort($sanitizedParams);
|
||||
|
||||
if (is_array($sortOrder)) {
|
||||
$sql .= ' ORDER BY ' . implode(',', $sortOrder);
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$apiRequestRecord = $this->apiRequestsFactory->createEmpty()->hydrate($row);
|
||||
|
||||
$apiRequestRecord->setUnmatchedProperty(
|
||||
'userName',
|
||||
$row['userName']
|
||||
);
|
||||
|
||||
$apiRequestRecord->setUnmatchedProperty(
|
||||
'applicationName',
|
||||
$row['applicationName']
|
||||
);
|
||||
|
||||
$rows[] = $apiRequestRecord;
|
||||
}
|
||||
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
count($rows),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
365
lib/Report/Bandwidth.php
Normal file
365
lib/Report/Bandwidth.php
Normal file
@@ -0,0 +1,365 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 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\Helper\DateFormatHelper;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Class Bandwidth
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
class Bandwidth implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait;
|
||||
|
||||
/**
|
||||
* @var DisplayFactory
|
||||
*/
|
||||
private $displayFactory;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->displayFactory = $container->get('displayFactory');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportChartScript($results)
|
||||
{
|
||||
return json_encode($results->chart);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'bandwidth-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'bandwidth-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm()
|
||||
{
|
||||
return new ReportForm(
|
||||
'bandwidth-report-form',
|
||||
'bandwidth',
|
||||
'Display',
|
||||
[
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$data = [];
|
||||
$data['reportName'] = 'bandwidth';
|
||||
|
||||
return [
|
||||
'template' => 'bandwidth-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$displayId = $sanitizedParams->getInt('displayId');
|
||||
|
||||
$filterCriteria['displayId'] = $displayId;
|
||||
$filterCriteria['filter'] = $filter;
|
||||
|
||||
// Bandwidth report does not support weekly as bandwidth has monthly records in DB
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
}
|
||||
|
||||
$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 bandwidth report', 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)
|
||||
{
|
||||
//
|
||||
// From and To Date Selection
|
||||
// --------------------------
|
||||
// Our report has a range filter which determins whether or not the user has to enter their own from / to dates
|
||||
// check the range filter first and set from/to dates accordingly.
|
||||
$reportFilter = $sanitizedParams->getString('reportFilter');
|
||||
|
||||
// Use the current date as a helper
|
||||
$now = Carbon::now();
|
||||
|
||||
// Bandwidth report does not support weekly as bandwidth has monthly records in DB
|
||||
switch ($reportFilter) {
|
||||
// Daily report if setup which has reportfilter = yesterday will be daily progression of bandwidth usage
|
||||
// It always starts from the start of the month so we get the month usage
|
||||
case 'yesterday':
|
||||
$fromDt = $now->copy()->startOfDay()->subDay();
|
||||
$fromDt->startOfMonth();
|
||||
|
||||
$toDt = $now->copy()->startOfDay();
|
||||
|
||||
break;
|
||||
|
||||
case 'lastmonth':
|
||||
$fromDt = $now->copy()->startOfMonth()->subMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
|
||||
break;
|
||||
|
||||
case 'lastyear':
|
||||
$fromDt = $now->copy()->startOfYear()->subYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
|
||||
break;
|
||||
|
||||
case '':
|
||||
default:
|
||||
// Expect dates to be provided.
|
||||
$fromDt = $sanitizedParams->getDate('fromDt', ['default' => $sanitizedParams->getDate('bandwidthFromDt')]);
|
||||
$fromDt->startOfMonth();
|
||||
|
||||
$toDt = $sanitizedParams->getDate('toDt', ['default' => $sanitizedParams->getDate('bandwidthToDt')]);
|
||||
$toDt->addMonth();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Get an array of display id this user has access to.
|
||||
$displayIds = $this->getDisplayIdFilter($sanitizedParams);
|
||||
|
||||
// Get some data for a bandwidth chart
|
||||
$dbh = $this->store->getConnection();
|
||||
|
||||
$displayId = $sanitizedParams->getInt('displayId');
|
||||
|
||||
$params = [
|
||||
'month' => $fromDt->copy()->format('U'),
|
||||
'month2' => $toDt->copy()->format('U')
|
||||
];
|
||||
|
||||
$SQL = 'SELECT display.display, IFNULL(SUM(Size), 0) AS size ';
|
||||
|
||||
if ($displayId != 0) {
|
||||
$SQL .= ', bandwidthtype.name AS type ';
|
||||
}
|
||||
|
||||
// For user with limited access, return only data for displays this user has permissions to.
|
||||
$joinType = ($this->getUser()->isSuperAdmin()) ? 'LEFT OUTER JOIN' : 'INNER JOIN';
|
||||
|
||||
$SQL .= ' FROM `bandwidth` ' .
|
||||
$joinType . ' `display`
|
||||
ON display.displayid = bandwidth.displayid ';
|
||||
|
||||
|
||||
// Displays
|
||||
if (count($displayIds) > 0) {
|
||||
$SQL .= ' AND display.displayId IN (' . implode(',', $displayIds) . ') ';
|
||||
}
|
||||
|
||||
if ($displayId != 0) {
|
||||
$SQL .= '
|
||||
INNER JOIN bandwidthtype
|
||||
ON bandwidthtype.bandwidthtypeid = bandwidth.type
|
||||
';
|
||||
}
|
||||
|
||||
$SQL .= ' WHERE month > :month
|
||||
AND month < :month2 ';
|
||||
|
||||
if ($displayId != 0) {
|
||||
$SQL .= ' AND display.displayid = :displayid ';
|
||||
$params['displayid'] = $displayId;
|
||||
}
|
||||
|
||||
$SQL .= 'GROUP BY display.displayId, display.display ';
|
||||
|
||||
if ($displayId != 0) {
|
||||
$SQL .= ' , bandwidthtype.name ';
|
||||
}
|
||||
|
||||
$SQL .= 'ORDER BY display.display';
|
||||
|
||||
$sth = $dbh->prepare($SQL);
|
||||
|
||||
$sth->execute($params);
|
||||
|
||||
// Get the results
|
||||
$results = $sth->fetchAll();
|
||||
|
||||
$maxSize = 0;
|
||||
foreach ($results as $library) {
|
||||
$maxSize = ($library['size'] > $maxSize) ? $library['size'] : $maxSize;
|
||||
}
|
||||
|
||||
// Decide what our units are going to be, based on the size
|
||||
// We need to put a fallback value in case it returns an infinite value
|
||||
$base = !is_infinite(floor(log($maxSize) / log(1024))) ? floor(log($maxSize) / log(1024)) : 0;
|
||||
|
||||
$labels = [];
|
||||
$data = [];
|
||||
$backgroundColor = [];
|
||||
|
||||
$rows = [];
|
||||
|
||||
|
||||
// Set up some suffixes
|
||||
$suffixes = array('bytes', 'k', 'M', 'G', 'T');
|
||||
foreach ($results as $row) {
|
||||
// label depends whether we are filtered by display
|
||||
if ($displayId != 0) {
|
||||
$label = $row['type'];
|
||||
$labels[] = $label;
|
||||
} else {
|
||||
$label = $row['display'] === null ? __('Deleted Displays') : $row['display'];
|
||||
$labels[] = $label;
|
||||
}
|
||||
$backgroundColor[] = ($row['display'] === null) ? 'rgb(255,0,0)' : 'rgb(11, 98, 164)';
|
||||
$bandwidth = round((double)$row['size'] / (pow(1024, $base)), 2);
|
||||
$data[] = $bandwidth;
|
||||
|
||||
// ----
|
||||
// Build Tabular data
|
||||
$entry = [];
|
||||
$entry['label'] = $label;
|
||||
$entry['bandwidth'] = $bandwidth;
|
||||
$entry['unit'] = (isset($suffixes[$base]) ? $suffixes[$base] : '');
|
||||
$rows[] = $entry;
|
||||
}
|
||||
|
||||
//
|
||||
// Output Results
|
||||
// --------------
|
||||
$chart = [
|
||||
'type' => 'bar',
|
||||
'data' => [
|
||||
'labels' => $labels,
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => __('Bandwidth'),
|
||||
'backgroundColor' => $backgroundColor,
|
||||
'data' => $data
|
||||
]
|
||||
]
|
||||
],
|
||||
'options' => [
|
||||
'scales' => [
|
||||
'yAxes' => [
|
||||
[
|
||||
'scaleLabel' => [
|
||||
'display' => true,
|
||||
'labelString' => (isset($suffixes[$base]) ? $suffixes[$base] : '')
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'legend' => [
|
||||
'display' => false
|
||||
],
|
||||
'maintainAspectRatio' => true
|
||||
]
|
||||
];
|
||||
|
||||
$metadata = [
|
||||
'periodStart' => $fromDt->format(DateFormatHelper::getSystemFormat()),
|
||||
'periodEnd' => $toDt->format(DateFormatHelper::getSystemFormat()),
|
||||
];
|
||||
|
||||
// Total records
|
||||
$recordsTotal = count($rows);
|
||||
|
||||
// ----
|
||||
// Chart Only
|
||||
// Return data to build chart/table
|
||||
// This will get saved to a json file when schedule runs
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
$recordsTotal,
|
||||
$chart
|
||||
);
|
||||
}
|
||||
}
|
||||
385
lib/Report/CampaignProofOfPlay.php
Normal file
385
lib/Report/CampaignProofOfPlay.php
Normal file
@@ -0,0 +1,385 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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 MongoDB\BSON\UTCDateTime;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Xibo\Controller\DataTablesDotNetTrait;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Event\ReportDataEvent;
|
||||
use Xibo\Factory\CampaignFactory;
|
||||
use Xibo\Factory\DisplayFactory;
|
||||
use Xibo\Factory\LayoutFactory;
|
||||
use Xibo\Factory\MediaFactory;
|
||||
use Xibo\Factory\ReportScheduleFactory;
|
||||
use Xibo\Helper\ApplicationState;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\SanitizerService;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Support\Exception\GeneralException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Class CampaignProofOfPlay
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
class CampaignProofOfPlay implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait, DataTablesDotNetTrait;
|
||||
|
||||
/**
|
||||
* @var CampaignFactory
|
||||
*/
|
||||
private $campaignFactory;
|
||||
|
||||
/**
|
||||
* @var DisplayFactory
|
||||
*/
|
||||
private $displayFactory;
|
||||
|
||||
/**
|
||||
* @var LayoutFactory
|
||||
*/
|
||||
private $layoutFactory;
|
||||
|
||||
/**
|
||||
* @var ReportScheduleFactory
|
||||
*/
|
||||
private $reportScheduleFactory;
|
||||
|
||||
/**
|
||||
* @var SanitizerService
|
||||
*/
|
||||
private $sanitizer;
|
||||
|
||||
/**
|
||||
* @var EventDispatcher
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var ApplicationState
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->campaignFactory = $container->get('campaignFactory');
|
||||
$this->displayFactory = $container->get('displayFactory');
|
||||
$this->reportScheduleFactory = $container->get('reportScheduleFactory');
|
||||
$this->sanitizer = $container->get('sanitizerService');
|
||||
$this->dispatcher = $container->get('dispatcher');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'campaign-proofofplay-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'campaign-proofofplay-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm()
|
||||
{
|
||||
return new ReportForm(
|
||||
'campaign-proofofplay-report-form',
|
||||
'campaignProofOfPlay',
|
||||
'Connector Reports',
|
||||
[
|
||||
'fromDateOneDay' => Carbon::now()->subSeconds(86400)->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat())
|
||||
],
|
||||
__('Select a display')
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$data['hiddenFields'] = '';
|
||||
$data['reportName'] = 'campaignProofOfPlay';
|
||||
|
||||
return [
|
||||
'template' => 'campaign-proofofplay-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$filterCriteria = [
|
||||
'filter' => $filter,
|
||||
'displayId' => $sanitizedParams->getInt('displayId'),
|
||||
'displayIds' => $sanitizedParams->getIntArray('displayIds'),
|
||||
];
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
}
|
||||
|
||||
$filterCriteria['sendEmail'] = $sanitizedParams->getCheckbox('sendEmail');
|
||||
$filterCriteria['nonusers'] = $sanitizedParams->getString('nonusers');
|
||||
|
||||
// Return
|
||||
return [
|
||||
'filterCriteria' => json_encode($filterCriteria),
|
||||
'schedule' => $schedule
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$saveAs = sprintf(__('%s report for ', ucfirst($sanitizedParams->getString('filter'))));
|
||||
|
||||
$displayId = $sanitizedParams->getInt('displayId');
|
||||
if (!empty($displayId)) {
|
||||
// Get display
|
||||
try {
|
||||
$displayName = $this->displayFactory->getById($displayId)->display;
|
||||
$saveAs .= '(Display: '. $displayName . ')';
|
||||
} catch (NotFoundException $error) {
|
||||
$saveAs .= '(DisplayId: Not Found )';
|
||||
}
|
||||
}
|
||||
|
||||
return $saveAs;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function restructureSavedReportOldJson($result)
|
||||
{
|
||||
return [
|
||||
'periodStart' => $result['periodStart'],
|
||||
'periodEnd' => $result['periodEnd'],
|
||||
'table' => $result['result'],
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportResults($json, $savedReport)
|
||||
{
|
||||
// Get filter criteria
|
||||
$rs = $this->reportScheduleFactory->getById($savedReport->reportScheduleId, 1)->filterCriteria;
|
||||
$filterCriteria = json_decode($rs, true);
|
||||
|
||||
// Show filter criteria
|
||||
$metadata = [];
|
||||
|
||||
// Get Meta data
|
||||
$metadata['periodStart'] = $json['metadata']['periodStart'];
|
||||
$metadata['periodEnd'] = $json['metadata']['periodEnd'];
|
||||
$metadata['generatedOn'] = Carbon::createFromTimestamp($savedReport->generatedOn)
|
||||
->format(DateFormatHelper::getSystemFormat());
|
||||
$metadata['title'] = $savedReport->saveAs;
|
||||
|
||||
// Report result object
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$json['table'],
|
||||
$json['recordsTotal']
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getResults(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$parentCampaignId = $sanitizedParams->getInt('parentCampaignId');
|
||||
|
||||
// Get campaign
|
||||
if (!empty($parentCampaignId)) {
|
||||
$campaign = $this->campaignFactory->getById($parentCampaignId);
|
||||
}
|
||||
|
||||
// Display filter.
|
||||
try {
|
||||
// Get an array of display id this user has access to.
|
||||
$displayIds = $this->getDisplayIdFilter($sanitizedParams);
|
||||
} catch (GeneralException $exception) {
|
||||
// stop the query
|
||||
return new ReportResult();
|
||||
}
|
||||
|
||||
//
|
||||
// From and To Date Selection
|
||||
// --------------------------
|
||||
// Our report has a range filter which determines whether the user has to enter their own from / to dates
|
||||
// check the range filter first and set from/to dates accordingly.
|
||||
$reportFilter = $sanitizedParams->getString('reportFilter');
|
||||
|
||||
// Use the current date as a helper
|
||||
$now = Carbon::now();
|
||||
|
||||
switch ($reportFilter) {
|
||||
case 'today':
|
||||
$fromDt = $now->copy()->startOfDay();
|
||||
$toDt = $fromDt->copy()->addDay();
|
||||
break;
|
||||
|
||||
case 'yesterday':
|
||||
$fromDt = $now->copy()->startOfDay()->subDay();
|
||||
$toDt = $now->copy()->startOfDay();
|
||||
break;
|
||||
|
||||
case 'thisweek':
|
||||
$fromDt = $now->copy()->locale(Translate::GetLocale())->startOfWeek();
|
||||
$toDt = $fromDt->copy()->addWeek();
|
||||
break;
|
||||
|
||||
case 'thismonth':
|
||||
$fromDt = $now->copy()->startOfMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
break;
|
||||
|
||||
case 'thisyear':
|
||||
$fromDt = $now->copy()->startOfYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
break;
|
||||
|
||||
case 'lastweek':
|
||||
$fromDt = $now->copy()->locale(Translate::GetLocale())->startOfWeek()->subWeek();
|
||||
$toDt = $fromDt->copy()->addWeek();
|
||||
break;
|
||||
|
||||
case 'lastmonth':
|
||||
$fromDt = $now->copy()->startOfMonth()->subMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
break;
|
||||
|
||||
case 'lastyear':
|
||||
$fromDt = $now->copy()->startOfYear()->subYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
break;
|
||||
|
||||
case '':
|
||||
default:
|
||||
// Expect dates to be provided.
|
||||
$fromDt = $sanitizedParams->getDate('statsFromDt', ['default' => Carbon::now()->subDay()]);
|
||||
$fromDt->startOfDay();
|
||||
|
||||
$toDt = $sanitizedParams->getDate('statsToDt', ['default' => Carbon::now()]);
|
||||
$toDt->endOfDay();
|
||||
|
||||
// What if the fromdt and todt are exactly the same?
|
||||
// in this case assume an entire day from midnight on the fromdt to midnight on the todt (i.e. add a day to the todt)
|
||||
if ($fromDt == $toDt) {
|
||||
$toDt->addDay();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$params = [
|
||||
'campaignId' => $parentCampaignId,
|
||||
'displayIds' => $displayIds,
|
||||
'groupBy' => $sanitizedParams->getString('groupBy')
|
||||
];
|
||||
|
||||
// when the reportfilter is wholecampaign take campaign start/end as form/to date
|
||||
if (!empty($parentCampaignId) && $sanitizedParams->getString('reportFilter') === 'wholecampaign') {
|
||||
$params['fromDt'] = !empty($campaign->getStartDt()) ? $campaign->getStartDt()->format('Y-m-d H:i:s') : null;
|
||||
$params['toDt'] = !empty($campaign->getEndDt()) ? $campaign->getEndDt()->format('Y-m-d H:i:s') : null;
|
||||
|
||||
if (empty($campaign->getStartDt()) || empty($campaign->getEndDt())) {
|
||||
return new ReportResult();
|
||||
}
|
||||
} else {
|
||||
$params['fromDt'] = $fromDt->format('Y-m-d H:i:s');
|
||||
$params['toDt'] = $toDt->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
// --------
|
||||
// ReportDataEvent
|
||||
$event = new ReportDataEvent('campaignProofofplay');
|
||||
|
||||
// Set query params for audience proof of play report
|
||||
$event->setParams($params);
|
||||
|
||||
// Dispatch the event - listened by Audience Report Connector
|
||||
$this->dispatcher->dispatch($event, ReportDataEvent::$NAME);
|
||||
$results = $event->getResults();
|
||||
|
||||
$result['periodStart'] = $params['fromDt'];
|
||||
$result['periodEnd'] = $params['toDt'];
|
||||
|
||||
// Sanitize results??
|
||||
$rows = [];
|
||||
foreach ($results['json'] as $row) {
|
||||
$entry = [];
|
||||
|
||||
$entry['labelDate'] = $row['labelDate'];
|
||||
$entry['adPlays'] = $row['adPlays'];
|
||||
$entry['adDuration'] = $row['adDuration'];
|
||||
$entry['impressions'] = $row['impressions'];
|
||||
$entry['spend'] = $row['spend'];
|
||||
|
||||
$rows[] = $entry;
|
||||
}
|
||||
|
||||
// Set Meta data
|
||||
$metadata = [
|
||||
'periodStart' => $result['periodStart'],
|
||||
'periodEnd' => $result['periodEnd'],
|
||||
];
|
||||
|
||||
$recordsTotal = count($rows);
|
||||
|
||||
// ----
|
||||
// Table Only
|
||||
// Return data to build chart/table
|
||||
// This will get saved to a json file when schedule runs
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
$recordsTotal,
|
||||
[],
|
||||
$results['error'] ?? null
|
||||
);
|
||||
}
|
||||
}
|
||||
36
lib/Report/DefaultReportEmailTrait.php
Normal file
36
lib/Report/DefaultReportEmailTrait.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2021 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Trait DefaultReportEmailTrait
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
trait DefaultReportEmailTrait
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'default-email-template.twig';
|
||||
}
|
||||
}
|
||||
492
lib/Report/DisplayAdPlay.php
Normal file
492
lib/Report/DisplayAdPlay.php
Normal file
@@ -0,0 +1,492 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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 MongoDB\BSON\UTCDateTime;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Xibo\Controller\DataTablesDotNetTrait;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Event\ReportDataEvent;
|
||||
use Xibo\Factory\CampaignFactory;
|
||||
use Xibo\Factory\DisplayFactory;
|
||||
use Xibo\Factory\LayoutFactory;
|
||||
use Xibo\Factory\ReportScheduleFactory;
|
||||
use Xibo\Helper\ApplicationState;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\SanitizerService;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Support\Exception\GeneralException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Class DisplayAdPlay
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
class DisplayAdPlay implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait, DataTablesDotNetTrait;
|
||||
|
||||
/**
|
||||
* @var CampaignFactory
|
||||
*/
|
||||
private $campaignFactory;
|
||||
|
||||
/**
|
||||
* @var DisplayFactory
|
||||
*/
|
||||
private $displayFactory;
|
||||
|
||||
/**
|
||||
* @var LayoutFactory
|
||||
*/
|
||||
private $layoutFactory;
|
||||
|
||||
/**
|
||||
* @var ReportScheduleFactory
|
||||
*/
|
||||
private $reportScheduleFactory;
|
||||
|
||||
/**
|
||||
* @var SanitizerService
|
||||
*/
|
||||
private $sanitizer;
|
||||
|
||||
/**
|
||||
* @var EventDispatcher
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var ApplicationState
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->campaignFactory = $container->get('campaignFactory');
|
||||
$this->displayFactory = $container->get('displayFactory');
|
||||
$this->reportScheduleFactory = $container->get('reportScheduleFactory');
|
||||
$this->sanitizer = $container->get('sanitizerService');
|
||||
$this->dispatcher = $container->get('dispatcher');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportChartScript($results)
|
||||
{
|
||||
return json_encode($results->chart);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'display-adplays-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'display-adplays-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm()
|
||||
{
|
||||
return new ReportForm(
|
||||
'display-adplays-report-form',
|
||||
'displayAdPlay',
|
||||
'Connector Reports',
|
||||
[
|
||||
'fromDateOneDay' => Carbon::now()->subSeconds(86400)->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
],
|
||||
__('Select a display')
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$data['hiddenFields'] = '';
|
||||
$data['reportName'] = 'displayAdPlay';
|
||||
|
||||
return [
|
||||
'template' => 'display-adplays-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$filterCriteria = [
|
||||
'filter' => $filter,
|
||||
'displayId' => $sanitizedParams->getInt('displayId'),
|
||||
'displayIds' => $sanitizedParams->getIntArray('displayIds'),
|
||||
];
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
$filterCriteria['groupByFilter'] = 'byweek';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
$filterCriteria['groupByFilter'] = 'bymonth';
|
||||
}
|
||||
|
||||
$filterCriteria['sendEmail'] = $sanitizedParams->getCheckbox('sendEmail');
|
||||
$filterCriteria['nonusers'] = $sanitizedParams->getString('nonusers');
|
||||
|
||||
// Return
|
||||
return [
|
||||
'filterCriteria' => json_encode($filterCriteria),
|
||||
'schedule' => $schedule
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$saveAs = sprintf(__('%s report for ', ucfirst($sanitizedParams->getString('filter'))));
|
||||
|
||||
$displayId = $sanitizedParams->getInt('displayId');
|
||||
if (!empty($displayId)) {
|
||||
// Get display
|
||||
try {
|
||||
$displayName = $this->displayFactory->getById($displayId)->display;
|
||||
$saveAs .= '(Display: '. $displayName . ')';
|
||||
} catch (NotFoundException $error) {
|
||||
$saveAs .= '(DisplayId: Not Found )';
|
||||
}
|
||||
}
|
||||
|
||||
return $saveAs;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function restructureSavedReportOldJson($result)
|
||||
{
|
||||
return [
|
||||
'periodStart' => $result['periodStart'],
|
||||
'periodEnd' => $result['periodEnd'],
|
||||
'table' => $result['result'],
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportResults($json, $savedReport)
|
||||
{
|
||||
// Get filter criteria
|
||||
$rs = $this->reportScheduleFactory->getById($savedReport->reportScheduleId, 1)->filterCriteria;
|
||||
$filterCriteria = json_decode($rs, true);
|
||||
|
||||
// Show filter criteria
|
||||
$metadata = [];
|
||||
|
||||
// Get Meta data
|
||||
$metadata['periodStart'] = $json['metadata']['periodStart'];
|
||||
$metadata['periodEnd'] = $json['metadata']['periodEnd'];
|
||||
$metadata['generatedOn'] = Carbon::createFromTimestamp($savedReport->generatedOn)
|
||||
->format(DateFormatHelper::getSystemFormat());
|
||||
$metadata['title'] = $savedReport->saveAs;
|
||||
|
||||
// Report result object
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$json['table'],
|
||||
$json['recordsTotal'],
|
||||
$json['chart']
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getResults(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$layoutId = $sanitizedParams->getInt('layoutId');
|
||||
$parentCampaignId = $sanitizedParams->getInt('parentCampaignId');
|
||||
|
||||
// Get campaign
|
||||
if (!empty($parentCampaignId)) {
|
||||
$campaign = $this->campaignFactory->getById($parentCampaignId);
|
||||
}
|
||||
|
||||
// Display filter.
|
||||
try {
|
||||
// Get an array of display id this user has access to.
|
||||
$displayIds = $this->getDisplayIdFilter($sanitizedParams);
|
||||
} catch (GeneralException $exception) {
|
||||
// stop the query
|
||||
return new ReportResult();
|
||||
}
|
||||
|
||||
//
|
||||
// From and To Date Selection
|
||||
// --------------------------
|
||||
// Our report has a range filter which determines whether the user has to enter their own from / to dates
|
||||
// check the range filter first and set from/to dates accordingly.
|
||||
$reportFilter = $sanitizedParams->getString('reportFilter');
|
||||
|
||||
// Use the current date as a helper
|
||||
$now = Carbon::now();
|
||||
|
||||
switch ($reportFilter) {
|
||||
case 'today':
|
||||
$fromDt = $now->copy()->startOfDay();
|
||||
$toDt = $fromDt->copy()->addDay();
|
||||
break;
|
||||
|
||||
case 'yesterday':
|
||||
$fromDt = $now->copy()->startOfDay()->subDay();
|
||||
$toDt = $now->copy()->startOfDay();
|
||||
break;
|
||||
|
||||
case 'thisweek':
|
||||
$fromDt = $now->copy()->locale(Translate::GetLocale())->startOfWeek();
|
||||
$toDt = $fromDt->copy()->addWeek();
|
||||
break;
|
||||
|
||||
case 'thismonth':
|
||||
$fromDt = $now->copy()->startOfMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
break;
|
||||
|
||||
case 'thisyear':
|
||||
$fromDt = $now->copy()->startOfYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
break;
|
||||
|
||||
case 'lastweek':
|
||||
$fromDt = $now->copy()->locale(Translate::GetLocale())->startOfWeek()->subWeek();
|
||||
$toDt = $fromDt->copy()->addWeek();
|
||||
break;
|
||||
|
||||
case 'lastmonth':
|
||||
$fromDt = $now->copy()->startOfMonth()->subMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
break;
|
||||
|
||||
case 'lastyear':
|
||||
$fromDt = $now->copy()->startOfYear()->subYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
break;
|
||||
|
||||
case '':
|
||||
default:
|
||||
// Expect dates to be provided.
|
||||
$fromDt = $sanitizedParams->getDate('fromDt', ['default' => Carbon::now()->subDay()]);
|
||||
$fromDt->startOfDay();
|
||||
|
||||
$toDt = $sanitizedParams->getDate('toDt', ['default' => Carbon::now()]);
|
||||
$toDt->endOfDay();
|
||||
|
||||
// What if the fromdt and todt are exactly the same?
|
||||
// in this case assume an entire day from midnight on the fromdt to midnight on the todt (i.e. add a day to the todt)
|
||||
if ($fromDt == $toDt) {
|
||||
$toDt->addDay();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$params = [
|
||||
'campaignId' => $parentCampaignId,
|
||||
'layoutId' => $layoutId,
|
||||
'displayIds' => $displayIds,
|
||||
'groupBy' => $sanitizedParams->getString('groupBy')
|
||||
];
|
||||
|
||||
// when the reportfilter is wholecampaign take campaign start/end as form/to date
|
||||
if (!empty($parentCampaignId) && $sanitizedParams->getString('reportFilter') === 'wholecampaign') {
|
||||
$params['fromDt'] = !empty($campaign->getStartDt()) ? $campaign->getStartDt()->format('Y-m-d H:i:s') : null;
|
||||
$params['toDt'] = !empty($campaign->getEndDt()) ? $campaign->getEndDt()->format('Y-m-d H:i:s') : null;
|
||||
|
||||
if (empty($campaign->getStartDt()) || empty($campaign->getEndDt())) {
|
||||
return new ReportResult();
|
||||
}
|
||||
} else {
|
||||
$params['fromDt'] = $fromDt->format('Y-m-d H:i:s');
|
||||
$params['toDt'] = $toDt->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
// --------
|
||||
// ReportDataEvent
|
||||
$event = new ReportDataEvent('displayAdPlay');
|
||||
|
||||
// Set query params for report
|
||||
$event->setParams($params);
|
||||
|
||||
// Dispatch the event - listened by Audience Report Connector
|
||||
$this->dispatcher->dispatch($event, ReportDataEvent::$NAME);
|
||||
$results = $event->getResults();
|
||||
|
||||
$result['periodStart'] = $params['fromDt'];
|
||||
$result['periodEnd'] = $params['toDt'];
|
||||
|
||||
$rows = [];
|
||||
$labels = [];
|
||||
$adPlaysData = [];
|
||||
$impressionsData = [];
|
||||
$spendData = [];
|
||||
$backgroundColor = [];
|
||||
|
||||
foreach ($results['json'] as $row) {
|
||||
// ----
|
||||
// Build Chart data
|
||||
$labels[] = $row['labelDate'];
|
||||
|
||||
$backgroundColor[] = 'rgb(34, 207, 207, 0.7)';
|
||||
|
||||
$adPlays = $row['adPlays'];
|
||||
$adPlaysData[] = ($adPlays == '') ? 0 : $adPlays;
|
||||
|
||||
$impressions = $row['impressions'];
|
||||
$impressionsData[] = ($impressions == '') ? 0 : $impressions;
|
||||
|
||||
$spend = $row['spend'];
|
||||
$spendData[] = ($spend == '') ? 0 : $spend;
|
||||
|
||||
// ----
|
||||
// Build Tabular data
|
||||
$entry = [];
|
||||
|
||||
$entry['labelDate'] = $row['labelDate'];
|
||||
$entry['adPlays'] = $row['adPlays'];
|
||||
$entry['adDuration'] = $row['adDuration'];
|
||||
$entry['impressions'] = $row['impressions'];
|
||||
$entry['spend'] = $row['spend'];
|
||||
|
||||
$rows[] = $entry;
|
||||
}
|
||||
|
||||
// Build Chart to pass in twig file chart.js
|
||||
$chart = [
|
||||
'type' => 'bar',
|
||||
'data' => [
|
||||
'labels' => $labels,
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => __('Total ad plays'),
|
||||
'yAxisID' => 'AdPlay',
|
||||
'backgroundColor' => $backgroundColor,
|
||||
'data' => $adPlaysData
|
||||
],
|
||||
[
|
||||
'label' => __('Total impressions'),
|
||||
'yAxisID' => 'Impression',
|
||||
'borderColor' => 'rgba(255,159,64,255)',
|
||||
'type' => 'line',
|
||||
'fill' => false,
|
||||
'data' => $impressionsData
|
||||
],
|
||||
[
|
||||
'label' => __('Total spend'),
|
||||
'yAxisID' => 'Spend',
|
||||
'borderColor' => 'rgba(255,99,132,255)',
|
||||
'type' => 'line',
|
||||
'fill' => false,
|
||||
'data' => $spendData
|
||||
]
|
||||
]
|
||||
],
|
||||
'options' => [
|
||||
'scales' => [
|
||||
'yAxes' => [
|
||||
[
|
||||
'id' => 'AdPlay',
|
||||
'type' => 'linear',
|
||||
'position' => 'left',
|
||||
'display' => true,
|
||||
'scaleLabel' => [
|
||||
'display' => true,
|
||||
'labelString' => __('Ad Play(s)')
|
||||
],
|
||||
'ticks' => [
|
||||
'beginAtZero' => true
|
||||
]
|
||||
], [
|
||||
'id' => 'Impression',
|
||||
'type' => 'linear',
|
||||
'position' => 'right',
|
||||
'display' => true,
|
||||
'scaleLabel' => [
|
||||
'display' => true,
|
||||
'labelString' => __('Impression(s)')
|
||||
],
|
||||
'ticks' => [
|
||||
'beginAtZero' => true
|
||||
]
|
||||
], [
|
||||
'id' => 'Spend',
|
||||
'type' => 'linear',
|
||||
'position' => 'right',
|
||||
'display' => true,
|
||||
'scaleLabel' => [
|
||||
'display' => true,
|
||||
'labelString' => __('Spend')
|
||||
],
|
||||
'ticks' => [
|
||||
'beginAtZero' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Set Meta data
|
||||
$metadata = [
|
||||
'periodStart' => $result['periodStart'],
|
||||
'periodEnd' => $result['periodEnd'],
|
||||
];
|
||||
|
||||
$recordsTotal = count($rows);
|
||||
|
||||
// ----
|
||||
// Table Only
|
||||
// Return data to build chart/table
|
||||
// This will get saved to a json file when schedule runs
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
$recordsTotal,
|
||||
$chart,
|
||||
$results['error'] ?? null
|
||||
);
|
||||
}
|
||||
}
|
||||
323
lib/Report/DisplayAlerts.php
Normal file
323
lib/Report/DisplayAlerts.php
Normal file
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 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\Controller\DataTablesDotNetTrait;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Factory\DisplayEventFactory;
|
||||
use Xibo\Factory\DisplayFactory;
|
||||
use Xibo\Factory\DisplayGroupFactory;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Class DisplayAlerts
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
class DisplayAlerts implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait, DataTablesDotNetTrait;
|
||||
|
||||
/** @var DisplayFactory */
|
||||
private $displayFactory;
|
||||
/** @var DisplayGroupFactory */
|
||||
private $displayGroupFactory;
|
||||
/** @var DisplayEventFactory */
|
||||
private $displayEventFactory;
|
||||
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->displayFactory = $container->get('displayFactory');
|
||||
$this->displayGroupFactory = $container->get('displayGroupFactory');
|
||||
$this->displayEventFactory = $container->get('displayEventFactory');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'displayalerts-email-template.twig';
|
||||
}
|
||||
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'displayalerts-report-preview';
|
||||
}
|
||||
|
||||
public function getReportForm()
|
||||
{
|
||||
return new ReportForm(
|
||||
'displayalerts-report-form',
|
||||
'displayalerts',
|
||||
'Display',
|
||||
[
|
||||
'fromDate' => Carbon::now()->startOfMonth()->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$data = [];
|
||||
$data['reportName'] = 'displayalerts';
|
||||
|
||||
return [
|
||||
'template' => 'displayalerts-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$displayId = $sanitizedParams->getInt('displayId');
|
||||
$displayGroupIds = $sanitizedParams->getIntArray('displayGroupId', ['default' => []]);
|
||||
$filterCriteria['displayId'] = $displayId;
|
||||
|
||||
if (empty($displayId) && count($displayGroupIds) > 0) {
|
||||
$filterCriteria['displayGroupId'] = $displayGroupIds;
|
||||
}
|
||||
|
||||
$filterCriteria['filter'] = $filter;
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
}
|
||||
|
||||
$filterCriteria['sendEmail'] = $sanitizedParams->getCheckbox('sendEmail');
|
||||
$filterCriteria['nonusers'] = $sanitizedParams->getString('nonusers');
|
||||
|
||||
// Return
|
||||
return [
|
||||
'filterCriteria' => json_encode($filterCriteria),
|
||||
'schedule' => $schedule
|
||||
];
|
||||
}
|
||||
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
return sprintf(__('%s report for Display'), ucfirst($sanitizedParams->getString('filter')));
|
||||
}
|
||||
|
||||
public function restructureSavedReportOldJson($json)
|
||||
{
|
||||
return $json;
|
||||
}
|
||||
|
||||
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'],
|
||||
);
|
||||
}
|
||||
|
||||
public function getResults(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$displayIds = $this->getDisplayIdFilter($sanitizedParams);
|
||||
$onlyLoggedIn = $sanitizedParams->getCheckbox('onlyLoggedIn') == 1;
|
||||
|
||||
//
|
||||
// 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');
|
||||
$currentDate = Carbon::now()->startOfDay();
|
||||
|
||||
// If toDt is current date then make it current datetime
|
||||
if ($toDt->format('Y-m-d') == $currentDate->format('Y-m-d')) {
|
||||
$toDt = Carbon::now();
|
||||
}
|
||||
|
||||
$metadata = [
|
||||
'periodStart' => Carbon::createFromTimestamp($fromDt->toDateTime()->format('U'))
|
||||
->format(DateFormatHelper::getSystemFormat()),
|
||||
'periodEnd' => Carbon::createFromTimestamp($toDt->toDateTime()->format('U'))
|
||||
->format(DateFormatHelper::getSystemFormat()),
|
||||
];
|
||||
|
||||
$params = [
|
||||
'start' => $fromDt->format('U'),
|
||||
'end' => $toDt->format('U')
|
||||
];
|
||||
|
||||
$sql = 'SELECT
|
||||
`displayevent`.displayId,
|
||||
`display`.display,
|
||||
`displayevent`.start,
|
||||
`displayevent`.end,
|
||||
`displayevent`.eventTypeId,
|
||||
`displayevent`.refId,
|
||||
`displayevent`.detail
|
||||
FROM `displayevent`
|
||||
INNER JOIN `display` ON `display`.displayId = `displayevent`.displayId
|
||||
INNER JOIN `lkdisplaydg` ON `display`.displayId = `lkdisplaydg`.displayId
|
||||
INNER JOIN `displaygroup` ON `displaygroup`.displayGroupId = `lkdisplaydg`.displayGroupId
|
||||
AND `displaygroup`.isDisplaySpecific = 1
|
||||
WHERE `displayevent`.eventDate BETWEEN :start AND :end ';
|
||||
|
||||
$eventTypeIdFilter = $sanitizedParams->getString('eventType');
|
||||
|
||||
if ($eventTypeIdFilter != -1) {
|
||||
$params['eventTypeId'] = $eventTypeIdFilter;
|
||||
|
||||
$sql .= 'AND `displayevent`.eventTypeId = :eventTypeId ';
|
||||
}
|
||||
|
||||
if (count($displayIds) > 0) {
|
||||
$sql .= 'AND `displayevent`.displayId IN (' . implode(',', $displayIds) . ')';
|
||||
}
|
||||
|
||||
if ($onlyLoggedIn) {
|
||||
$sql .= ' AND `display`.loggedIn = 1 ';
|
||||
}
|
||||
|
||||
// Tags
|
||||
if (!empty($sanitizedParams->getString('tags'))) {
|
||||
$tagFilter = $sanitizedParams->getString('tags');
|
||||
|
||||
if (trim($tagFilter) === '--no-tag') {
|
||||
$sql .= ' AND `displaygroup`.displaygroupId NOT IN (
|
||||
SELECT `lktagdisplaygroup`.displaygroupId
|
||||
FROM tag
|
||||
INNER JOIN `lktagdisplaygroup`
|
||||
ON `lktagdisplaygroup`.tagId = tag.tagId
|
||||
)
|
||||
';
|
||||
} else {
|
||||
$operator = $sanitizedParams->getCheckbox('exactTags') == 1 ? '=' : 'LIKE';
|
||||
$logicalOperator = $sanitizedParams->getString('logicalOperator', ['default' => 'OR']);
|
||||
$allTags = explode(',', $tagFilter);
|
||||
$notTags = [];
|
||||
$tags = [];
|
||||
|
||||
foreach ($allTags as $tag) {
|
||||
if (str_starts_with($tag, '-')) {
|
||||
$notTags[] = ltrim(($tag), '-');
|
||||
} else {
|
||||
$tags[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($notTags)) {
|
||||
$sql .= ' AND `displaygroup`.displaygroupId NOT IN (
|
||||
SELECT `lktagdisplaygroup`.displaygroupId
|
||||
FROM tag
|
||||
INNER JOIN `lktagdisplaygroup`
|
||||
ON `lktagdisplaygroup`.tagId = tag.tagId
|
||||
';
|
||||
|
||||
$this->displayFactory->tagFilter(
|
||||
$notTags,
|
||||
'lktagdisplaygroup',
|
||||
'lkTagDisplayGroupId',
|
||||
'displayGroupId',
|
||||
$logicalOperator,
|
||||
$operator,
|
||||
true,
|
||||
$sql,
|
||||
$params
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($tags)) {
|
||||
$sql .= ' AND `displaygroup`.displaygroupId IN (
|
||||
SELECT `lktagdisplaygroup`.displaygroupId
|
||||
FROM tag
|
||||
INNER JOIN `lktagdisplaygroup`
|
||||
ON `lktagdisplaygroup`.tagId = tag.tagId
|
||||
';
|
||||
|
||||
$this->displayFactory->tagFilter(
|
||||
$tags,
|
||||
'lktagdisplaygroup',
|
||||
'lkTagDisplayGroupId',
|
||||
'displayGroupId',
|
||||
$logicalOperator,
|
||||
$operator,
|
||||
false,
|
||||
$sql,
|
||||
$params
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sorting?
|
||||
$sortOrder = $this->gridRenderSort($sanitizedParams);
|
||||
|
||||
if (is_array($sortOrder)) {
|
||||
$sql .= 'ORDER BY ' . implode(',', $sortOrder);
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$displayEvent = $this->displayEventFactory->createEmpty()->hydrate($row);
|
||||
$displayEvent->setUnmatchedProperty(
|
||||
'eventType',
|
||||
$displayEvent->getEventNameFromId($displayEvent->eventTypeId)
|
||||
);
|
||||
$displayEvent->setUnmatchedProperty(
|
||||
'display',
|
||||
$row['display']
|
||||
);
|
||||
|
||||
$rows[] = $displayEvent;
|
||||
}
|
||||
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
count($rows),
|
||||
);
|
||||
}
|
||||
}
|
||||
316
lib/Report/DisplayPercentage.php
Normal file
316
lib/Report/DisplayPercentage.php
Normal file
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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 MongoDB\BSON\UTCDateTime;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Xibo\Controller\DataTablesDotNetTrait;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Event\ReportDataEvent;
|
||||
use Xibo\Factory\CampaignFactory;
|
||||
use Xibo\Factory\DisplayFactory;
|
||||
use Xibo\Factory\LayoutFactory;
|
||||
use Xibo\Factory\ReportScheduleFactory;
|
||||
use Xibo\Helper\ApplicationState;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\SanitizerService;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Support\Exception\GeneralException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Class DisplayPercentage
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
class DisplayPercentage implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait, DataTablesDotNetTrait;
|
||||
|
||||
/**
|
||||
* @var CampaignFactory
|
||||
*/
|
||||
private $campaignFactory;
|
||||
|
||||
/**
|
||||
* @var DisplayFactory
|
||||
*/
|
||||
private $displayFactory;
|
||||
|
||||
/**
|
||||
* @var LayoutFactory
|
||||
*/
|
||||
private $layoutFactory;
|
||||
|
||||
/**
|
||||
* @var ReportScheduleFactory
|
||||
*/
|
||||
private $reportScheduleFactory;
|
||||
|
||||
/**
|
||||
* @var SanitizerService
|
||||
*/
|
||||
private $sanitizer;
|
||||
|
||||
/**
|
||||
* @var EventDispatcher
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var ApplicationState
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->campaignFactory = $container->get('campaignFactory');
|
||||
$this->displayFactory = $container->get('displayFactory');
|
||||
$this->reportScheduleFactory = $container->get('reportScheduleFactory');
|
||||
$this->sanitizer = $container->get('sanitizerService');
|
||||
$this->dispatcher = $container->get('dispatcher');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportChartScript($results)
|
||||
{
|
||||
return json_encode($results->chart);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'display-percentage-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'display-percentage-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm()
|
||||
{
|
||||
return new ReportForm(
|
||||
'display-percentage-report-form',
|
||||
'displayPercentage',
|
||||
'Connector Reports',
|
||||
[
|
||||
'fromDateOneDay' => Carbon::now()->subSeconds(86400)->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
],
|
||||
__('Select a campaign')
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$data['hiddenFields'] = json_encode([
|
||||
'parentCampaignId' => $sanitizedParams->getInt('parentCampaignId')
|
||||
]);
|
||||
$data['reportName'] = 'displayPercentage';
|
||||
|
||||
return [
|
||||
'template' => 'display-percentage-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$hiddenFields = json_decode($sanitizedParams->getString('hiddenFields'), true);
|
||||
|
||||
$filterCriteria = [
|
||||
'filter' => $filter,
|
||||
'parentCampaignId' => $hiddenFields['parentCampaignId']
|
||||
];
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
$filterCriteria['groupByFilter'] = 'byweek';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
$filterCriteria['groupByFilter'] = 'bymonth';
|
||||
}
|
||||
|
||||
$filterCriteria['sendEmail'] = $sanitizedParams->getCheckbox('sendEmail');
|
||||
$filterCriteria['nonusers'] = $sanitizedParams->getString('nonusers');
|
||||
|
||||
// Return
|
||||
return [
|
||||
'filterCriteria' => json_encode($filterCriteria),
|
||||
'schedule' => $schedule
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$saveAs = sprintf(__('%s report for ', ucfirst($sanitizedParams->getString('filter'))));
|
||||
|
||||
$parentCampaignId = $sanitizedParams->getInt('parentCampaignId');
|
||||
if (!empty($parentCampaignId)) {
|
||||
// Get display
|
||||
try {
|
||||
$parentCampaignName = $this->campaignFactory->getById($parentCampaignId)->campaign;
|
||||
$saveAs .= '(Campaign: '. $parentCampaignName . ')';
|
||||
} catch (NotFoundException $error) {
|
||||
$saveAs .= '(Campaign: Not Found )';
|
||||
}
|
||||
}
|
||||
|
||||
return $saveAs;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function restructureSavedReportOldJson($result)
|
||||
{
|
||||
return [
|
||||
'periodStart' => $result['periodStart'],
|
||||
'periodEnd' => $result['periodEnd'],
|
||||
'table' => $result['result'],
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportResults($json, $savedReport)
|
||||
{
|
||||
// Get filter criteria
|
||||
$rs = $this->reportScheduleFactory->getById($savedReport->reportScheduleId, 1)->filterCriteria;
|
||||
$filterCriteria = json_decode($rs, true);
|
||||
|
||||
// Show filter criteria
|
||||
$metadata = [];
|
||||
|
||||
// Get Meta data
|
||||
$metadata['periodStart'] = $json['metadata']['periodStart'];
|
||||
$metadata['periodEnd'] = $json['metadata']['periodEnd'];
|
||||
$metadata['generatedOn'] = Carbon::createFromTimestamp($savedReport->generatedOn)
|
||||
->format(DateFormatHelper::getSystemFormat());
|
||||
$metadata['title'] = $savedReport->saveAs;
|
||||
|
||||
// Report result object
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$json['table'],
|
||||
$json['recordsTotal'],
|
||||
$json['chart']
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getResults(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$params = [
|
||||
'parentCampaignId' => $sanitizedParams->getInt('parentCampaignId')
|
||||
];
|
||||
|
||||
// --------
|
||||
// ReportDataEvent
|
||||
$event = new ReportDataEvent('displayPercentage');
|
||||
|
||||
// Set query params for report
|
||||
$event->setParams($params);
|
||||
|
||||
// Dispatch the event - listened by Audience Report Connector
|
||||
$this->dispatcher->dispatch($event, ReportDataEvent::$NAME);
|
||||
$results = $event->getResults();
|
||||
|
||||
// TODO
|
||||
$result['periodStart'] = Carbon::now()->format('Y-m-d H:i:s');
|
||||
$result['periodEnd'] = Carbon::now()->format('Y-m-d H:i:s');
|
||||
|
||||
$rows = [];
|
||||
$displayCache = [];
|
||||
|
||||
foreach ($results['json'] as $row) {
|
||||
// ----
|
||||
// Build Chart data
|
||||
|
||||
// ----
|
||||
// Build Tabular data
|
||||
$entry = [];
|
||||
|
||||
// --------
|
||||
// Get Display
|
||||
try {
|
||||
if (!array_key_exists($row['displayId'], $displayCache)) {
|
||||
$display = $this->displayFactory->getById($row['displayId']);
|
||||
$displayCache[$row['displayId']] = $display->display;
|
||||
}
|
||||
$entry['label'] = $displayCache[$row['displayId']] ?? '';
|
||||
} catch (\Exception $e) {
|
||||
$entry['label'] = __('Not found');
|
||||
}
|
||||
|
||||
$entry['spendData'] = $row['spendData'];
|
||||
$entry['playtimeDuration'] = $row['playtimeDuration'];
|
||||
$entry['backgroundColor'] = '#'.substr(md5($row['displayId']), 0, 6);
|
||||
|
||||
$rows[] = $entry;
|
||||
}
|
||||
|
||||
// Build Chart to pass in twig file chart.js
|
||||
$chart = [];
|
||||
|
||||
// Set Meta data
|
||||
$metadata = [
|
||||
'periodStart' => $result['periodStart'],
|
||||
'periodEnd' => $result['periodEnd'],
|
||||
];
|
||||
|
||||
$recordsTotal = count($rows);
|
||||
|
||||
// ----
|
||||
// Table Only
|
||||
// Return data to build chart/table
|
||||
// This will get saved to a json file when schedule runs
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
$recordsTotal,
|
||||
$chart,
|
||||
$results['error'] ?? null
|
||||
);
|
||||
}
|
||||
}
|
||||
1258
lib/Report/DistributionReport.php
Normal file
1258
lib/Report/DistributionReport.php
Normal file
File diff suppressed because it is too large
Load Diff
636
lib/Report/LibraryUsage.php
Normal file
636
lib/Report/LibraryUsage.php
Normal file
@@ -0,0 +1,636 @@
|
||||
<?php
|
||||
|
||||
namespace Xibo\Report;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Xibo\Controller\DataTablesDotNetTrait;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Factory\DisplayFactory;
|
||||
use Xibo\Factory\DisplayGroupFactory;
|
||||
use Xibo\Factory\LayoutFactory;
|
||||
use Xibo\Factory\MediaFactory;
|
||||
use Xibo\Factory\SavedReportFactory;
|
||||
use Xibo\Factory\UserFactory;
|
||||
use Xibo\Factory\UserGroupFactory;
|
||||
use Xibo\Helper\ApplicationState;
|
||||
use Xibo\Helper\ByteFormatter;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\SanitizerService;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Service\ConfigServiceInterface;
|
||||
use Xibo\Service\ReportServiceInterface;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Class LibraryUsage
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
class LibraryUsage implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait, DataTablesDotNetTrait;
|
||||
|
||||
/**
|
||||
* @var DisplayFactory
|
||||
*/
|
||||
private $displayFactory;
|
||||
|
||||
/**
|
||||
* @var MediaFactory
|
||||
*/
|
||||
private $mediaFactory;
|
||||
|
||||
/**
|
||||
* @var LayoutFactory
|
||||
*/
|
||||
private $layoutFactory;
|
||||
|
||||
/**
|
||||
* @var SavedReportFactory
|
||||
*/
|
||||
private $savedReportFactory;
|
||||
|
||||
/**
|
||||
* @var UserFactory
|
||||
*/
|
||||
private $userFactory;
|
||||
|
||||
/**
|
||||
* @var UserGroupFactory
|
||||
*/
|
||||
private $userGroupFactory;
|
||||
|
||||
/**
|
||||
* @var DisplayGroupFactory
|
||||
*/
|
||||
private $displayGroupFactory;
|
||||
|
||||
/**
|
||||
* @var ReportServiceInterface
|
||||
*/
|
||||
private $reportService;
|
||||
|
||||
/**
|
||||
* @var ConfigServiceInterface
|
||||
*/
|
||||
private $configService;
|
||||
|
||||
/**
|
||||
* @var SanitizerService
|
||||
*/
|
||||
private $sanitizer;
|
||||
|
||||
/**
|
||||
* @var ApplicationState
|
||||
*/
|
||||
private $state;
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->mediaFactory = $container->get('mediaFactory');
|
||||
$this->userFactory = $container->get('userFactory');
|
||||
$this->userGroupFactory = $container->get('userGroupFactory');
|
||||
$this->reportService = $container->get('reportService');
|
||||
$this->configService = $container->get('configService');
|
||||
$this->sanitizer = $container->get('sanitizerService');
|
||||
$this->dispatcher = $container->get('dispatcher');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDispatcher()
|
||||
{
|
||||
return $this->dispatcher;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportChartScript($results)
|
||||
{
|
||||
return json_encode($results->chart);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'libraryusage-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'libraryusage-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm()
|
||||
{
|
||||
$data = [];
|
||||
|
||||
// Set up some suffixes
|
||||
$suffixes = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
|
||||
|
||||
// Widget for the library usage pie chart
|
||||
try {
|
||||
if ($this->getUser()->libraryQuota != 0) {
|
||||
$libraryLimit = $this->getUser()->libraryQuota * 1024;
|
||||
} else {
|
||||
$libraryLimit = $this->configService->getSetting('LIBRARY_SIZE_LIMIT_KB') * 1024;
|
||||
}
|
||||
|
||||
// Library Size in Bytes
|
||||
$params = [];
|
||||
$sql = 'SELECT IFNULL(SUM(FileSize), 0) AS SumSize, type FROM `media` WHERE 1 = 1 ';
|
||||
$this->mediaFactory->viewPermissionSql(
|
||||
'Xibo\Entity\Media',
|
||||
$sql,
|
||||
$params,
|
||||
'`media`.mediaId',
|
||||
'`media`.userId',
|
||||
[
|
||||
'userCheckUserId' => $this->getUser()->userId
|
||||
]
|
||||
);
|
||||
$sql .= ' GROUP BY type ';
|
||||
|
||||
$sth = $this->store->getConnection()->prepare($sql);
|
||||
$sth->execute($params);
|
||||
|
||||
$results = $sth->fetchAll();
|
||||
// add any dependencies fonts, player software etc to the results
|
||||
$event = new \Xibo\Event\DependencyFileSizeEvent($results);
|
||||
$this->getDispatcher()->dispatch($event, $event::$NAME);
|
||||
$results = $event->getResults();
|
||||
|
||||
// Do we base the units on the maximum size or the library limit
|
||||
$maxSize = 0;
|
||||
if ($libraryLimit > 0) {
|
||||
$maxSize = $libraryLimit;
|
||||
} else {
|
||||
// Find the maximum sized chunk of the items in the library
|
||||
foreach ($results as $library) {
|
||||
$maxSize = ($library['SumSize'] > $maxSize) ? $library['SumSize'] : $maxSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Decide what our units are going to be, based on the size
|
||||
$base = ($maxSize == 0) ? 0 : floor(log($maxSize) / log(1024));
|
||||
|
||||
$libraryUsage = [];
|
||||
$libraryLabels = [];
|
||||
$totalSize = 0;
|
||||
foreach ($results as $library) {
|
||||
$libraryUsage[] = round((double)$library['SumSize'] / (pow(1024, $base)), 2);
|
||||
$libraryLabels[] = ucfirst($library['type']) . ' ' . $suffixes[$base];
|
||||
|
||||
$totalSize = $totalSize + $library['SumSize'];
|
||||
}
|
||||
|
||||
// Do we need to add the library remaining?
|
||||
if ($libraryLimit > 0) {
|
||||
$remaining = round(($libraryLimit - $totalSize) / (pow(1024, $base)), 2);
|
||||
|
||||
$libraryUsage[] = $remaining;
|
||||
$libraryLabels[] = __('Free') . ' ' . $suffixes[$base];
|
||||
}
|
||||
|
||||
// What if we are empty?
|
||||
if (count($results) == 0 && $libraryLimit <= 0) {
|
||||
$libraryUsage[] = 0;
|
||||
$libraryLabels[] = __('Empty');
|
||||
}
|
||||
|
||||
$data['libraryLimitSet'] = ($libraryLimit > 0);
|
||||
$data['libraryLimit'] = (round((double)$libraryLimit / (pow(1024, $base)), 2)) . ' ' . $suffixes[$base];
|
||||
$data['librarySize'] = ByteFormatter::format($totalSize, 1);
|
||||
$data['librarySuffix'] = $suffixes[$base];
|
||||
$data['libraryWidgetLabels'] = json_encode($libraryLabels);
|
||||
$data['libraryWidgetData'] = json_encode($libraryUsage);
|
||||
} catch (\Exception $exception) {
|
||||
$this->getLog()->error('Error rendering the library stats page widget');
|
||||
}
|
||||
|
||||
// Note: getReportForm is only run by the web UI and therefore the logged-in users permissions are checked here
|
||||
$data['users'] = $this->userFactory->query();
|
||||
$data['groups'] = $this->userGroupFactory->query();
|
||||
$data['availableReports'] = $this->reportService->listReports();
|
||||
|
||||
return new ReportForm(
|
||||
'libraryusage-report-form',
|
||||
'libraryusage',
|
||||
'Library',
|
||||
$data
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$data = [];
|
||||
$data['reportName'] = 'libraryusage';
|
||||
|
||||
// Note: getReportScheduleFormData is only run by the web UI and therefore the logged-in users permissions
|
||||
// are checked here
|
||||
$data['users'] = $this->userFactory->query();
|
||||
$data['groups'] = $this->userGroupFactory->query();
|
||||
|
||||
return [
|
||||
'template' => 'libraryusage-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
|
||||
$userId = $sanitizedParams->getInt('userId');
|
||||
$filterCriteria['userId'] = $userId;
|
||||
|
||||
$groupId = $sanitizedParams->getInt('groupId');
|
||||
$filterCriteria['groupId'] = $groupId;
|
||||
|
||||
$filterCriteria['filter'] = $filter;
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
}
|
||||
|
||||
$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 library usage report', 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)
|
||||
{
|
||||
$filter = [
|
||||
'userId' => $sanitizedParams->getInt('userId'),
|
||||
'groupId' => $sanitizedParams->getInt('groupId'),
|
||||
'start' => $sanitizedParams->getInt('start'),
|
||||
'length' => $sanitizedParams->getInt('length'),
|
||||
];
|
||||
|
||||
//
|
||||
// From and To Date Selection
|
||||
// --------------------------
|
||||
// Our report has a range filter which determins whether or not the user has to enter their own from / to dates
|
||||
// check the range filter first and set from/to dates accordingly.
|
||||
$reportFilter = $sanitizedParams->getString('reportFilter');
|
||||
|
||||
// Use the current date as a helper
|
||||
$now = Carbon::now();
|
||||
|
||||
switch ($reportFilter) {
|
||||
// the monthly data starts from yesterday
|
||||
case 'yesterday':
|
||||
$fromDt = $now->copy()->startOfDay()->subDay();
|
||||
$toDt = $now->copy()->startOfDay();
|
||||
|
||||
break;
|
||||
case 'lastweek':
|
||||
$fromDt = $now->copy()->locale(Translate::GetLocale())->startOfWeek()->subWeek();
|
||||
$toDt = $fromDt->copy()->addWeek();
|
||||
|
||||
break;
|
||||
|
||||
case 'lastmonth':
|
||||
$fromDt = $now->copy()->startOfMonth()->subMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
|
||||
break;
|
||||
|
||||
case 'lastyear':
|
||||
$fromDt = $now->copy()->startOfYear()->subYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
|
||||
break;
|
||||
|
||||
case '':
|
||||
default:
|
||||
// Expect dates to be provided.
|
||||
$fromDt= $now;
|
||||
$toDt = $now;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$params = [];
|
||||
$select = '
|
||||
SELECT `user`.userId,
|
||||
`user`.userName,
|
||||
IFNULL(SUM(`media`.FileSize), 0) AS bytesUsed,
|
||||
COUNT(`media`.mediaId) AS numFiles
|
||||
';
|
||||
$body = '
|
||||
FROM `user`
|
||||
LEFT OUTER JOIN `media`
|
||||
ON `media`.userID = `user`.UserID
|
||||
WHERE 1 = 1
|
||||
';
|
||||
|
||||
// Restrict on the users we have permission to see
|
||||
// Normal users can only see themselves
|
||||
$permissions = '';
|
||||
if ($this->getUser()->userTypeId == 3) {
|
||||
$permissions .= ' AND user.userId = :currentUserId ';
|
||||
$filterBy['currentUserId'] = $this->getUser()->userId;
|
||||
} elseif ($this->getUser()->userTypeId == 2) {
|
||||
// Group admins can only see users from their groups.
|
||||
$permissions .= '
|
||||
AND user.userId IN (
|
||||
SELECT `otherUserLinks`.userId
|
||||
FROM `lkusergroup`
|
||||
INNER JOIN `group`
|
||||
ON `group`.groupId = `lkusergroup`.groupId
|
||||
AND `group`.isUserSpecific = 0
|
||||
INNER JOIN `lkusergroup` `otherUserLinks`
|
||||
ON `otherUserLinks`.groupId = `group`.groupId
|
||||
WHERE `lkusergroup`.userId = :currentUserId
|
||||
)
|
||||
';
|
||||
$params['currentUserId'] = $this->getUser()->userId;
|
||||
}
|
||||
|
||||
// Filter by userId
|
||||
if ($sanitizedParams->getInt('userId') !== null) {
|
||||
$body .= ' AND user.userId = :userId ';
|
||||
$params['userId'] = $sanitizedParams->getInt('userId');
|
||||
}
|
||||
|
||||
// Filter by groupId
|
||||
if ($sanitizedParams->getInt('groupId') !== null) {
|
||||
$body .= ' AND user.userId IN (SELECT userId FROM `lkusergroup` WHERE groupId = :groupId) ';
|
||||
$params['groupId'] = $sanitizedParams->getInt('groupId');
|
||||
}
|
||||
|
||||
$body .= $permissions;
|
||||
$body .= '
|
||||
GROUP BY `user`.userId,
|
||||
`user`.userName
|
||||
';
|
||||
|
||||
// Sorting?
|
||||
$filterBy = $this->gridRenderFilter($filter);
|
||||
$sortOrder = $this->gridRenderSort($sanitizedParams);
|
||||
|
||||
$order = '';
|
||||
if (is_array($sortOrder)) {
|
||||
$newSortOrder = [];
|
||||
foreach ($sortOrder as $sort) {
|
||||
if ($sort == '`bytesUsedFormatted`') {
|
||||
$newSortOrder[] = '`bytesUsed`';
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($sort == '`bytesUsedFormatted` DESC') {
|
||||
$newSortOrder[] = '`bytesUsed` DESC';
|
||||
continue;
|
||||
}
|
||||
$newSortOrder[] = $sort;
|
||||
}
|
||||
$sortOrder = $newSortOrder;
|
||||
|
||||
$order .= 'ORDER BY ' . implode(',', $sortOrder);
|
||||
}
|
||||
|
||||
$limit = '';
|
||||
// Paging
|
||||
if ($filterBy !== null
|
||||
&& $sanitizedParams->getInt('start') !== null
|
||||
&& $sanitizedParams->getInt('length') !== null
|
||||
) {
|
||||
$limit = ' LIMIT ' . $sanitizedParams->getInt('start', ['default' => 0])
|
||||
. ', ' . $sanitizedParams->getInt('length', ['default' => 10]);
|
||||
}
|
||||
|
||||
$sql = $select . $body . $order . $limit;
|
||||
$rows = [];
|
||||
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$entry = [];
|
||||
$sanitizedRow = $this->sanitizer->getSanitizer($row);
|
||||
|
||||
$entry['userId'] = $sanitizedRow->getInt('userId');
|
||||
$entry['userName'] = $sanitizedRow->getString('userName');
|
||||
$entry['bytesUsed'] = $sanitizedRow->getInt('bytesUsed');
|
||||
$entry['bytesUsedFormatted'] = ByteFormatter::format($sanitizedRow->getInt('bytesUsed'), 2);
|
||||
$entry['numFiles'] = $sanitizedRow->getInt('numFiles');
|
||||
|
||||
$rows[] = $entry;
|
||||
}
|
||||
|
||||
// Paging
|
||||
$recordsTotal = 0;
|
||||
if ($limit != '' && count($rows) > 0) {
|
||||
$results = $this->store->select('SELECT COUNT(*) AS total FROM `user` ' . $permissions, $params);
|
||||
$recordsTotal = intval($results[0]['total']);
|
||||
}
|
||||
|
||||
// Get the Library widget labels and Widget Data
|
||||
$libraryWidgetLabels = [];
|
||||
$libraryWidgetData = [];
|
||||
$suffixes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
|
||||
|
||||
// Widget for the library usage pie chart
|
||||
try {
|
||||
if ($this->getUser()->libraryQuota != 0) {
|
||||
$libraryLimit = $this->userFactory->getUser()->libraryQuota * 1024;
|
||||
} else {
|
||||
$libraryLimit = $this->configService->getSetting('LIBRARY_SIZE_LIMIT_KB') * 1024;
|
||||
}
|
||||
|
||||
// Library Size in Bytes
|
||||
$params = [];
|
||||
$sql = 'SELECT IFNULL(SUM(FileSize), 0) AS SumSize, type FROM `media` WHERE 1 = 1 ';
|
||||
$this->mediaFactory->viewPermissionSql(
|
||||
'Xibo\Entity\Media',
|
||||
$sql,
|
||||
$params,
|
||||
'`media`.mediaId',
|
||||
'`media`.userId',
|
||||
[
|
||||
'userCheckUserId' => $this->getUser()->userId
|
||||
]
|
||||
);
|
||||
$sql .= ' GROUP BY type ';
|
||||
|
||||
$sth = $this->store->getConnection()->prepare($sql);
|
||||
$sth->execute($params);
|
||||
|
||||
$results = $sth->fetchAll();
|
||||
|
||||
// Do we base the units on the maximum size or the library limit
|
||||
$maxSize = 0;
|
||||
if ($libraryLimit > 0) {
|
||||
$maxSize = $libraryLimit;
|
||||
} else {
|
||||
// Find the maximum sized chunk of the items in the library
|
||||
foreach ($results as $library) {
|
||||
$maxSize = ($library['SumSize'] > $maxSize) ? $library['SumSize'] : $maxSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Decide what our units are going to be, based on the size
|
||||
$base = ($maxSize == 0) ? 0 : floor(log($maxSize) / log(1024));
|
||||
|
||||
$libraryUsage = [];
|
||||
$libraryLabels = [];
|
||||
$totalSize = 0;
|
||||
foreach ($results as $library) {
|
||||
$libraryUsage[] = round((double)$library['SumSize'] / (pow(1024, $base)), 2);
|
||||
$libraryLabels[] = ucfirst($library['type']) . ' ' . $suffixes[$base];
|
||||
|
||||
$totalSize = $totalSize + $library['SumSize'];
|
||||
}
|
||||
|
||||
// Do we need to add the library remaining?
|
||||
if ($libraryLimit > 0) {
|
||||
$remaining = round(($libraryLimit - $totalSize) / (pow(1024, $base)), 2);
|
||||
|
||||
$libraryUsage[] = $remaining;
|
||||
$libraryLabels[] = __('Free') . ' ' . $suffixes[$base];
|
||||
}
|
||||
|
||||
// What if we are empty?
|
||||
if (count($results) == 0 && $libraryLimit <= 0) {
|
||||
$libraryUsage[] = 0;
|
||||
$libraryLabels[] = __('Empty');
|
||||
}
|
||||
|
||||
$libraryWidgetLabels = $libraryLabels;
|
||||
$libraryWidgetData = $libraryUsage;
|
||||
} catch (\Exception $exception) {
|
||||
$this->getLog()->error('Error rendering the library stats page widget');
|
||||
}
|
||||
|
||||
|
||||
// Build the Library Usage and User Percentage Usage chart data
|
||||
$totalSize = 0;
|
||||
foreach ($rows as $row) {
|
||||
$totalSize += $row['bytesUsed'];
|
||||
}
|
||||
|
||||
$userData = [];
|
||||
$userLabels = [];
|
||||
foreach ($rows as $row) {
|
||||
$userData[] = ($row['bytesUsed']/$totalSize)*100;
|
||||
$userLabels[] = $row['userName'];
|
||||
}
|
||||
|
||||
$colours = [];
|
||||
foreach ($userData as $userDatum) {
|
||||
$colours[] = 'rgb(' . mt_rand(0, 255).','. mt_rand(0, 255).',' . mt_rand(0, 255) .')';
|
||||
}
|
||||
|
||||
$libraryColours = [];
|
||||
foreach ($libraryWidgetData as $libraryDatum) {
|
||||
$libraryColours[] = 'rgb(' . mt_rand(0, 255).','. mt_rand(0, 255).',' . mt_rand(0, 255) .')';
|
||||
}
|
||||
|
||||
$chart = [
|
||||
// we will use User_Percentage_Usage as report name when we export/email pdf
|
||||
'User_Percentage_Usage' => [
|
||||
'type' => 'pie',
|
||||
'data' => [
|
||||
'labels' => $userLabels,
|
||||
'datasets' => [
|
||||
[
|
||||
'backgroundColor' => $colours,
|
||||
'data' => $userData
|
||||
]
|
||||
]
|
||||
],
|
||||
'options' => [
|
||||
'maintainAspectRatio' => false
|
||||
]
|
||||
],
|
||||
'Library_Usage' => [
|
||||
'type' => 'pie',
|
||||
'data' => [
|
||||
'labels' => $libraryWidgetLabels,
|
||||
'datasets' => [
|
||||
[
|
||||
'backgroundColor' => $libraryColours,
|
||||
'data' => $libraryWidgetData
|
||||
]
|
||||
]
|
||||
],
|
||||
'options' => [
|
||||
'maintainAspectRatio' => false
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// ----
|
||||
// Both Chart and Table
|
||||
// Return data to build chart/table
|
||||
// This will get saved to a json file when schedule runs
|
||||
return new ReportResult(
|
||||
[
|
||||
'periodStart' => $fromDt->format(DateFormatHelper::getSystemFormat()),
|
||||
'periodEnd' => $toDt->format(DateFormatHelper::getSystemFormat()),
|
||||
],
|
||||
$rows,
|
||||
$recordsTotal,
|
||||
$chart
|
||||
);
|
||||
}
|
||||
}
|
||||
418
lib/Report/MobileProofOfPlay.php
Normal file
418
lib/Report/MobileProofOfPlay.php
Normal file
@@ -0,0 +1,418 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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 Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Xibo\Controller\DataTablesDotNetTrait;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Event\ReportDataEvent;
|
||||
use Xibo\Factory\CampaignFactory;
|
||||
use Xibo\Factory\DisplayFactory;
|
||||
use Xibo\Factory\LayoutFactory;
|
||||
use Xibo\Factory\ReportScheduleFactory;
|
||||
use Xibo\Helper\ApplicationState;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\SanitizerService;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Support\Exception\GeneralException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Class MobileProofOfPlay
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
class MobileProofOfPlay implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait, DataTablesDotNetTrait;
|
||||
|
||||
/**
|
||||
* @var CampaignFactory
|
||||
*/
|
||||
private $campaignFactory;
|
||||
|
||||
/**
|
||||
* @var DisplayFactory
|
||||
*/
|
||||
private $displayFactory;
|
||||
|
||||
/**
|
||||
* @var LayoutFactory
|
||||
*/
|
||||
private $layoutFactory;
|
||||
|
||||
/**
|
||||
* @var ReportScheduleFactory
|
||||
*/
|
||||
private $reportScheduleFactory;
|
||||
|
||||
/**
|
||||
* @var SanitizerService
|
||||
*/
|
||||
private $sanitizer;
|
||||
|
||||
/**
|
||||
* @var EventDispatcher
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* @var ApplicationState
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->campaignFactory = $container->get('campaignFactory');
|
||||
$this->displayFactory = $container->get('displayFactory');
|
||||
$this->layoutFactory = $container->get('layoutFactory');
|
||||
$this->reportScheduleFactory = $container->get('reportScheduleFactory');
|
||||
$this->sanitizer = $container->get('sanitizerService');
|
||||
$this->dispatcher = $container->get('dispatcher');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'mobile-proofofplay-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'mobile-proofofplay-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm()
|
||||
{
|
||||
return new ReportForm(
|
||||
'mobile-proofofplay-report-form',
|
||||
'mobileProofOfPlay',
|
||||
'Connector Reports',
|
||||
[
|
||||
'fromDateOneDay' => Carbon::now()->subSeconds(86400)->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat())
|
||||
],
|
||||
__('Select a display')
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$data['hiddenFields'] = '';
|
||||
$data['reportName'] = 'mobileProofOfPlay';
|
||||
|
||||
return [
|
||||
'template' => 'mobile-proofofplay-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$filterCriteria = [
|
||||
'filter' => $filter,
|
||||
'displayId' => $sanitizedParams->getInt('displayId'),
|
||||
'displayIds' => $sanitizedParams->getIntArray('displayIds'),
|
||||
];
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
}
|
||||
|
||||
$filterCriteria['sendEmail'] = $sanitizedParams->getCheckbox('sendEmail');
|
||||
$filterCriteria['nonusers'] = $sanitizedParams->getString('nonusers');
|
||||
|
||||
// Return
|
||||
return [
|
||||
'filterCriteria' => json_encode($filterCriteria),
|
||||
'schedule' => $schedule
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$saveAs = sprintf(__('%s report for ', ucfirst($sanitizedParams->getString('filter'))));
|
||||
|
||||
$displayId = $sanitizedParams->getInt('displayId');
|
||||
if (!empty($displayId)) {
|
||||
// Get display
|
||||
try {
|
||||
$displayName = $this->displayFactory->getById($displayId)->display;
|
||||
$saveAs .= '(Display: '. $displayName . ')';
|
||||
} catch (NotFoundException $error) {
|
||||
$saveAs .= '(DisplayId: Not Found )';
|
||||
}
|
||||
}
|
||||
|
||||
return $saveAs;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function restructureSavedReportOldJson($result)
|
||||
{
|
||||
return [
|
||||
'periodStart' => $result['periodStart'],
|
||||
'periodEnd' => $result['periodEnd'],
|
||||
'table' => $result['result'],
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportResults($json, $savedReport)
|
||||
{
|
||||
// Get filter criteria
|
||||
$rs = $this->reportScheduleFactory->getById($savedReport->reportScheduleId, 1)->filterCriteria;
|
||||
$filterCriteria = json_decode($rs, true);
|
||||
|
||||
// Show filter criteria
|
||||
$metadata = [];
|
||||
|
||||
// Get Meta data
|
||||
$metadata['periodStart'] = $json['metadata']['periodStart'];
|
||||
$metadata['periodEnd'] = $json['metadata']['periodEnd'];
|
||||
$metadata['generatedOn'] = Carbon::createFromTimestamp($savedReport->generatedOn)
|
||||
->format(DateFormatHelper::getSystemFormat());
|
||||
$metadata['title'] = $savedReport->saveAs;
|
||||
|
||||
// Report result object
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$json['table'],
|
||||
$json['recordsTotal']
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getResults(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$parentCampaignId = $sanitizedParams->getInt('parentCampaignId');
|
||||
$layoutId = $sanitizedParams->getInt('layoutId');
|
||||
|
||||
// Get campaign
|
||||
if (!empty($parentCampaignId)) {
|
||||
$campaign = $this->campaignFactory->getById($parentCampaignId);
|
||||
}
|
||||
|
||||
// Display filter.
|
||||
try {
|
||||
// Get an array of display id this user has access to.
|
||||
$displayIds = $this->getDisplayIdFilter($sanitizedParams);
|
||||
} catch (GeneralException $exception) {
|
||||
// stop the query
|
||||
return new ReportResult();
|
||||
}
|
||||
|
||||
//
|
||||
// From and To Date Selection
|
||||
// --------------------------
|
||||
// Our report has a range filter which determines whether the user has to enter their own from / to dates
|
||||
// check the range filter first and set from/to dates accordingly.
|
||||
$reportFilter = $sanitizedParams->getString('reportFilter');
|
||||
|
||||
// Use the current date as a helper
|
||||
$now = Carbon::now();
|
||||
|
||||
switch ($reportFilter) {
|
||||
case 'today':
|
||||
$fromDt = $now->copy()->startOfDay();
|
||||
$toDt = $fromDt->copy()->addDay();
|
||||
break;
|
||||
|
||||
case 'yesterday':
|
||||
$fromDt = $now->copy()->startOfDay()->subDay();
|
||||
$toDt = $now->copy()->startOfDay();
|
||||
break;
|
||||
|
||||
case 'thisweek':
|
||||
$fromDt = $now->copy()->locale(Translate::GetLocale())->startOfWeek();
|
||||
$toDt = $fromDt->copy()->addWeek();
|
||||
break;
|
||||
|
||||
case 'thismonth':
|
||||
$fromDt = $now->copy()->startOfMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
break;
|
||||
|
||||
case 'thisyear':
|
||||
$fromDt = $now->copy()->startOfYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
break;
|
||||
|
||||
case 'lastweek':
|
||||
$fromDt = $now->copy()->locale(Translate::GetLocale())->startOfWeek()->subWeek();
|
||||
$toDt = $fromDt->copy()->addWeek();
|
||||
break;
|
||||
|
||||
case 'lastmonth':
|
||||
$fromDt = $now->copy()->startOfMonth()->subMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
break;
|
||||
|
||||
case 'lastyear':
|
||||
$fromDt = $now->copy()->startOfYear()->subYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
break;
|
||||
|
||||
case '':
|
||||
default:
|
||||
// Expect dates to be provided.
|
||||
$fromDt = $sanitizedParams->getDate('fromDt', ['default' => Carbon::now()->subDay()]);
|
||||
$fromDt->startOfDay();
|
||||
|
||||
$toDt = $sanitizedParams->getDate('toDt', ['default' => Carbon::now()]);
|
||||
$toDt->endOfDay();
|
||||
|
||||
// What if the fromdt and todt are exactly the same?
|
||||
// in this case assume an entire day from midnight on the fromdt to midnight on the todt (i.e. add a day to the todt)
|
||||
if ($fromDt == $toDt) {
|
||||
$toDt->addDay();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$params = [
|
||||
'campaignId' => $parentCampaignId,
|
||||
'layoutId' => $layoutId,
|
||||
'displayIds' => $displayIds,
|
||||
];
|
||||
|
||||
// when the reportfilter is wholecampaign take campaign start/end as form/to date
|
||||
if (!empty($parentCampaignId) && $sanitizedParams->getString('reportFilter') === 'wholecampaign') {
|
||||
$params['from'] = !empty($campaign->getStartDt()) ? $campaign->getStartDt()->format('Y-m-d H:i:s') : null;
|
||||
$params['to'] = !empty($campaign->getEndDt()) ? $campaign->getEndDt()->format('Y-m-d H:i:s') : null;
|
||||
|
||||
if (empty($campaign->getStartDt()) || empty($campaign->getEndDt())) {
|
||||
return new ReportResult();
|
||||
}
|
||||
} else {
|
||||
$params['from'] = $fromDt->format('Y-m-d H:i:s');
|
||||
$params['to'] = $toDt->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
// --------
|
||||
// ReportDataEvent
|
||||
$event = new ReportDataEvent('mobileProofofplay');
|
||||
|
||||
// Set query params for report
|
||||
$event->setParams($params);
|
||||
|
||||
// Dispatch the event - listened by Audience Report Connector
|
||||
$this->dispatcher->dispatch($event, ReportDataEvent::$NAME);
|
||||
$results = $event->getResults();
|
||||
|
||||
$result['periodStart'] = $params['from'];
|
||||
$result['periodEnd'] = $params['to'];
|
||||
|
||||
$rows = [];
|
||||
$displayCache = [];
|
||||
$layoutCache = [];
|
||||
foreach ($results['json'] as $row) {
|
||||
$entry = [];
|
||||
|
||||
$entry['from'] = $row['from'];
|
||||
$entry['to'] = $row['to'];
|
||||
|
||||
// --------
|
||||
// Get Display
|
||||
$entry['displayId'] = $row['displayId'];
|
||||
try {
|
||||
if (!empty($entry['displayId'])) {
|
||||
if (!array_key_exists($row['displayId'], $displayCache)) {
|
||||
$display = $this->displayFactory->getById($row['displayId']);
|
||||
$displayCache[$row['displayId']] = $display->display;
|
||||
}
|
||||
}
|
||||
$entry['display'] = $displayCache[$row['displayId']] ?? '';
|
||||
} catch (\Exception $e) {
|
||||
$entry['display'] = __('Not found');
|
||||
}
|
||||
// --------
|
||||
// Get layout
|
||||
$entry['layoutId'] = $row['layoutId'];
|
||||
try {
|
||||
if (!empty($entry['layoutId'])) {
|
||||
if (!array_key_exists($row['layoutId'], $layoutCache)) {
|
||||
$layout = $this->layoutFactory->getById($row['layoutId']);
|
||||
$layoutCache[$row['layoutId']] = $layout->layout;
|
||||
}
|
||||
}
|
||||
$entry['layout'] = $layoutCache[$row['layoutId']] ?? '';
|
||||
} catch (\Exception $e) {
|
||||
$entry['layout'] = __('Not found');
|
||||
}
|
||||
|
||||
$entry['startLat'] = $row['startLat'];
|
||||
$entry['startLong'] = $row['startLong'];
|
||||
$entry['endLat'] = $row['endLat'];
|
||||
$entry['endLong'] = $row['endLong'];
|
||||
$entry['duration'] = $row['duration'];
|
||||
|
||||
$rows[] = $entry;
|
||||
}
|
||||
|
||||
// Set Meta data
|
||||
$metadata = [
|
||||
'periodStart' => $result['periodStart'],
|
||||
'periodEnd' => $result['periodEnd'],
|
||||
];
|
||||
|
||||
$recordsTotal = count($rows);
|
||||
|
||||
// ----
|
||||
// Table Only
|
||||
// Return data to build chart/table
|
||||
// This will get saved to a json file when schedule runs
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
$recordsTotal,
|
||||
[],
|
||||
$results['error'] ?? null
|
||||
);
|
||||
}
|
||||
}
|
||||
103
lib/Report/PeriodTrait.php
Normal file
103
lib/Report/PeriodTrait.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2021 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Trait PeriodTrait
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
trait PeriodTrait
|
||||
{
|
||||
public function generateHourPeriods($filterRangeStart, $filterRangeEnd, $start, $end, $ranges)
|
||||
{
|
||||
|
||||
$periodData = []; // to generate periods table
|
||||
|
||||
// Generate all hours of the period
|
||||
foreach ($ranges as $range) {
|
||||
$startHour = $start->addHour()->format('U');
|
||||
|
||||
// Remove the period which crossed the end range
|
||||
if ($startHour >= $filterRangeEnd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Period start
|
||||
$periodData[$range]['start'] = $startHour;
|
||||
if ($periodData[$range]['start'] < $filterRangeStart) {
|
||||
$periodData[$range]['start'] = $filterRangeStart;
|
||||
}
|
||||
|
||||
// Period end
|
||||
$periodData[$range]['end'] = $end->addHour()->format('U');
|
||||
if ($periodData[$range]['end'] > $filterRangeEnd) {
|
||||
$periodData[$range]['end'] = $filterRangeEnd;
|
||||
}
|
||||
|
||||
$hourofday = Carbon::createFromTimestamp($periodData[$range]['start'])->hour;
|
||||
|
||||
// groupbycol = hour
|
||||
$periodData[$range]['groupbycol'] = $hourofday;
|
||||
}
|
||||
|
||||
return $periodData;
|
||||
}
|
||||
|
||||
public function generateDayPeriods($filterRangeStart, $filterRangeEnd, $start, $end, $ranges, $groupByFilter = null)
|
||||
{
|
||||
$periodData = []; // to generate periods table
|
||||
|
||||
// Generate all days of the period
|
||||
foreach ($ranges as $range) {
|
||||
$startDay = $start->addDay()->format('U');
|
||||
|
||||
// Remove the period which crossed the end range
|
||||
if ($startDay >= $filterRangeEnd) {
|
||||
continue;
|
||||
}
|
||||
// Period start
|
||||
$periodData[$range]['start'] = $startDay;
|
||||
if ($periodData[$range]['start'] < $filterRangeStart) {
|
||||
$periodData[$range]['start'] = $filterRangeStart;
|
||||
}
|
||||
|
||||
// Period end
|
||||
$periodData[$range]['end'] = $end->addDay()->format('U');
|
||||
if ($periodData[$range]['end'] > $filterRangeEnd) {
|
||||
$periodData[$range]['end'] = $filterRangeEnd;
|
||||
}
|
||||
|
||||
if ($groupByFilter == 'bydayofweek') {
|
||||
$groupbycol = Carbon::createFromTimestamp($periodData[$range]['start'])->dayOfWeekIso;
|
||||
} else {
|
||||
$groupbycol = Carbon::createFromTimestamp($periodData[$range]['start'])->day;
|
||||
}
|
||||
|
||||
// groupbycol = dayofweek
|
||||
$periodData[$range]['groupbycol'] = $groupbycol;
|
||||
}
|
||||
return $periodData;
|
||||
}
|
||||
}
|
||||
1368
lib/Report/ProofOfPlay.php
Normal file
1368
lib/Report/ProofOfPlay.php
Normal file
File diff suppressed because it is too large
Load Diff
334
lib/Report/ReportDefaultTrait.php
Normal file
334
lib/Report/ReportDefaultTrait.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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 http\Exception\RuntimeException;
|
||||
use Psr\Log\NullLogger;
|
||||
use Slim\Http\ServerRequest as Request;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Storage\TimeSeriesStoreInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Trait ReportDefaultTrait
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
trait ReportDefaultTrait
|
||||
{
|
||||
/**
|
||||
* @var StorageServiceInterface
|
||||
*/
|
||||
private $store;
|
||||
|
||||
/**
|
||||
* @var TimeSeriesStoreInterface
|
||||
*/
|
||||
private $timeSeriesStore;
|
||||
|
||||
/**
|
||||
* @var LogServiceInterface
|
||||
*/
|
||||
private $logService;
|
||||
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/** @var \Xibo\Entity\User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* Set common dependencies.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param TimeSeriesStoreInterface $timeSeriesStore
|
||||
* @return $this
|
||||
*/
|
||||
public function setCommonDependencies($store, $timeSeriesStore)
|
||||
{
|
||||
$this->store = $store;
|
||||
$this->timeSeriesStore = $timeSeriesStore;
|
||||
$this->logService = new NullLogger();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LogServiceInterface $logService
|
||||
* @return $this
|
||||
*/
|
||||
public function useLogger(LogServiceInterface $logService)
|
||||
{
|
||||
$this->logService = $logService;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Store
|
||||
* @return StorageServiceInterface
|
||||
*/
|
||||
protected function getStore()
|
||||
{
|
||||
return $this->store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TimeSeriesStore
|
||||
* @return TimeSeriesStoreInterface
|
||||
*/
|
||||
protected function getTimeSeriesStore()
|
||||
{
|
||||
return $this->timeSeriesStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Log
|
||||
* @return LogServiceInterface
|
||||
*/
|
||||
protected function getLog()
|
||||
{
|
||||
return $this->logService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Xibo\Entity\User
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user Id
|
||||
* @param \Xibo\Entity\User $user
|
||||
* @return $this
|
||||
*/
|
||||
public function setUser($user)
|
||||
{
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get chart script
|
||||
* @param ReportResult $results
|
||||
* @return string
|
||||
*/
|
||||
public function getReportChartScript($results)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate saved report name
|
||||
* @param SanitizerInterface $sanitizedParams
|
||||
* @return string
|
||||
*/
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$saveAs = sprintf(__('%s report'), ucfirst($sanitizedParams->getString('filter')));
|
||||
|
||||
return $saveAs. ' '. Carbon::now()->format('Y-m-d');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a temporary table representing the periods covered
|
||||
* @param Carbon $fromDt
|
||||
* @param Carbon $toDt
|
||||
* @param string $groupByFilter
|
||||
* @param string $table
|
||||
* @param string $customLabel Custom Label
|
||||
* @return string
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getTemporaryPeriodsTable($fromDt, $toDt, $groupByFilter, $table = 'temp_periods', $customLabel = 'Y-m-d H:i:s')
|
||||
{
|
||||
// My from/to dt represent the entire range we're interested in.
|
||||
// we need to generate periods according to our grouping, within that range.
|
||||
// Clone them so as to not effect the calling object
|
||||
$fromDt = $fromDt->copy();
|
||||
$toDt = $toDt->copy();
|
||||
|
||||
// our from/to dates might not sit nicely inside our period groupings
|
||||
// for example if we look at June, by week, the 1st of June is a Saturday, week 22.
|
||||
// NB:
|
||||
// FromDT/ToDt should always be at the start of the day.
|
||||
switch ($groupByFilter) {
|
||||
case 'byweek':
|
||||
$fromDt->locale(Translate::GetLocale())->startOfWeek();
|
||||
break;
|
||||
|
||||
case 'bymonth':
|
||||
$fromDt->startOfMonth();
|
||||
break;
|
||||
}
|
||||
|
||||
// Temporary Periods Table
|
||||
// -----------------------
|
||||
// we will use a temporary table for this.
|
||||
// Drop table if exists
|
||||
|
||||
$this->getStore()->getConnection()->exec('
|
||||
DROP TABLE IF EXISTS `' . $table . '`');
|
||||
|
||||
$this->getStore()->getConnection()->exec('
|
||||
CREATE TEMPORARY TABLE `' . $table . '` (
|
||||
id INT,
|
||||
customLabel VARCHAR(20),
|
||||
label VARCHAR(20),
|
||||
start INT,
|
||||
end INT
|
||||
);
|
||||
');
|
||||
|
||||
// Prepare an insert statement
|
||||
$periods = $this->getStore()->getConnection()->prepare('
|
||||
INSERT INTO `' . $table . '` (id, customLabel, label, start, end)
|
||||
VALUES (:id, :customLabel, :label, :start, :end)
|
||||
');
|
||||
|
||||
|
||||
// Loop until we've covered all periods needed
|
||||
$loopDate = $fromDt->copy();
|
||||
while ($toDt > $loopDate) {
|
||||
// We add different periods for each type of grouping
|
||||
if ($groupByFilter == 'byhour') {
|
||||
$periods->execute([
|
||||
'id' => $loopDate->hour,
|
||||
'customLabel' => $loopDate->format($customLabel),
|
||||
'label' => $loopDate->format('g:i A'),
|
||||
'start' => $loopDate->format('U'),
|
||||
'end' => $loopDate->addHour()->format('U')
|
||||
]);
|
||||
} elseif ($groupByFilter == 'byday') {
|
||||
$periods->execute([
|
||||
'id' => $loopDate->year . $loopDate->month . $loopDate->day,
|
||||
'customLabel' => $loopDate->format($customLabel),
|
||||
'label' => $loopDate->format('Y-m-d'),
|
||||
'start' => $loopDate->format('U'),
|
||||
'end' => $loopDate->addDay()->format('U')
|
||||
]);
|
||||
} elseif ($groupByFilter == 'byweek') {
|
||||
$weekNo = $loopDate->locale(Translate::GetLocale())->week();
|
||||
|
||||
$periods->execute([
|
||||
'id' => $loopDate->weekOfYear . $loopDate->year,
|
||||
'customLabel' => $loopDate->format($customLabel),
|
||||
'label' => $loopDate->format('Y-m-d') . '(w' . $weekNo . ')',
|
||||
'start' => $loopDate->format('U'),
|
||||
'end' => $loopDate->addWeek()->format('U')
|
||||
]);
|
||||
} elseif ($groupByFilter == 'bymonth') {
|
||||
$periods->execute([
|
||||
'id' => $loopDate->year . $loopDate->month,
|
||||
'customLabel' => $loopDate->format($customLabel),
|
||||
'label' => $loopDate->format('M'),
|
||||
'start' => $loopDate->format('U'),
|
||||
'end' => $loopDate->addMonth()->format('U')
|
||||
]);
|
||||
} elseif ($groupByFilter == 'bydayofweek') {
|
||||
$periods->execute([
|
||||
'id' => $loopDate->dayOfWeek,
|
||||
'customLabel' => $loopDate->format($customLabel),
|
||||
'label' => $loopDate->format('D'),
|
||||
'start' => $loopDate->format('U'),
|
||||
'end' => $loopDate->addDay()->format('U')
|
||||
]);
|
||||
} elseif ($groupByFilter == 'bydayofmonth') {
|
||||
$periods->execute([
|
||||
'id' => $loopDate->day,
|
||||
'customLabel' => $loopDate->format($customLabel),
|
||||
'label' => $loopDate->format('d'),
|
||||
'start' => $loopDate->format('U'),
|
||||
'end' => $loopDate->addDay()->format('U')
|
||||
]);
|
||||
} else {
|
||||
$this->getLog()->error('Unknown Grouping Selected ' . $groupByFilter);
|
||||
throw new InvalidArgumentException(__('Unknown Grouping ') . $groupByFilter, 'groupByFilter');
|
||||
}
|
||||
}
|
||||
|
||||
$this->getLog()->debug(json_encode($this->store->select('SELECT * FROM ' . $table, []), JSON_PRETTY_PRINT));
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of displayIds we should pass into the query,
|
||||
* if an exception is thrown, we should stop the report and return no results.
|
||||
* @param \Xibo\Support\Sanitizer\SanitizerInterface $params
|
||||
* @return array displayIds
|
||||
* @throws \Xibo\Support\Exception\GeneralException
|
||||
*/
|
||||
private function getDisplayIdFilter(SanitizerInterface $params): array
|
||||
{
|
||||
$displayIds = [];
|
||||
|
||||
// Filters
|
||||
$displayId = $params->getInt('displayId');
|
||||
$displayGroupIds = $params->getIntArray('displayGroupId', ['default' => null]);
|
||||
|
||||
if ($displayId !== null) {
|
||||
// Don't bother checking if we are a super admin
|
||||
if (!$this->getUser()->isSuperAdmin()) {
|
||||
$display = $this->displayFactory->getById($displayId);
|
||||
if ($this->getUser()->checkViewable($display)) {
|
||||
$displayIds[] = $displayId;
|
||||
}
|
||||
} else {
|
||||
$displayIds[] = $displayId;
|
||||
}
|
||||
} else {
|
||||
// If we are NOT a super admin OR we have some display group filters
|
||||
// get an array of display id this user has access to.
|
||||
// we cannot rely on the logged-in user because this will be run by the task runner which is a sysadmin
|
||||
if (!$this->getUser()->isSuperAdmin() || $displayGroupIds !== null) {
|
||||
// This will be the displayIds the user has access to, and are in the displayGroupIds provided.
|
||||
foreach ($this->displayFactory->query(
|
||||
null,
|
||||
[
|
||||
'userCheckUserId' => $this->getUser()->userId,
|
||||
'displayGroupIds' => $displayGroupIds,
|
||||
]
|
||||
) as $display) {
|
||||
$displayIds[] = $display->displayId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are a super admin without anything filtered, the object of this method is to return an empty
|
||||
// array.
|
||||
// If we are any other user, we must return something in the array.
|
||||
if (!$this->getUser()->isSuperAdmin() && count($displayIds) <= 0) {
|
||||
throw new InvalidArgumentException(__('No displays with View permissions'), 'displays');
|
||||
}
|
||||
|
||||
return $displayIds;
|
||||
}
|
||||
}
|
||||
127
lib/Report/ReportInterface.php
Normal file
127
lib/Report/ReportInterface.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2019 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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 Psr\Container\ContainerInterface;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Interface ReportInterface
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
interface ReportInterface
|
||||
{
|
||||
/**
|
||||
* Set factories
|
||||
* @param ContainerInterface $container
|
||||
* @return $this
|
||||
*/
|
||||
public function setFactories(ContainerInterface $container);
|
||||
|
||||
/**
|
||||
* Set user Id
|
||||
* @param \Xibo\Entity\User $user
|
||||
* @return $this
|
||||
*/
|
||||
public function setUser($user);
|
||||
|
||||
/**
|
||||
* Get the user
|
||||
* @return \Xibo\Entity\User
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function getUser();
|
||||
|
||||
/**
|
||||
* Get chart script
|
||||
* @param ReportResult $results
|
||||
* @return string
|
||||
*/
|
||||
public function getReportChartScript($results);
|
||||
|
||||
/**
|
||||
* Return the twig file name of the saved report email and export template
|
||||
* @return string
|
||||
*/
|
||||
public function getReportEmailTemplate();
|
||||
|
||||
/**
|
||||
* Return the twig file name of the saved report preview template
|
||||
* @return string
|
||||
*/
|
||||
public function getSavedReportTemplate();
|
||||
|
||||
/**
|
||||
* Return the twig file name of the report form
|
||||
* Load the report form
|
||||
* @return ReportForm
|
||||
*/
|
||||
public function getReportForm();
|
||||
|
||||
/**
|
||||
* Populate form title and hidden fields
|
||||
* @param SanitizerInterface $sanitizedParams
|
||||
* @return array
|
||||
*/
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams);
|
||||
|
||||
/**
|
||||
* Set Report Schedule form data
|
||||
* @param SanitizerInterface $sanitizedParams
|
||||
* @return array
|
||||
*/
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams);
|
||||
|
||||
/**
|
||||
* Generate saved report name
|
||||
* @param SanitizerInterface $sanitizedParams
|
||||
* @return string
|
||||
*/
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams);
|
||||
|
||||
/**
|
||||
* Resrtucture old saved report's json file to support schema version 2
|
||||
* @param $json
|
||||
* @return array
|
||||
*/
|
||||
public function restructureSavedReportOldJson($json);
|
||||
|
||||
/**
|
||||
* Return data from saved json file to build chart/table for saved report
|
||||
* @param array $json
|
||||
* @param object $savedReport
|
||||
* @return ReportResult
|
||||
*/
|
||||
public function getSavedReportResults($json, $savedReport);
|
||||
|
||||
/**
|
||||
* Get results when on demand report runs and
|
||||
* This result will get saved to a json if schedule report runs
|
||||
* @param SanitizerInterface $sanitizedParams
|
||||
* @return ReportResult
|
||||
* @throws \Xibo\Support\Exception\GeneralException
|
||||
*/
|
||||
public function getResults(SanitizerInterface $sanitizedParams);
|
||||
}
|
||||
438
lib/Report/SessionHistory.php
Normal file
438
lib/Report/SessionHistory.php
Normal file
@@ -0,0 +1,438 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 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\Controller\DataTablesDotNetTrait;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Factory\AuditLogFactory;
|
||||
use Xibo\Factory\LogFactory;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Support\Exception\AccessDeniedException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
class SessionHistory implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait, DataTablesDotNetTrait;
|
||||
|
||||
/** @var LogFactory */
|
||||
private $logFactory;
|
||||
|
||||
/** @var AuditLogFactory */
|
||||
private $auditLogFactory;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->logFactory = $container->get('logFactory');
|
||||
$this->auditLogFactory = $container->get('auditLogFactory');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'sessionhistory-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'sessionhistory-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm(): ReportForm
|
||||
{
|
||||
return new ReportForm(
|
||||
'sessionhistory-report-form',
|
||||
'sessionhistory',
|
||||
'Audit',
|
||||
[
|
||||
'fromDate' => Carbon::now()->startOfMonth()->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams): array
|
||||
{
|
||||
$data = [];
|
||||
$data['reportName'] = 'sessionhistory';
|
||||
|
||||
return [
|
||||
'template' => 'sessionhistory-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams): array
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$filterCriteria['userId'] = $sanitizedParams->getInt('userId');
|
||||
$filterCriteria['type'] = $sanitizedParams->getString('type');
|
||||
$filterCriteria['scheduledReport'] = true;
|
||||
|
||||
$filterCriteria['filter'] = $filter;
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
}
|
||||
|
||||
$filterCriteria['sendEmail'] = $sanitizedParams->getCheckbox('sendEmail');
|
||||
$filterCriteria['nonusers'] = $sanitizedParams->getString('nonusers');
|
||||
|
||||
// Return
|
||||
return [
|
||||
'filterCriteria' => json_encode($filterCriteria),
|
||||
'schedule' => $schedule
|
||||
];
|
||||
}
|
||||
|
||||
public function generateSavedReportName(SanitizerInterface $sanitizedParams): string
|
||||
{
|
||||
return sprintf(
|
||||
__('%s Session %s log report for User'),
|
||||
ucfirst($sanitizedParams->getString('filter')),
|
||||
ucfirst($sanitizedParams->getString('type'))
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function restructureSavedReportOldJson($json)
|
||||
{
|
||||
return $json;
|
||||
}
|
||||
|
||||
/** @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'],
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getResults(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
if (!$this->getUser()->isSuperAdmin()) {
|
||||
throw new AccessDeniedException();
|
||||
}
|
||||
|
||||
//
|
||||
// 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.
|
||||
$reportFilter = $sanitizedParams->getString('reportFilter');
|
||||
|
||||
// Use the current date as a helper
|
||||
$now = Carbon::now();
|
||||
|
||||
// This calculation will be retained as it is used for scheduled reports
|
||||
switch ($reportFilter) {
|
||||
case 'yesterday':
|
||||
$fromDt = $now->copy()->startOfDay()->subDay();
|
||||
$toDt = $now->copy()->startOfDay();
|
||||
break;
|
||||
|
||||
case 'lastweek':
|
||||
$fromDt = $now->copy()->locale(Translate::GetLocale())->startOfWeek()->subWeek();
|
||||
$toDt = $fromDt->copy()->addWeek();
|
||||
break;
|
||||
|
||||
case 'lastmonth':
|
||||
$fromDt = $now->copy()->startOfMonth()->subMonth();
|
||||
$toDt = $fromDt->copy()->addMonth();
|
||||
break;
|
||||
|
||||
case 'lastyear':
|
||||
$fromDt = $now->copy()->startOfYear()->subYear();
|
||||
$toDt = $fromDt->copy()->addYear();
|
||||
break;
|
||||
|
||||
case '':
|
||||
default:
|
||||
// fromDt will always be from start of day ie 00:00
|
||||
$fromDt = $sanitizedParams->getDate('fromDt') ?? $now->copy()->startOfDay();
|
||||
$toDt = $sanitizedParams->getDate('toDt') ?? $now;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$metadata = [
|
||||
'periodStart' => Carbon::createFromTimestamp($fromDt->toDateTime()->format('U'))
|
||||
->format(DateFormatHelper::getSystemFormat()),
|
||||
'periodEnd' => Carbon::createFromTimestamp($toDt->toDateTime()->format('U'))
|
||||
->format(DateFormatHelper::getSystemFormat()),
|
||||
];
|
||||
|
||||
$type = $sanitizedParams->getString('type');
|
||||
|
||||
if ($type === 'audit') {
|
||||
$params = [
|
||||
'fromDt' => $fromDt->format('U'),
|
||||
'toDt' => $toDt->format('U'),
|
||||
];
|
||||
|
||||
$sql = 'SELECT
|
||||
`auditlog`.`logId`,
|
||||
`auditlog`.`logDate`,
|
||||
`user`.`userName`,
|
||||
`auditlog`.`message`,
|
||||
`auditlog`.`objectAfter`,
|
||||
`auditlog`.`entity`,
|
||||
`auditlog`.`entityId`,
|
||||
`auditlog`.userId,
|
||||
`auditlog`.ipAddress,
|
||||
`auditlog`.sessionHistoryId,
|
||||
`session_history`.userAgent
|
||||
FROM `auditlog`
|
||||
INNER JOIN `user` ON `user`.`userId` = `auditlog`.`userId`
|
||||
INNER JOIN `session_history` ON `session_history`.`sessionId` = `auditlog`.`sessionHistoryId`
|
||||
WHERE `auditlog`.logDate BETWEEN :fromDt AND :toDt
|
||||
';
|
||||
|
||||
if ($sanitizedParams->getInt('userId') !== null) {
|
||||
$sql .= ' AND `auditlog`.`userId` = :userId';
|
||||
$params['userId'] = $sanitizedParams->getInt('userId');
|
||||
}
|
||||
|
||||
if ($sanitizedParams->getInt('sessionHistoryId') !== null) {
|
||||
$sql .= ' AND `auditlog`.`sessionHistoryId` = :sessionHistoryId';
|
||||
$params['sessionHistoryId'] = $sanitizedParams->getInt('sessionHistoryId');
|
||||
}
|
||||
|
||||
// Sorting?
|
||||
$sortOrder = $this->gridRenderSort($sanitizedParams);
|
||||
|
||||
if (is_array($sortOrder)) {
|
||||
$sql .= ' ORDER BY ' . implode(',', $sortOrder);
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$auditRecord = $this->auditLogFactory->create()->hydrate($row);
|
||||
$auditRecord->setUnmatchedProperty(
|
||||
'userAgent',
|
||||
$row['userAgent']
|
||||
);
|
||||
|
||||
// decode for grid view, leave as json for email/preview.
|
||||
if (!$sanitizedParams->getCheckbox('scheduledReport')) {
|
||||
$auditRecord->objectAfter = json_decode($auditRecord->objectAfter);
|
||||
}
|
||||
|
||||
$auditRecord->logDate = Carbon::createFromTimestamp($auditRecord->logDate)
|
||||
->format(DateFormatHelper::getSystemFormat());
|
||||
|
||||
$rows[] = $auditRecord;
|
||||
}
|
||||
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
count($rows),
|
||||
);
|
||||
} else if ($type === 'debug') {
|
||||
$params = [
|
||||
'fromDt' => $fromDt->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDt' => $toDt->format(DateFormatHelper::getSystemFormat()),
|
||||
];
|
||||
|
||||
$sql = 'SELECT
|
||||
`log`.`logId`,
|
||||
`log`.`logDate`,
|
||||
`log`.`runNo`,
|
||||
`log`.`channel`,
|
||||
`log`.`page`,
|
||||
`log`.`function`,
|
||||
`log`.`type`,
|
||||
`log`.`message`,
|
||||
`log`.`userId`,
|
||||
`log`.`sessionHistoryId`,
|
||||
`user`.`userName`,
|
||||
`display`.`displayId`,
|
||||
`display`.`display`,
|
||||
`session_history`.ipAddress,
|
||||
`session_history`.userAgent
|
||||
FROM `log`
|
||||
LEFT OUTER JOIN `display` ON `display`.`displayid` = `log`.`displayid`
|
||||
INNER JOIN `user` ON `user`.`userId` = `log`.`userId`
|
||||
INNER JOIN `session_history` ON `session_history`.`sessionId` = `log`.`sessionHistoryId`
|
||||
WHERE `log`.logDate BETWEEN :fromDt AND :toDt
|
||||
';
|
||||
|
||||
if ($sanitizedParams->getInt('userId') !== null) {
|
||||
$sql .= ' AND `log`.`userId` = :userId';
|
||||
$params['userId'] = $sanitizedParams->getInt('userId');
|
||||
}
|
||||
|
||||
if ($sanitizedParams->getInt('sessionHistoryId') !== null) {
|
||||
$sql .= ' AND `log`.`sessionHistoryId` = :sessionHistoryId';
|
||||
$params['sessionHistoryId'] = $sanitizedParams->getInt('sessionHistoryId');
|
||||
}
|
||||
|
||||
// Sorting?
|
||||
$sortOrder = $this->gridRenderSort($sanitizedParams);
|
||||
|
||||
if (is_array($sortOrder)) {
|
||||
$sql .= ' ORDER BY ' . implode(',', $sortOrder);
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$logRecord = $this->logFactory->createEmpty()->hydrate($row, ['htmlStringProperties' => ['message']]);
|
||||
$logRecord->setUnmatchedProperty(
|
||||
'userAgent',
|
||||
$row['userAgent']
|
||||
);
|
||||
|
||||
$logRecord->setUnmatchedProperty(
|
||||
'ipAddress',
|
||||
$row['ipAddress']
|
||||
);
|
||||
|
||||
$logRecord->setUnmatchedProperty(
|
||||
'userName',
|
||||
$row['userName']
|
||||
);
|
||||
|
||||
$rows[] = $logRecord;
|
||||
}
|
||||
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
count($rows),
|
||||
);
|
||||
} else {
|
||||
$params = [
|
||||
'fromDt' => $fromDt->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDt' => $toDt->format(DateFormatHelper::getSystemFormat()),
|
||||
];
|
||||
|
||||
$sql = 'SELECT
|
||||
`session_history`.`sessionId`,
|
||||
`session_history`.`startTime`,
|
||||
`session_history`.`userId`,
|
||||
`session_history`.`userAgent`,
|
||||
`session_history`.`ipAddress`,
|
||||
`session_history`.`lastUsedTime`,
|
||||
`user`.`userName`,
|
||||
`usertype`.`userType`
|
||||
FROM `session_history`
|
||||
LEFT OUTER JOIN `user` ON `user`.`userId` = `session_history`.`userId`
|
||||
LEFT OUTER JOIN `usertype` ON `usertype`.`userTypeId` = `user`.`userTypeId`
|
||||
WHERE `session_history`.startTime BETWEEN :fromDt AND :toDt
|
||||
';
|
||||
|
||||
if ($sanitizedParams->getInt('userId') !== null) {
|
||||
$sql .= ' AND `session_history`.`userId` = :userId';
|
||||
$params['userId'] = $sanitizedParams->getInt('userId');
|
||||
}
|
||||
|
||||
if ($sanitizedParams->getInt('sessionHistoryId') !== null) {
|
||||
$sql .= ' AND `session_history`.`sessionId` = :sessionHistoryId';
|
||||
$params['sessionHistoryId'] = $sanitizedParams->getInt('sessionHistoryId');
|
||||
}
|
||||
|
||||
// Sorting?
|
||||
$sortOrder = $this->gridRenderSort($sanitizedParams);
|
||||
|
||||
if (is_array($sortOrder)) {
|
||||
$sql .= ' ORDER BY ' . implode(',', $sortOrder);
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$sessionRecord = $this->logFactory->createEmpty()->hydrate($row, ['htmlStringProperties' => ['message']]);
|
||||
$duration = isset($row['lastUsedTime'])
|
||||
? date_diff(date_create($row['startTime']), date_create($row['lastUsedTime']))->format('%H:%I:%S')
|
||||
: null;
|
||||
|
||||
$sessionRecord->setUnmatchedProperty(
|
||||
'userAgent',
|
||||
$row['userAgent']
|
||||
);
|
||||
|
||||
$sessionRecord->setUnmatchedProperty(
|
||||
'ipAddress',
|
||||
$row['ipAddress']
|
||||
);
|
||||
|
||||
$sessionRecord->setUnmatchedProperty(
|
||||
'userName',
|
||||
$row['userName']
|
||||
);
|
||||
|
||||
$sessionRecord->setUnmatchedProperty(
|
||||
'endTime',
|
||||
$row['lastUsedTime']
|
||||
);
|
||||
|
||||
$sessionRecord->setUnmatchedProperty(
|
||||
'duration',
|
||||
$duration
|
||||
);
|
||||
|
||||
$rows[] = $sessionRecord;
|
||||
}
|
||||
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
count($rows),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
145
lib/Report/SummaryDistributionCommonTrait.php
Normal file
145
lib/Report/SummaryDistributionCommonTrait.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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 Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Common function between the Summary and Distribution reports
|
||||
*/
|
||||
trait SummaryDistributionCommonTrait
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function restructureSavedReportOldJson($result)
|
||||
{
|
||||
$durationData = $result['durationData'];
|
||||
$countData = $result['countData'];
|
||||
$labels = $result['labels'];
|
||||
$backgroundColor = $result['backgroundColor'];
|
||||
$borderColor = $result['borderColor'];
|
||||
$periodStart = $result['periodStart'];
|
||||
$periodEnd = $result['periodEnd'];
|
||||
|
||||
return [
|
||||
'hasData' => count($durationData) > 0 && count($countData) > 0,
|
||||
'chart' => [
|
||||
'type' => 'bar',
|
||||
'data' => [
|
||||
'labels' => $labels,
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => __('Total duration'),
|
||||
'yAxisID' => 'Duration',
|
||||
'backgroundColor' => $backgroundColor,
|
||||
'data' => $durationData
|
||||
],
|
||||
[
|
||||
'label' => __('Total count'),
|
||||
'yAxisID' => 'Count',
|
||||
'borderColor' => $borderColor,
|
||||
'type' => 'line',
|
||||
'fill' => false,
|
||||
'data' => $countData
|
||||
]
|
||||
]
|
||||
],
|
||||
'options' => [
|
||||
'scales' => [
|
||||
'yAxes' => [
|
||||
[
|
||||
'id' => 'Duration',
|
||||
'type' => 'linear',
|
||||
'position' => 'left',
|
||||
'display' => true,
|
||||
'scaleLabel' => [
|
||||
'display' => true,
|
||||
'labelString' => __('Duration(s)')
|
||||
],
|
||||
'ticks' => [
|
||||
'beginAtZero' => true
|
||||
]
|
||||
], [
|
||||
'id' => 'Count',
|
||||
'type' => 'linear',
|
||||
'position' => 'right',
|
||||
'display' => true,
|
||||
'scaleLabel' => [
|
||||
'display' => true,
|
||||
'labelString' => __('Count')
|
||||
],
|
||||
'ticks' => [
|
||||
'beginAtZero' => true
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'periodStart' => $periodStart,
|
||||
'periodEnd' => $periodEnd,
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Xibo\Support\Sanitizer\SanitizerInterface $sanitizedParams
|
||||
* @return array
|
||||
* @throws \Xibo\Support\Exception\InvalidArgumentException
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
private function getReportScheduleFormTitle(SanitizerInterface $sanitizedParams): array
|
||||
{
|
||||
$type = $sanitizedParams->getString('type');
|
||||
if ($type == 'layout') {
|
||||
$selectedId = $sanitizedParams->getInt('layoutId');
|
||||
$title = sprintf(
|
||||
__('Add Report Schedule for %s - %s'),
|
||||
$type,
|
||||
$this->layoutFactory->getById($selectedId)->layout
|
||||
);
|
||||
} elseif ($type == 'media') {
|
||||
$selectedId = $sanitizedParams->getInt('mediaId');
|
||||
$title = sprintf(
|
||||
__('Add Report Schedule for %s - %s'),
|
||||
$type,
|
||||
$this->mediaFactory->getById($selectedId)->name
|
||||
);
|
||||
} elseif ($type == 'event') {
|
||||
$selectedId = 0; // we only need eventTag
|
||||
$eventTag = $sanitizedParams->getString('eventTag');
|
||||
$title = sprintf(
|
||||
__('Add Report Schedule for %s - %s'),
|
||||
$type,
|
||||
$eventTag
|
||||
);
|
||||
} else {
|
||||
throw new InvalidArgumentException(__('Unknown type ') . $type, 'type');
|
||||
}
|
||||
|
||||
return [
|
||||
'title' => $title,
|
||||
'selectedId' => $selectedId
|
||||
];
|
||||
}
|
||||
}
|
||||
1141
lib/Report/SummaryReport.php
Normal file
1141
lib/Report/SummaryReport.php
Normal file
File diff suppressed because it is too large
Load Diff
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')
|
||||
];
|
||||
}
|
||||
}
|
||||
564
lib/Report/TimeDisconnectedSummary.php
Normal file
564
lib/Report/TimeDisconnectedSummary.php
Normal file
@@ -0,0 +1,564 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 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\Controller\DataTablesDotNetTrait;
|
||||
use Xibo\Entity\ReportForm;
|
||||
use Xibo\Entity\ReportResult;
|
||||
use Xibo\Entity\ReportSchedule;
|
||||
use Xibo\Factory\DisplayFactory;
|
||||
use Xibo\Factory\DisplayGroupFactory;
|
||||
use Xibo\Helper\ApplicationState;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\SanitizerService;
|
||||
use Xibo\Helper\Translate;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* Class TimeDisconnectedSummary
|
||||
* @package Xibo\Report
|
||||
*/
|
||||
class TimeDisconnectedSummary implements ReportInterface
|
||||
{
|
||||
use ReportDefaultTrait, DataTablesDotNetTrait;
|
||||
|
||||
/**
|
||||
* @var DisplayFactory
|
||||
*/
|
||||
private $displayFactory;
|
||||
|
||||
/**
|
||||
* @var DisplayGroupFactory
|
||||
*/
|
||||
private $displayGroupFactory;
|
||||
|
||||
/**
|
||||
* @var SanitizerService
|
||||
*/
|
||||
private $sanitizer;
|
||||
|
||||
/**
|
||||
* @var ApplicationState
|
||||
*/
|
||||
private $state;
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setFactories(ContainerInterface $container)
|
||||
{
|
||||
$this->displayFactory = $container->get('displayFactory');
|
||||
$this->displayGroupFactory = $container->get('displayGroupFactory');
|
||||
$this->sanitizer = $container->get('sanitizerService');
|
||||
$this->state = $container->get('state');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportChartScript($results)
|
||||
{
|
||||
return json_encode($results->chart);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportEmailTemplate()
|
||||
{
|
||||
return 'timedisconnectedsummary-email-template.twig';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getSavedReportTemplate()
|
||||
{
|
||||
return 'timedisconnectedsummary-report-preview';
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportForm()
|
||||
{
|
||||
return new ReportForm(
|
||||
'timedisconnectedsummary-report-form',
|
||||
'timedisconnectedsummary',
|
||||
'Display',
|
||||
[
|
||||
'fromDate' => Carbon::now()->subSeconds(86400 * 35)->format(DateFormatHelper::getSystemFormat()),
|
||||
'toDate' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function getReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$data = [];
|
||||
$data['reportName'] = 'timedisconnectedsummary';
|
||||
|
||||
return [
|
||||
'template' => 'timedisconnectedsummary-schedule-form-add',
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function setReportScheduleFormData(SanitizerInterface $sanitizedParams)
|
||||
{
|
||||
$filter = $sanitizedParams->getString('filter');
|
||||
$displayId = $sanitizedParams->getInt('displayId');
|
||||
$displayGroupIds = $sanitizedParams->getIntArray('displayGroupId', ['default' => []]);
|
||||
|
||||
$filterCriteria['displayId'] = $displayId;
|
||||
if (empty($displayId) && count($displayGroupIds) > 0) {
|
||||
$filterCriteria['displayGroupId'] = $displayGroupIds;
|
||||
}
|
||||
$filterCriteria['filter'] = $filter;
|
||||
|
||||
$schedule = '';
|
||||
if ($filter == 'daily') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_DAILY;
|
||||
$filterCriteria['reportFilter'] = 'yesterday';
|
||||
} elseif ($filter == 'weekly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_WEEKLY;
|
||||
$filterCriteria['reportFilter'] = 'lastweek';
|
||||
} elseif ($filter == 'monthly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_MONTHLY;
|
||||
$filterCriteria['reportFilter'] = 'lastmonth';
|
||||
} elseif ($filter == 'yearly') {
|
||||
$schedule = ReportSchedule::$SCHEDULE_YEARLY;
|
||||
$filterCriteria['reportFilter'] = 'lastyear';
|
||||
}
|
||||
|
||||
$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 time disconnected summary report', 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)
|
||||
{
|
||||
// Filter by displayId?
|
||||
$displayIds = $this->getDisplayIdFilter($sanitizedParams);
|
||||
|
||||
$tags = $sanitizedParams->getString('tags');
|
||||
$onlyLoggedIn = $sanitizedParams->getCheckbox('onlyLoggedIn') == 1;
|
||||
$groupBy = $sanitizedParams->getString('groupBy');
|
||||
|
||||
//
|
||||
// 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');
|
||||
$currentDate = Carbon::now()->startOfDay();
|
||||
|
||||
// If toDt is current date then make it current datetime
|
||||
if ($toDt->format('Y-m-d') == $currentDate->format('Y-m-d')) {
|
||||
$toDt = Carbon::now();
|
||||
}
|
||||
|
||||
// Get an array of display groups this user has access to
|
||||
$displayGroupIds = [];
|
||||
|
||||
foreach ($this->displayGroupFactory->query(null, [
|
||||
'isDisplaySpecific' => -1,
|
||||
'userCheckUserId' => $this->getUser()->userId
|
||||
]) as $displayGroup) {
|
||||
$displayGroupIds[] = $displayGroup->displayGroupId;
|
||||
}
|
||||
|
||||
if (count($displayGroupIds) <= 0) {
|
||||
throw new InvalidArgumentException(__('No display groups with View permissions'), 'displayGroup');
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'start' => $fromDt->format('U'),
|
||||
'end' => $toDt->format('U')
|
||||
);
|
||||
|
||||
// Disconnected Displays Query
|
||||
$select = '
|
||||
SELECT display.display, display.displayId,
|
||||
SUM(LEAST(IFNULL(`end`, :end), :end) - GREATEST(`start`, :start)) AS duration,
|
||||
:end - :start as filter ';
|
||||
|
||||
$body = 'FROM `displayevent`
|
||||
INNER JOIN `display`
|
||||
ON display.displayId = `displayevent`.displayId
|
||||
WHERE `start` <= :end
|
||||
AND IFNULL(`end`, :end) >= :start
|
||||
AND :end <= UNIX_TIMESTAMP(NOW())
|
||||
AND `displayevent`.eventTypeId = 1 ';
|
||||
|
||||
if (count($displayIds) > 0) {
|
||||
$body .= 'AND display.displayId IN (' . implode(',', $displayIds) . ') ';
|
||||
}
|
||||
|
||||
if ($onlyLoggedIn) {
|
||||
$body .= ' AND `display`.loggedIn = 1 ';
|
||||
}
|
||||
|
||||
$body .= '
|
||||
GROUP BY display.display, display.displayId
|
||||
';
|
||||
|
||||
$sql = $select . $body;
|
||||
$maxDuration = 0;
|
||||
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$maxDuration = $maxDuration + $this->sanitizer->getSanitizer($row)->getDouble('duration');
|
||||
}
|
||||
|
||||
if ($maxDuration > 86400) {
|
||||
$postUnits = __('Days');
|
||||
$divisor = 86400;
|
||||
} elseif ($maxDuration > 3600) {
|
||||
$postUnits = __('Hours');
|
||||
$divisor = 3600;
|
||||
} else {
|
||||
$postUnits = __('Minutes');
|
||||
$divisor = 60;
|
||||
}
|
||||
|
||||
// Tabular Data
|
||||
$disconnectedDisplays = [];
|
||||
foreach ($this->store->select($sql, $params) as $row) {
|
||||
$sanitizedRow = $this->sanitizer->getSanitizer($row);
|
||||
|
||||
$entry = [];
|
||||
$entry['timeDisconnected'] = round($sanitizedRow->getDouble('duration') / $divisor, 2);
|
||||
$entry['timeConnected'] = round($sanitizedRow->getDouble('filter') / $divisor - $entry['timeDisconnected'], 2);
|
||||
$disconnectedDisplays[$sanitizedRow->getInt(('displayId'))] = $entry;
|
||||
}
|
||||
|
||||
// Displays with filters such as tags
|
||||
$displaySelect = '
|
||||
SELECT display.display, display.displayId ';
|
||||
|
||||
if ($tags != '') {
|
||||
$displaySelect .= ', (SELECT GROUP_CONCAT(DISTINCT tag)
|
||||
FROM tag
|
||||
INNER JOIN lktagdisplaygroup
|
||||
ON lktagdisplaygroup.tagId = tag.tagId
|
||||
WHERE lktagdisplaygroup.displayGroupId = displaygroup.DisplayGroupID
|
||||
GROUP BY lktagdisplaygroup.displayGroupId) AS tags ';
|
||||
}
|
||||
|
||||
$displayBody = 'FROM `display` ';
|
||||
|
||||
if ($groupBy === 'displayGroup') {
|
||||
$displaySelect .= ', displaydg.displayGroup, displaydg.displayGroupId ';
|
||||
}
|
||||
|
||||
if ($tags != '') {
|
||||
$displayBody .= 'INNER JOIN `lkdisplaydg`
|
||||
ON lkdisplaydg.DisplayID = display.displayid
|
||||
INNER JOIN `displaygroup`
|
||||
ON displaygroup.displaygroupId = lkdisplaydg.displaygroupId
|
||||
AND `displaygroup`.isDisplaySpecific = 1 ';
|
||||
}
|
||||
|
||||
// Grouping Option
|
||||
if ($groupBy === 'displayGroup') {
|
||||
$displayBody .= 'INNER JOIN `lkdisplaydg` AS linkdg
|
||||
ON linkdg.DisplayID = display.displayid
|
||||
INNER JOIN `displaygroup` AS displaydg
|
||||
ON displaydg.displaygroupId = linkdg.displaygroupId
|
||||
AND `displaydg`.isDisplaySpecific = 0 ';
|
||||
}
|
||||
|
||||
$displayBody .= 'WHERE 1 = 1 ';
|
||||
|
||||
if (count($displayIds) > 0) {
|
||||
$displayBody .= 'AND display.displayId IN (' . implode(',', $displayIds) . ') ';
|
||||
}
|
||||
|
||||
$tagParams = [];
|
||||
|
||||
if ($tags != '') {
|
||||
if (trim($tags) === '--no-tag') {
|
||||
$displayBody .= ' AND `displaygroup`.displaygroupId NOT IN (
|
||||
SELECT `lktagdisplaygroup`.displaygroupId
|
||||
FROM tag
|
||||
INNER JOIN `lktagdisplaygroup`
|
||||
ON `lktagdisplaygroup`.tagId = tag.tagId
|
||||
)
|
||||
';
|
||||
} else {
|
||||
$operator = $sanitizedParams->getCheckbox('exactTags') == 1 ? '=' : 'LIKE';
|
||||
|
||||
$displayBody .= ' AND `displaygroup`.displaygroupId IN (
|
||||
SELECT `lktagdisplaygroup`.displaygroupId
|
||||
FROM tag
|
||||
INNER JOIN `lktagdisplaygroup`
|
||||
ON `lktagdisplaygroup`.tagId = tag.tagId
|
||||
';
|
||||
$i = 0;
|
||||
|
||||
foreach (explode(',', $tags) as $tag) {
|
||||
$i++;
|
||||
|
||||
if ($i == 1) {
|
||||
$displayBody .= ' WHERE `tag` ' . $operator . ' :tags' . $i;
|
||||
} else {
|
||||
$displayBody .= ' OR `tag` ' . $operator . ' :tags' . $i;
|
||||
}
|
||||
|
||||
if ($operator === '=') {
|
||||
$tagParams['tags' . $i] = $tag;
|
||||
} else {
|
||||
$tagParams['tags' . $i] = '%' . $tag . '%';
|
||||
}
|
||||
}
|
||||
|
||||
$displayBody .= ' ) ';
|
||||
}
|
||||
}
|
||||
|
||||
if ($onlyLoggedIn) {
|
||||
$displayBody .= ' AND `display`.loggedIn = 1 ';
|
||||
}
|
||||
|
||||
$displayBody .= '
|
||||
GROUP BY display.display, display.displayId
|
||||
';
|
||||
|
||||
if ($tags != '') {
|
||||
$displayBody .= ', displaygroup.displayGroupId ';
|
||||
}
|
||||
|
||||
if ($groupBy === 'displayGroup') {
|
||||
$displayBody .= ', displaydg.displayGroupId ';
|
||||
}
|
||||
|
||||
// Sorting?
|
||||
$sortOrder = $this->gridRenderSort($sanitizedParams);
|
||||
|
||||
$order = '';
|
||||
if (is_array($sortOrder)) {
|
||||
$order .= 'ORDER BY ' . implode(',', $sortOrder);
|
||||
}
|
||||
|
||||
// Get a list of displays by filters
|
||||
$displaySql = $displaySelect . $displayBody . $order;
|
||||
$rows = [];
|
||||
|
||||
// Retrieve the disconnected/connected time from the $disconnectedDisplays array into displays
|
||||
foreach ($this->store->select($displaySql, $tagParams) as $displayRow) {
|
||||
$sanitizedDisplayRow = $this->sanitizer->getSanitizer($displayRow);
|
||||
$entry = [];
|
||||
$displayId = $sanitizedDisplayRow->getInt(('displayId'));
|
||||
$entry['displayId'] = $displayId;
|
||||
$entry['display'] = $sanitizedDisplayRow->getString(('display'));
|
||||
$entry['timeDisconnected'] = $disconnectedDisplays[$displayId]['timeDisconnected'] ?? 0 ;
|
||||
$entry['timeConnected'] = $disconnectedDisplays[$displayId]['timeConnected'] ?? round(($toDt->format('U') - $fromDt->format('U')) / $divisor, 2);
|
||||
$entry['postUnits'] = $postUnits;
|
||||
$entry['displayGroupId'] = $sanitizedDisplayRow->getInt(('displayGroupId'));
|
||||
$entry['displayGroup'] = $sanitizedDisplayRow->getString(('displayGroup'));
|
||||
$entry['avgTimeDisconnected'] = 0;
|
||||
$entry['avgTimeConnected'] = 0;
|
||||
$entry['availabilityPercentage'] = $this->getAvailabilityPercentage(
|
||||
$entry['timeConnected'],
|
||||
$entry['timeDisconnected']
|
||||
) . '%';
|
||||
|
||||
$rows[] = $entry;
|
||||
}
|
||||
|
||||
//
|
||||
// Output Results
|
||||
// --------------
|
||||
|
||||
$availabilityData = [];
|
||||
$availabilityDataConnected = [];
|
||||
$availabilityLabels = [];
|
||||
$postUnits = '';
|
||||
|
||||
if ($groupBy === 'displayGroup') {
|
||||
$rows = $this->getByDisplayGroup($rows, $sanitizedParams->getIntArray('displayGroupId', ['default' => []]));
|
||||
}
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$availabilityData[] = $row['timeDisconnected'];
|
||||
$availabilityDataConnected[] = $row['timeConnected'];
|
||||
$availabilityLabels[] = ($groupBy === 'displayGroup')
|
||||
? $row['displayGroup']
|
||||
: $row['display'];
|
||||
$postUnits = $row['postUnits'];
|
||||
}
|
||||
|
||||
// Build Chart to pass in twig file chart.js
|
||||
$chart = [
|
||||
'type' => 'bar',
|
||||
'data' => [
|
||||
'labels' => $availabilityLabels,
|
||||
'datasets' => [
|
||||
[
|
||||
'backgroundColor' => 'rgb(11, 98, 164)',
|
||||
'data' => $availabilityData,
|
||||
'label' => __('Downtime')
|
||||
],
|
||||
[
|
||||
'backgroundColor' => 'rgb(0, 255, 0)',
|
||||
'data' => $availabilityDataConnected,
|
||||
'label' => __('Uptime')
|
||||
]
|
||||
]
|
||||
],
|
||||
'options' => [
|
||||
|
||||
'scales' => [
|
||||
'xAxes' => [
|
||||
[
|
||||
'stacked' => true
|
||||
]
|
||||
],
|
||||
'yAxes' => [
|
||||
[
|
||||
'stacked' => true,
|
||||
'scaleLabel' => [
|
||||
'display' => true,
|
||||
'labelString' => $postUnits
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'legend' => [
|
||||
'display' => false
|
||||
],
|
||||
'maintainAspectRatio' => false
|
||||
]
|
||||
];
|
||||
|
||||
$metadata = [
|
||||
'periodStart' => Carbon::createFromTimestamp($fromDt->toDateTime()->format('U'))->format(DateFormatHelper::getSystemFormat()),
|
||||
'periodEnd' => Carbon::createFromTimestamp($toDt->toDateTime()->format('U'))->format(DateFormatHelper::getSystemFormat()),
|
||||
];
|
||||
|
||||
// ----
|
||||
// Return data to build chart/table
|
||||
// This will get saved to a json file when schedule runs
|
||||
return new ReportResult(
|
||||
$metadata,
|
||||
$rows,
|
||||
count($rows),
|
||||
$chart
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Availability Percentage
|
||||
* @param float $connectedTime
|
||||
* @param float $disconnectedTime
|
||||
* @return float
|
||||
*/
|
||||
private function getAvailabilityPercentage(float $connectedTime, float $disconnectedTime) : float
|
||||
{
|
||||
$connectedPercentage = $connectedTime/($connectedTime + $disconnectedTime ?: 1);
|
||||
|
||||
return abs(round($connectedPercentage * 100, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the accumulated value by display groups
|
||||
* @param array $rows
|
||||
* @param array $displayGroupIds
|
||||
* @return array
|
||||
*/
|
||||
private function getByDisplayGroup(array $rows, array $displayGroupIds = []) : array
|
||||
{
|
||||
$data = [];
|
||||
$displayGroups = [];
|
||||
|
||||
// Get the accumulated values by displayGroupId
|
||||
foreach ($rows as $row) {
|
||||
$displayGroupId = $row['displayGroupId'];
|
||||
|
||||
if (isset($displayGroups[$displayGroupId])) {
|
||||
$displayGroups[$displayGroupId]['timeDisconnected'] += $row['timeDisconnected'];
|
||||
$displayGroups[$displayGroupId]['timeConnected'] += $row['timeConnected'];
|
||||
$displayGroups[$displayGroupId]['count'] += 1;
|
||||
} else {
|
||||
$row['count'] = 1;
|
||||
$displayGroups[$displayGroupId] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
// Get all display groups or selected display groups only
|
||||
foreach ($displayGroups as $displayGroup) {
|
||||
if (!$displayGroupIds || in_array($displayGroup['displayGroupId'], $displayGroupIds)) {
|
||||
$displayGroup['timeConnected'] = round($displayGroup['timeConnected'], 2);
|
||||
$displayGroup['timeDisconnected'] = round($displayGroup['timeDisconnected'], 2);
|
||||
$displayGroup['availabilityPercentage'] = $this->getAvailabilityPercentage(
|
||||
$displayGroup['timeConnected'],
|
||||
$displayGroup['timeDisconnected']
|
||||
) . '%';
|
||||
|
||||
// Calculate the average values
|
||||
$displayGroup['avgTimeConnected'] = round($displayGroup['timeConnected'] / $displayGroup['count'], 2);
|
||||
$displayGroup['avgTimeDisconnected'] = round($displayGroup['timeDisconnected'] / $displayGroup['count'], 2);
|
||||
|
||||
$data[] = $displayGroup;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user