<?php
/**
* Created by Elements.at New Media Solutions GmbH
*
*/
namespace App\Service;
use Carbon\Carbon;
use Elements\Bundle\SeoHelperBundle\Service\PrettyUrl;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Pimcore\Cache;
use Pimcore\Model\DataObject;
use Pimcore\Model\DataObject\Lift;
use Pimcore\Model\DataObject\Listing\Concrete;
use Pimcore\Model\DataObject\Miscellaneous;
use Pimcore\Model\DataObject\Resort;
use Pimcore\Model\DataObject\Slope;
use Pimcore\Model\Document\Editable;
use Pimcore\Translation\Translator;
class FacilityService
{
const NO_INFORMATION = 0;
const OPEN = 1;
const IN_PREPARATION = 2;
const CLOSED_TECHNICAL = 3;
const CLOSED_OPERATIONAL = 5;
const CLOSED_ATMOSPHERIC = 6;
const OUT_OF_ORDER = 7;
const SLOPE_OPEN = 8;
const SLOPE_IN_PREPARATION = 9;
const SLOPE_CLOSED_AVALANCHE = 10;
const SLOPE_CLOSED_ICE = 11;
const SLOPE_CLOSED_WIND = 12;
const SLOPE_CLOSED_STORM = 13;
const SLOPE_OUT_OF_ORDER = 14;
const SLOPE_CLOSED = 15;
const CLOSED = 16;
const DAY_OF_REST = 17;
const COMPANY_VACATION = 18;
public const INFOSNOW_URL = 'https://infosnow.zbag.ch/?type=xml&id=16&report=93&ref=afed710d1d79f879e110247c8051d83a';
public const LIFETIME = 180;
public function __construct(
protected PrettyUrl $prettyUrl,
protected FormService $formService,
protected Client $client,
protected Translator $translator
) {
}
/**
* @param Editable|null $resortRelations
*
* @return array<mixed>
*/
public function getResortTabs(Editable $resortRelations = null, ?string $date = null, ?string $language = 'de'): array
{
$listing = new Resort\Listing();
$resorts = $resortRelations && !$resortRelations->isEmpty() && method_exists($resortRelations, 'getElements') ? $resortRelations->getElements() : $listing->load();
return array_map(function ($r) use ($resorts, $date, $language) {
return [
'text' => $r->getName(),
'id' => $r->getId(),
'targetId' => 'tab-' . $r->getId(),
'isActive' => $r->getId() == $resorts[0]->getId(),
'contentUrl' => $this->prettyUrl->prettyUrl([ 'tab' => $r->getId(), 'date' => $date ], 'facility_tab'),
'warnings' => $this->getInfoSnowWarnings($language, $r->getInfosnowId()),
];
}, $resorts);
}
/**
* @param string $language
* @param bool $disableCache
*
* @return array<mixed>|null
*/
public function getAllInfoSnowWarnings(string $language = 'de', bool $disableCache = false): ?array
{
$data = null;
$messageCacheKey = md5(($language . 'overviewData'));
if(!$disableCache) {
$data = Cache::load($messageCacheKey);
}
if (!$data) {
$tmpData = $this->getCachedData(self::INFOSNOW_URL, self::LIFETIME, $disableCache);
try {
$data = [];
$languageMapping = ['de' => 1, 'fr' => 2, 'en' => 3, 'it' => 4];
$xml = simplexml_load_string($tmpData);
$items = $xml->xpath('/Export/ticker/program');
foreach ($items as $item) {
$regions = null;
foreach ($item->xpath('sectors/sector') as $region) {
$regions[] = (string)$region->sector;
}
$tmpAggData = null;
foreach ($item->xpath('text') as $text) {
if ((int)$text->attributes()->lang === $languageMapping[$language]) {
$tmpAggData[] = (string)$text;
}
}
$title = null;
if (!is_array($regions)) {
$regions = ['general'];
$title = $this->translator->trans('infosnow.general.title');
}
foreach ($regions as $region) {
$key = hash('sha256', $region);
$newContent = $tmpAggData;
if (array_key_exists($key, $data) && array_key_exists('items', $data[$key])) {
$newContent = array_merge($data[$key]['items'], $tmpAggData);
}
$data[$key]['title'] = $title ?? $region;
$data[$key]['items'] = $newContent;
}
}
} catch (Exception $exception) {
}
Cache::save(
$data,
$messageCacheKey,
['resortData', 'warnings'],
self::LIFETIME
);
}
return $data;
}
/**
* @param string $language
* @param int|null $resortId
* @param bool $disableCache
*
* @return array<mixed>|null
*/
public function getInfoSnowWarnings(string $language = 'de', ?int $resortId = null, bool $disableCache = false): ?array
{
$data = null;
$messageCacheKey = md5(($language . $resortId));
if(!$disableCache) {
$data = Cache::load($messageCacheKey);
}
if (!$data) {
$tmpData = $this->getCachedData(self::INFOSNOW_URL, self::LIFETIME, $disableCache);
$data = $this->aggregateData($tmpData, $language, $resortId);
Cache::save(
$data,
$messageCacheKey,
['resortData', 'warnings'],
self::LIFETIME
);
}
return $data;
}
public function hasInfoSnowWarnings(): bool
{
$tmpData = $this->getCachedData(self::INFOSNOW_URL, self::LIFETIME);
$xml = simplexml_load_string($tmpData);
$items = $xml->xpath('/Export/ticker/program');
return !empty($items);
}
/**
* @param string $tmpData
* @param string $language
* @param int|null $resortId
*
* @return array<mixed>
*/
public function aggregateData(string $tmpData, string $language, ?int $resortId): array
{
try {
$data = [];
$languageMapping = ['de' => 1, 'fr' => 2, 'en' => 3, 'it' => 4];
$xml = simplexml_load_string($tmpData);
$items = $xml->xpath('/Export/ticker/program');
foreach ($items as $item) {
$additionalTitle = null;
try {
if (null !== $resortId) {
foreach ($item->xpath('sectors/sector') as $region) {
if ((int)$region->attributes()->mappingKey === $resortId) {
$additionalTitle = '.specified';
}
}
}
if (!empty($item->xpath('sectors')) && $additionalTitle === null) {
continue;
}
} catch (\Throwable $exception) {
continue;
}
foreach ($item->xpath('text') as $text) {
if ((int)$text->attributes()->lang === $languageMapping[$language]) {
$data[] = [
'title' => 'infosnow.' . ((string)$item->attributes()->priority === '1' ? 'warning' : 'info') . $additionalTitle,
'content' => (string)$text,
'priority' => (string)$item->attributes()->priority,
];
}
}
}
} catch (Exception $exception) {
}
return $data;
}
/**
* @return array<mixed>
*/
public function getFacilitiesCount(): array
{
$resort = new Resort\Listing();
$resort->addConditionParam('openLiftCount != 0 OR totalLiftCount != 0 OR openSlopeLength != 0 OR totalSlopeLength != 0');
$resort->setLimit(1);
$resort = $resort->current();
return [
'openLifts' => $resort ? $resort->getOpenLiftCount() : 0,
'totalLifts' => $resort ? $resort->getTotalLiftCount() : 0,
'openSlopes' => $resort ? $resort->getOpenSlopeLength() : 0,
'totalSlopes' => $resort ? $resort->getTotalSlopeLength() : 0,
];
}
/**
* @param Resort|null $resort
*
* @return array<mixed>
*
* @throws Exception
*/
public function getFacilitiesViewData(Resort $resort = null, string $date = null, string $language = 'de'): array
{
if (!$resort instanceof Resort) {
return [];
}
$conditions = [ 'area__id = :resortId' ];
$params = [ 'resortId' => $resort->getId() ];
if ($date) {
$date = Carbon::createFromFormat('Y-m-d\\T00:00:00', $date)->startOfDay();
$conditions[] = 'o_id IN (select distinct id from bundle_recurringdates_occurrences where (dateFrom >= :dateFrom and dateTo <= :dateTo))';
$params['dateFrom'] = $date->format('Y-m-d H:i:s');
$params['dateTo'] = $date->endOfDay()->format('Y-m-d H:i:s');
$slopes = [];
} else {
$slopes = $this->filterAndSortListing(new Slope\Listing(), $conditions, $params);
}
$lifts = $this->filterAndSortListing(new Lift\Listing(), $conditions, $params);
$misc = $this->filterAndSortListing(new Miscellaneous\Listing(), $conditions, $params);
$salesPoints = $this->filterAndSortListing(new Miscellaneous\Listing(), $conditions, $params, true);
$lifts = $this->customSortFunction($lifts->load());
$mappedLiftTypes = [];
foreach ($lifts as $lift) {
$mappedLiftType = $this->getTypeImageMapping($lift);
if (!in_array($mappedLiftType, $mappedLiftTypes)) {
$mappedLiftTypes[] = $mappedLiftType;
}
}
return [
'modificationDate' => $this->getModificationDate(),
'snippet' => $resort->getSnippet(),
'liftTypes' => $mappedLiftTypes,
'slopeTypes' => $slopes ? $this->formService->getRelatedObjects($slopes, 'facilityType') : [],
'lifts' => $lifts,
'slopes' => $slopes ? $this->customSortFunction($slopes->load()) : [],
'misc' => $this->customSortFunction($misc->load()),
'salesPoints' => $this->customSortFunction($salesPoints->load()),
'weather' => DataObject\Weather::getByArea($resort, 1),
'warnings' => $this->getInfoSnowWarnings($language, (int)$resort->getInfosnowId()),
];
}
public function getTypeImageMapping(Lift $lift): string
{
$type = $lift->getFacilityType();
if (in_array($type, ['4', '5', '6', '7'])) {
$type = '9';
}
return $type;
}
public function isOpen(?DataObject $object = null): bool
{
if ($object && method_exists($object, 'getStatus') &&
($object->getStatus() == FacilityService::OPEN || $object->getStatus() == FacilityService::SLOPE_OPEN)) {
return true;
}
return false;
}
/**
* @param Concrete $listing
* @param array<mixed> $conditions
* @param array<mixed> $params
* @param bool $salesPoint
*
* @return Concrete
*/
private function filterAndSortListing(Concrete $listing, array $conditions = [], array $params = [], bool $salesPoint = false): Concrete
{
if ($listing instanceof Miscellaneous\Listing) {
$listing->addConditionParam('showOnWebsite = 1');
if($salesPoint) {
$listing->addConditionParam('isSalesPoint = 1');
} else {
$listing->addConditionParam('isSalesPoint IS NULL OR isSalesPoint != 1');
}
} else {
$listing->addConditionParam('hideOnWebsite IS NULL OR hideOnWebsite != 1');
}
if ($listing instanceof Lift\Listing) {
if (!array_key_exists('dateFrom', $params) && !array_key_exists('dateTo', $params) || array_key_exists('dateFrom', $params) && Carbon::parse($params['dateFrom'])->isToday()) {
$conditions[] = 'o_id IN (select distinct id from bundle_recurringdates_occurrences where (dateFrom >= :dateFrom and dateTo <= :dateTo))';
$params['dateFrom'] = Carbon::now()->startOfDay()->format('Y-m-d H:i:s');
$params['dateTo'] = Carbon::now()->endOfDay()->format('Y-m-d H:i:s');
}
} else {
$listing->addConditionParam('status != :outOfOrder AND status != :slopeOutOfOrder', [
'outOfOrder' => FacilityService::OUT_OF_ORDER,
'slopeOutOfOrder' => FacilityService::SLOPE_OUT_OF_ORDER,
]);
if ($listing instanceof Slope\Listing) {
$listing->addConditionParam('status != :noInformation', [
'noInformation' => FacilityService::NO_INFORMATION,
]);
}
}
if ($conditions != []) {
$listing->addConditionParam(implode(' AND ', $conditions), $params);
}
$listing->setOrderKey(['status', 'sort']);
$listing->setOrder('ASC');
return $listing;
}
private function getModificationDate(): int
{
$lifts = new Lift\Listing();
$lifts->addConditionParam('hideOnWebsite IS NULL OR hideOnWebsite != 1');
$lifts->setOrderKey('o_modificationDate');
$lifts->setOrder('DESC');
$lifts->setLimit(1);
$lastModificatedLift = $lifts->current();
return $lastModificatedLift->getModificationDate();
}
/**
* @param array<mixed> $objects
*
* @return array<mixed>
*/
private function customSortFunction(array $objects): array
{
usort($objects, function ($a, $b) {
$statusA = $a->getStatus();
$statusB = $b->getStatus();
if ($statusA == 0) {
return 1;
}
if ($statusB == 0) {
return -1;
}
$statusComparison = (int)$statusA - (int)$statusB;
if ($statusComparison !== 0) {
return $statusComparison;
}
return $a->getSort() - $b->getSort();
});
return $objects;
}
private function getCachedData(string $uri, ?int $lifetime = null, bool $disableCache = false): mixed
{
$data = null;
$cacheKey = md5($uri);
if (!$disableCache) {
$data = Cache::load($cacheKey);
}
if (!$data) {
try {
$response = $this->client->request('GET', $uri);
if ($response->getStatusCode() === 200) {
$data = $response->getBody()->getContents();
Cache::save(
$data,
$cacheKey,
['rss', 'warnings'],
$lifetime
);
}
} catch (GuzzleException $e) {
if (\Pimcore::inDebugMode()) {
p_r($e);
}
}
}
return $data;
}
}