<?php
namespace App\Controller\Back;
use App\Client\Algolia\Exception\ClientException;
use App\Entity\BackUser;
use App\Entity\BackUserLog;
use App\Entity\Contact;
use App\Entity\Newsletter;
use App\Entity\Page;
use App\Entity\Utils\History;
use App\Entity\Utils\Notification;
use App\Form\FeaturesType;
use App\Form\Model\Features;
use App\Form\Model\Settings;
use App\Form\SettingsType;
use App\Repository\BackUserLogRepository;
use App\Repository\ContactRepository;
use App\Repository\NewsletterRepository;
use App\Repository\PageRepository;
use App\Repository\Utils\HistoryRepository;
use App\Repository\Utils\NotificationRepository;
use App\Service\Algolia\AnalyticsService as AlgoliaAnalyticsService;
use App\Service\Google\AnalyticsService;
use App\Utils\FormHelper;
use Doctrine\Persistence\ManagerRegistry;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Cache\InvalidArgumentException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
#[Route('/back')]
class StaticController extends AbstractController
{
/**
* StaticController constructor.
*/
public function __construct(
protected AnalyticsService $googleAnalyticsService,
protected AlgoliaAnalyticsService $algoliaAnalyticsService,
protected TranslatorInterface $translator,
protected ManagerRegistry $registry
) {
}
/**
* @throws InvalidArgumentException
* @throws \Exception
*/
#[Route('', name: 'back_static_index')]
public function index(): Response
{
/** @var BackUser $user */
$user = $this->getUser();
$today = new \DateTime();
/** @var HistoryRepository $historyRepository */
$historyRepository = $this->registry->getRepository(History::class);
/** @var BackUserLogRepository $userLogRepository */
$userLogRepository = $this->registry->getRepository(BackUserLog::class);
/** @var NotificationRepository $notificationRepository */
$notificationRepository = $this->registry->getRepository(Notification::class);
/** @var ContactRepository $contactRepository */
$contactRepository = $this->registry->getRepository(Contact::class);
/** @var NewsletterRepository $newsletterRepository */
$newsletterRepository = $this->registry->getRepository(Newsletter::class);
/** @var PageRepository $pageRepository */
$pageRepository = $this->registry->getRepository(Page::class);
$histories = $historyRepository->getLastEntries($today);
$lastConnections = $userLogRepository->countConnectionsByUserOnCurrentMonth($user->getId());
$notificationCpt = $notificationRepository->countByUser($user->getId());
$contactCpt = $contactRepository->countContact();
$pageCpt = $pageRepository->countPage();
$newsletterCpt = $newsletterRepository->countSubscription();
$settings = new Settings();
$analytics = null;
if ($settings->getGoogleAnalyticsAuthFile()) {
$analytics = $this->googleAnalyticsService->getFullAnalytics('30daysAgo', 'today');
}
return $this->render('back/static/index.html.twig', [
'analytics' => $analytics,
'histories' => $histories,
'lastConnections' => $lastConnections[0][1],
'notificationCpt' => $notificationCpt[0][1],
'contactCpt' => $contactCpt[1],
'pageCpt' => $pageCpt[1],
'newsletterCpt' => $newsletterCpt[1],
]);
}
#[Route('/insights/search', name: 'back_static_insights_search')]
public function search(): Response
{
$allIndexes = $this->algoliaAnalyticsService->getAllIndexes();
return $this->render('back/static/search.html.twig', [
'allIndexes' => $allIndexes,
]);
}
#[Route('/insights/analytics', name: 'back_static_insights_analytics')]
public function analytics(): Response
{
$settings = new Settings();
if (!$settings->getGoogleAnalyticsAuthFile()) {
throw $this->createNotFoundException('Analytics not configured !');
}
return $this->render('back/static/analytics.html.twig');
}
#[Route('/insights/stats', name: 'back_static_insights_stats')]
public function stats(): Response
{
return $this->render('back/custom/static/stats.html.twig', [
]);
}
/**
* @throws InvalidArgumentException
* @throws \Exception
*/
#[Route('/analytics/kpi', name: 'back_static_analytics_kpi')]
public function analyticsKpiMetrics(Request $request): JsonResponse
{
$dateStart = $request->get('dateStart', '2018-04-08');
$dateEnd = $request->get('dateEnd', '2018-05-08');
$metrics = [
'sessions',
'users',
'sessionsPerUser',
'bounceRate',
'pageviews',
'pageviewsPerSession',
'avgTimeOnPage',
'percentNewSessions',
'exitRate',
];
$data = $this->googleAnalyticsService->getMetricsReport($metrics, $dateStart, $dateEnd);
return new JsonResponse($data);
}
/**
* @throws InvalidArgumentException
* @throws \Exception
*/
#[Route('/analytics/speed', name: 'back_static_analytics_speed')]
public function analyticsSpeedMetrics(Request $request): JsonResponse
{
$dateStart = $request->get('dateStart', '2018-04-08');
$dateEnd = $request->get('dateEnd', '2018-05-08');
$metrics = [
'avgPageLoadTime',
'avgDomainLookupTime',
'avgPageDownloadTime',
'avgRedirectionTime',
'avgServerConnectionTime',
'avgServerResponseTime',
'avgDomInteractiveTime',
'avgDomContentLoadedTime',
];
$data = $this->googleAnalyticsService->getMetricsReport($metrics, $dateStart, $dateEnd);
return new JsonResponse($data);
}
/**
* @throws InvalidArgumentException
* @throws \Exception
*/
#[Route('/analytics/chart', name: 'back_static_analytics_chart')]
public function analyticsChart(Request $request): JsonResponse
{
$sort = [];
$data = [];
$metricsStr = $request->get('metrics', 'users'); // maybe null instead of user ?
$dimensionsStr = $request->get('dimensions', 'date'); // maybe null instead of date ?
$sortStr = $request->get('sort');
$order = $request->get('order');
$limit = $request->get('limit');
if (null === $metricsStr) {
new JsonResponse([]);
}
$dateStart = $request->get('dateStart', '2018-04-08');
$dateEnd = $request->get('dateEnd', '2018-05-08');
$getPrevious = 'true' === $request->get('getPrevious');
$start = \DateTime::createFromFormat('Y-m-d', $dateStart);
$end = \DateTime::createFromFormat('Y-m-d', $dateEnd);
$diff = $end->diff($start)->format('%a');
if ($getPrevious) {
$previousEnd = \DateTime::createFromFormat('Y-m-d', $dateStart);
$previousEnd = $previousEnd->sub(new \DateInterval('P1D'));
$previousStart = new \DateTime($previousEnd->format('Y-m-d'));
$previousStart = $previousStart->sub(new \DateInterval('P'.$diff.'D'));
}
$metrics = explode(',', (string) $metricsStr);
$dimensions = explode(',', (string) $dimensionsStr);
if (null !== $sortStr) {
$sort['fields'] = explode(',', (string) $sortStr);
$sort['order'] = $order;
} else {
$sort = null;
}
if ($getPrevious) {
$result = $this->googleAnalyticsService->getDataDateRangeMetricsDimensions($previousStart->format('Y-m-d'), $dateEnd, $metrics, $dimensions, $sort, null, null, $limit);
[$data['previous'], $data['current']] = array_chunk($result, ceil(count($result) / 2));
} else {
$data = $this->googleAnalyticsService->getDataDateRangeMetricsDimensions($dateStart, $dateEnd, $metrics, $dimensions, $sort, null, null, $limit);
}
return new JsonResponse($data);
}
#[Route('/activity', name: 'back_static_activity')]
public function activity(): Response
{
return $this->render('back/static/activity.html.twig', [
'url' => $this->generateUrl('back_activity_paginate'),
]);
}
/**
* @throws \Exception
*/
#[Route('/activity/paginate', name: 'back_activity_paginate')]
public function paginateActivity(Request $request): Response
{
$today = new \DateTime();
$nextDate = null;
$date = $request->get('date', $today->format('Ymd'));
/** @var HistoryRepository $repository */
$repository = $this->registry->getRepository(History::class);
$date = \DateTime::createFromFormat('Ymd', $date);
$items = $repository->paginate($date);
return $this->render('back/static/partials/activity/block.html.twig', [
'date' => $date,
'nextDate' => $nextDate,
'items' => $items,
]);
}
#[Route('/members-logs', name: 'back_static_log')]
public function log(): Response
{
return $this->render('back/static/user-log.html.twig', [
'url' => $this->generateUrl('back_log_paginate'),
]);
}
/**
* @throws \Exception
*/
#[Route('/members-logs/paginate', name: 'back_log_paginate')]
public function paginateLog(Request $request): Response
{
$today = new \DateTime();
$nextDate = null;
$date = $request->get('date', $today->format('Ymd'));
/** @var BackUserLogRepository $repository */
$repository = $this->registry->getRepository(BackUserLog::class);
$date = \DateTime::createFromFormat('Ymd', $date);
$items = $repository->paginate($date);
return $this->render('back/static/partials/user-log/block.html.twig', [
'date' => $date,
'nextDate' => $nextDate,
'items' => $items,
]);
}
#[Route('/settings', name: 'back_static_settings')]
public function settings(Request $request): JsonResponse|Response
{
$settings = new Settings();
$this->denyAccessUnlessGranted('edit', $settings);
$form = $this->createForm(SettingsType::class, $settings);
if ('POST' == $request->getMethod()) {
$data = FormHelper::getDataDecoded($request->getContent(), 'json');
$form->setData($data['settings']);
$form->submit($data['settings']);
$featuresValid = true;
if (isset($data['features'])) {
$features = new Features();
$this->denyAccessUnlessGranted('edit', $features);
$formFeatures = $this->createForm(FeaturesType::class, $features);
$formFeatures->setData($data['features']);
$formFeatures->submit($data['features']);
if ($formFeatures->isSubmitted() && $formFeatures->isValid()) {
$features->exportFile();
} else {
$featuresValid = false;
}
}
if ($form->isSubmitted() && $form->isValid() && $featuresValid) {
$settings->exportFile();
return new JsonResponse([
'success' => true,
]);
}
return new JsonResponse([
'success' => false,
'error' => [
'title' => $this->translator->trans('form.error.main.title'),
'description' => $this->translator->trans('form.error.main.description'),
],
'errors' => FormHelper::getErrorMessages($form),
]);
}
$form = FormHelper::getData($form);
// replace this example code with whatever you need
return $this->render('back/static/settings.html.twig', [
'form' => $form['settings']['children'],
]);
}
#[Route('/features/form', name: 'back_static_features')]
public function features(Request $request): JsonResponse|Response
{
$password = $request->query->get('password', null);
$features = new Features();
$this->denyAccessUnlessGranted('edit', $features);
if (!$password || !$features->isValidPassword($password)) {
return new JsonResponse([
'success' => false,
'error' => [
'title' => 'Access denied',
'description' => 'Either you enter a wrong password or you don\'t have enough rights to access this part...',
],
]);
}
$form = $this->createForm(FeaturesType::class, $features);
$form = FormHelper::getData($form);
// replace this example code with whatever you need
return new JsonResponse([
'success' => true,
'view' => $this->renderView('back/static/partials/features.html.twig', [
'form' => $form['features']['children'],
]),
]);
}
#[Route('/help', name: 'back_static_help')]
public function help(): Response
{
// replace this example code with whatever you need
return $this->render('back/custom/static/help.html.twig');
}
#[Route('/about', name: 'back_static_about')]
public function about(): Response
{
// replace this example code with whatever you need
return $this->render('back/static/about.html.twig');
}
#[Route('/notification-center', name: 'back_static_notification')]
public function notification(): Response
{
// replace this example code with whatever you need
return $this->render('back/static/notification.html.twig', [
'url' => $this->generateUrl('back_notification_paginate'),
]);
}
#[Route('/notification-center/paginate', name: 'back_notification_paginate')]
public function paginateNotification(Request $request): Response
{
$page = $request->get('page', 0);
$limit = $request->get('limit', 30);
$user = $this->getUser();
/** @var NotificationRepository $repository */
$repository = $this->registry->getRepository(Notification::class);
$items = $repository->paginate($user->getId(), $limit, $page * $limit);
return $this->render('back/static/partials/notification/block.html.twig', [
'items' => $items,
]);
}
/**
* @throws \Exception
*/
public function findNextDate(\DateTime $date): \DateTime
{
$repository = $this->registry->getRepository(History::class);
$nextDate = $date;
$nextDate->sub(new \DateInterval('P1D'));
/** @var HistoryRepository $repository */
$items = $repository->paginate($nextDate);
if ($items) {
return $nextDate->add(new \DateInterval('P1D'));
}
return $this->findNextDate($nextDate);
}
#[Route('/preview-mode', name: 'back_static_preview_mode')]
public function previewMode(Request $request): Response
{
// replace this example code with whatever you need
return $this->render('back/static/preview-mode.html.twig', [
'url' => $request->get('url'),
]);
}
/**
* @throws ClientException
* @throws GuzzleException
* @throws InvalidArgumentException
* @throws \Exception
*/
#[Route('/search/performances', name: 'back_static_search_performances')]
public function algoliaGlobalPerformances(Request $request): JsonResponse
{
$data = $this->algoliaAnalyticsService->getGlobalPerformances(
new \DateTime($request->get('dateStart')),
new \DateTime($request->get('dateEnd'))
);
return $this->json(
$data
);
}
/**
* @throws ClientException
* @throws GuzzleException
* @throws InvalidArgumentException
* @throws \Exception
*/
#[Route('/search/daily', name: 'back_static_search_daily')]
public function algoliaSearchPerformances(Request $request): JsonResponse
{
$data = $this->algoliaAnalyticsService->getSearchPerformances(
new \DateTime($request->get('dateStart')),
new \DateTime($request->get('dateEnd'))
);
return new JsonResponse(
$data
);
}
/**
* @throws ClientException
* @throws GuzzleException
* @throws \Exception
*/
#[Route('/search/countries', name: 'back_static_search_countries')]
public function algoliaSearchByCountries(Request $request): JsonResponse
{
$data = $this->algoliaAnalyticsService->getTopCountries(
new \DateTime($request->get('dateStart')),
new \DateTime($request->get('dateEnd'))
);
return new JsonResponse($data);
}
/**
* @throws \Exception
*/
#[Route('/prospects/daily', name: 'back_prospects_daily')]
public function prospectsDaily(Request $request): JsonResponse
{
/** @var ContactRepository $contactRepository */
$contactRepository = $this->registry->getRepository(Contact::class);
/** @var NewsletterRepository $newsletterRepository */
$newsletterRepository = $this->registry->getRepository(Newsletter::class);
$from = new \DateTime($request->get('dateStart'));
$to = new \DateTime($request->get('dateEnd'));
$contacts = $contactRepository->findContactsPerDay($from, $to);
$newsletters = $newsletterRepository->findNewslettersPerDay($from, $to);
// arrange datas
$interval = $from->diff($to);
$diff = $interval->format('%a');
$contactDatas = [];
$newsletterDatas = [];
for ($i = 0; $i <= $diff; ++$i) {
$date = new \DateTime($from->format('Y-m-d'));
$date = $date->modify('+'.$i.' day');
$df = $date->format('Ymd');
$contactDatas[$df] = 0;
$newsletterDatas[$df] = 0;
foreach ($contacts as $item) {
if ($item['date'] == $df) {
$contactDatas[$df] = intval($item['value']);
}
}
foreach ($newsletters as $item) {
if ($item['date'] == $df) {
$newsletterDatas[$df] = intval($item['value']);
}
}
}
$data = [
'contacts' => $contactDatas,
'newsletters' => $newsletterDatas,
];
return new JsonResponse(
$data
);
}
}