getSyncLongExecutionTime()); $station = $request->getStation(); $stationTz = $station->getTimezoneObject(); $params = $request->getQueryParams(); $isLive = empty($params['start']); $now = CarbonImmutable::now($stationTz); $qb = $em->createQueryBuilder() ->select('l') ->from(Entity\Listener::class, 'l') ->where('l.station = :station') ->setParameter('station', $station) ->orderBy('l.timestamp_start', 'ASC'); if ($isLive) { $range = 'live'; $startTimestamp = $now->getTimestamp(); $endTimestamp = $now->getTimestamp(); $qb = $qb->andWhere('l.timestamp_end = 0'); } else { $startString = $params['start']; if (10 === strlen($startString)) { $startString .= ' 00:00:00'; } $start = CarbonImmutable::parse($startString, $stationTz); $startTimestamp = $start->getTimestamp(); $endString = $params['end'] ?? $params['start']; if (10 === strlen($endString)) { $endString .= ' 23:59:59'; } $end = CarbonImmutable::parse($endString, $stationTz); $endTimestamp = $end->getTimestamp(); $range = $start->format('Y-m-d_H-i-s') . '_to_' . $end->format('Y-m-d_H-i-s'); $qb = $qb->andWhere('l.timestamp_start < :time_end') ->andWhere('(l.timestamp_end = 0 OR l.timestamp_end > :time_start)') ->setParameter('time_start', $startTimestamp) ->setParameter('time_end', $endTimestamp); } /** @var Locale $locale */ $locale = $request->getAttribute(ServerRequest::ATTR_LOCALE); $mountNames = $mountRepo->getDisplayNames($station); $remoteNames = $remoteRepo->getDisplayNames($station); $listenersIterator = ReadOnlyBatchIteratorAggregate::fromQuery($qb->getQuery(), 250); /** @var Entity\Api\Listener[] $listeners */ $listeners = []; $listenersByHash = []; $groupByUnique = ('false' !== ($params['unique'] ?? 'true')); foreach ($listenersIterator as $listener) { /** @var Entity\Listener $listener */ $listenerStart = $listener->getTimestampStart(); if ($isLive) { $listenerEnd = $now->getTimestamp(); } else { if ($listenerStart < $startTimestamp) { $listenerStart = $startTimestamp; } $listenerEnd = $listener->getTimestampEnd(); if (0 === $listenerEnd || $listenerEnd > $endTimestamp) { $listenerEnd = $endTimestamp; } } $hash = $listener->getListenerHash(); if ($groupByUnique && isset($listenersByHash[$hash])) { $listenersByHash[$hash]['intervals'][] = [ 'start' => $listenerStart, 'end' => $listenerEnd, ]; continue; } $userAgent = $listener->getListenerUserAgent(); $dd = $deviceDetector->parse($userAgent); if ($dd->isBot()) { $clientBot = (array)$dd->getBot(); $clientBotName = $clientBot['name'] ?? 'Unknown Crawler'; $clientBotType = $clientBot['category'] ?? 'Generic Crawler'; $client = $clientBotName . ' (' . $clientBotType . ')'; } else { $clientInfo = (array)$dd->getClient(); $clientBrowser = $clientInfo['name'] ?? 'Unknown Browser'; $clientVersion = $clientInfo['version'] ?? '0.00'; $clientOsInfo = (array)$dd->getOs(); $clientOs = $clientOsInfo['name'] ?? 'Unknown OS'; $client = $clientBrowser . ' ' . $clientVersion . ', ' . $clientOs; } $api = new Entity\Api\Listener(); $api->ip = $listener->getListenerIp(); $api->user_agent = $userAgent; $api->hash = $hash; $api->client = $client; $api->is_mobile = $dd->isMobile(); if ($listener->getMountId()) { $mountId = $listener->getMountId(); $api->mount_is_local = true; $api->mount_name = $mountNames[$mountId]; } elseif ($listener->getRemoteId()) { $remoteId = $listener->getRemoteId(); $api->mount_is_local = false; $api->mount_name = $remoteNames[$remoteId]; } $api->location = $geoLite->getLocationInfo($api->ip, $locale->getLocale()); if ($groupByUnique) { $listenersByHash[$hash] = [ 'api' => $api, 'intervals' => [ [ 'start' => $listenerStart, 'end' => $listenerEnd, ], ], ]; } else { $api->connected_on = $listenerStart; $api->connected_until = $listenerEnd; $api->connected_time = $listenerEnd - $listenerStart; $listeners[] = $api; } } if ($groupByUnique) { foreach ($listenersByHash as $listenerInfo) { $intervals = (array)$listenerInfo['intervals']; $startTime = $now->getTimestamp(); $endTime = 0; foreach ($intervals as $interval) { $startTime = min($interval['start'], $startTime); $endTime = max($interval['end'], $endTime); } /** @var Entity\Api\Listener $api */ $api = $listenerInfo['api']; $api->connected_on = $startTime; $api->connected_until = $endTime; $api->connected_time = Entity\Listener::getListenerSeconds($intervals); $listeners[] = $api; } } $format = $params['format'] ?? 'json'; if ('csv' === $format) { return $this->exportReportAsCsv( $response, $station, $listeners, $station->getShortName() . '_listeners_' . $range . '.csv' ); } return $response->withJson($listeners); } /** * @param Response $response * @param Entity\Station $station * @param Entity\Api\Listener[] $listeners * @param string $filename */ protected function exportReportAsCsv( Response $response, Entity\Station $station, array $listeners, string $filename ): ResponseInterface { $tempFile = tmpfile() ?: throw new RuntimeException('Could not create temp file.'); $csv = Writer::createFromStream($tempFile); $tz = $station->getTimezoneObject(); $csv->insertOne( [ 'IP', 'Start Time', 'End Time', 'Seconds Connected', 'User Agent', 'Client', 'Is Mobile', 'Mount Type', 'Mount Name', 'Location', 'Country', 'Region', 'City', ] ); foreach ($listeners as $listener) { $startTime = CarbonImmutable::createFromTimestamp($listener->connected_on, $tz); $endTime = CarbonImmutable::createFromTimestamp($listener->connected_until, $tz); $export_row = [ $listener->ip, $startTime->toIso8601String(), $endTime->toIso8601String(), $listener->connected_time, $listener->user_agent, $listener->client, $listener->is_mobile ? 'True' : 'False', ]; if ('' === $listener->mount_name) { $export_row[] = 'Unknown'; $export_row[] = 'Unknown'; } else { $export_row[] = ($listener->mount_is_local) ? 'Local' : 'Remote'; $export_row[] = $listener->mount_name; } $location = $listener->location; if ('success' === $location['status']) { $export_row[] = $location['region'] . ', ' . $location['country']; $export_row[] = $location['country']; $export_row[] = $location['region']; $export_row[] = $location['city']; } else { $export_row[] = $location['message'] ?? 'N/A'; $export_row[] = ''; $export_row[] = ''; $export_row[] = ''; } $csv->insertOne($export_row); } $stream = new Stream($tempFile); return $response->renderStreamAsFile($stream, 'text/csv', $filename); } }