diff --git a/lib/Report/ProofOfPlay.php b/lib/Report/ProofOfPlay.php index 7c42a359f3..dd89c50897 100644 --- a/lib/Report/ProofOfPlay.php +++ b/lib/Report/ProofOfPlay.php @@ -33,6 +33,7 @@ use Xibo\Factory\MediaFactory; use Xibo\Factory\ReportScheduleFactory; use Xibo\Factory\DisplayGroupFactory; +use Xibo\Factory\TagFactory; use Xibo\Helper\ApplicationState; use Xibo\Helper\DateFormatHelper; use Xibo\Helper\SanitizerService; @@ -75,6 +76,11 @@ class ProofOfPlay implements ReportInterface */ private $displayGroupFactory; + /** + * @var TagFactory + */ + private $tagFactory; + /** * @var SanitizerService */ @@ -101,6 +107,7 @@ public function setFactories(ContainerInterface $container) $this->layoutFactory = $container->get('layoutFactory'); $this->reportScheduleFactory = $container->get('reportScheduleFactory'); $this->displayGroupFactory = $container->get('displayGroupFactory'); + $this->tagFactory = $container->get('tagFactory'); $this->sanitizer = $container->get('sanitizerService'); return $this; @@ -470,7 +477,9 @@ public function getResults(SanitizerInterface $sanitizedParams) $tags, $tagsType, $exactTags, - $operator + $operator, + $groupBy, + $displayGroupIds ); } else { $result = $this->getProofOfPlayReportMySql( @@ -952,6 +961,8 @@ private function getBodyForTagsType($tagsType, $exclude) :string * @param $tags string * @param $tagsType string * @param $exactTags mixed + * @param $groupBy string + * @param $displayGroupIds array * @return array[array result, date periodStart, date periodEnd, int count, int totalStats] * @throws InvalidArgumentException * @throws \Xibo\Support\Exception\GeneralException @@ -968,7 +979,9 @@ private function getProofOfPlayReportMongoDb( $tags, $tagsType, $exactTags, - $operator + $operator, + $groupBy, + $displayGroupIds ) { $fromDt = new UTCDateTime($filterFromDt->format('U')*1000); $toDt = new UTCDateTime($filterToDt->format('U')*1000); @@ -1196,11 +1209,21 @@ private function getProofOfPlayReportMongoDb( $entry['widgetId'] = $row['widgetId']; $entry['mediaId'] = $row['mediaId']; $entry['tag'] = $row['eventName']; + $entry['displayGroupId'] = ''; + $entry['displayGroup'] = ''; + $entry['tagId'] = ''; + $entry['tagName'] = ''; $rows[] = $entry; } } + if ($groupBy === 'tag') { + $rows = $this->groupByTagMongoDb($rows, $tagsType); + } else if ($groupBy === 'displayGroup') { + $rows = $this->groupByDisplayGroupMongoDb($rows, $displayGroupIds); + } + return [ 'periodStart' => $filterFromDt->format(DateFormatHelper::getSystemFormat()), 'periodEnd' => $filterToDt->format(DateFormatHelper::getSystemFormat()), @@ -1228,4 +1251,115 @@ private function groupByTagType(string $tagType) : string `lktagdisplaygroup` AS taglink ON taglink.displaygroupId = displaydg.displaygroupId', }; } + + /** + * Group by display group in MongoDB + * @param array $rows + * @param array $displayIds + * @return array + * @throws NotFoundException + */ + private function groupByDisplayGroupMongoDb(array $rows, array $displayIds = []) : array + { + $data = []; + + // Get the display groups + foreach ($rows as $row) { + foreach ($this->displayGroupFactory->query(null, ['displayId' => $row['displayId']]) as $dg) { + + // Do we have a filter? + if (!$displayIds || in_array($dg->displayGroupId, $displayIds)) { + // Create a temporary key to group by multiple columns at once + // and save memory instead of checking each column recursively + $key = $dg->displayGroupId . '_' . $row['layoutId'] . '_' . $row['mediaId'] . '_' . + $row['tag'] . '_' . $row['widgetId'] . '_' . $row['parentCampaignId'] . '_' . $row['type']; + + if (!isset($data[$key])) { + // Since we already have the display group as the grouping option, we can remove the display info + $row['display'] = null; + $row['displayId'] = null; + $row['displayGroupId'] = $dg->displayGroupId; + $row['displayGroup'] = $dg->displayGroup; + + $data[$key] = $row; + } else { + $data[$key]['duration'] += $row['duration']; + $data[$key]['numberPlays'] += $row['numberPlays']; + } + } + } + } + + return $data; + } + + /** + * Group by tag in MongoDB + * @param array $rows + * @param string $tagsType + * @return array + */ + private function groupByTagMongoDb(array $rows, string $tagsType) : array + { + $data = []; + $tags = $this->filterByTagType($tagsType); + $type = match ($tagsType) { + 'media' => 'mediaId', + 'layout' => 'layoutId', + 'dg' => 'displayId', + };; + + foreach ($rows as $row) { + foreach ($tags as $tag) { + if ($row[$type] == $tag['entityId']) { + // Create a temporary key to group by multiple columns at once + // and save memory instead of checking each column recursively + $key = $tag['tagId'] . '_' . $row['layoutId'] . '_' . $row['mediaId'] . '_' . + $row['tag'] . '_' . $row['widgetId'] . '_' . $row['parentCampaignId'] . '_' . $row['type']; + + if (!isset($data[$key])) { + // Since we already have the tags as the grouping option, we can remove the display info + $row['display'] = null; + $row['displayId'] = null; + $row['tagName'] = $tag['tag']; + $row['tagId'] = $tag['tagId']; + + $data[$key] = $row; + } else { + $data[$key]['duration'] += $row['duration']; + $data[$key]['numberPlays'] += $row['numberPlays']; + } + } + } + } + + return $data; + } + + /** + * @param string $tagsType + * @return array + */ + private function filterByTagType(string $tagsType): array + { + $tags = []; + $filter = match ($tagsType) { + 'media' => 'Media', + 'layout' => 'Layout', + 'dg' => 'Display', + }; + + // What type of tags are looking for? + foreach ($this->tagFactory->query() as $tag) { + foreach ($this->tagFactory->getAllLinks(null, ['tagId' => $tag->tagId]) as $filteredTag) { + if ($filteredTag['type'] == $filter) { + $filteredTag['tagId'] = $tag->tagId; + $filteredTag['tag'] = $tag->tag; + $tags[] = $filteredTag; + } + } + } + + return $tags; + } } diff --git a/reports/proofofplay-report-form.twig b/reports/proofofplay-report-form.twig index c9d611e2ac..f8be3c39ae 100644 --- a/reports/proofofplay-report-form.twig +++ b/reports/proofofplay-report-form.twig @@ -390,7 +390,7 @@ let totalNumberPlaysPage = api.column(13, { page: 'current'}).data().reduce(function (a, b) { return a + b; }, 0); - let totalDurationPage = api.column(13, { page: 'current'}).data().reduce(function (a, b) { + let totalDurationPage = api.column(15, { page: 'current'}).data().reduce(function (a, b) { return a + b; }, 0);