Completed
Push — 2.1 ( d7139b...4f94f6 )
by Rafał
09:57
created

AnalyticsExportController::processRoutesToIds()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Superdesk Web Publisher Core Bundle.
7
 *
8
 * Copyright 2020 Sourcefabric z.ú. and contributors.
9
 *
10
 * For the full copyright and license information, please see the
11
 * AUTHORS and LICENSE files distributed with this source code.
12
 *
13
 * @copyright 2020 Sourcefabric z.ú
14
 * @license http://www.superdesk.org/license
15
 */
16
17
namespace SWP\Bundle\CoreBundle\Controller;
18
19
use Doctrine\Common\Cache\Cache;
20
use Hoa\Mime\Mime;
21
use League\Flysystem\Filesystem;
22
use SWP\Bundle\ContentBundle\Model\ArticleAuthorInterface;
23
use SWP\Bundle\ContentBundle\Model\RouteRepositoryInterface;
24
use SWP\Bundle\CoreBundle\AnalyticsExport\CsvReportFileLocationResolver;
25
use SWP\Bundle\CoreBundle\AnalyticsExport\ExportAnalytics;
26
use SWP\Bundle\CoreBundle\Context\CachedTenantContextInterface;
27
use SWP\Bundle\CoreBundle\Form\Type\ExportAnalyticsType;
28
use SWP\Bundle\CoreBundle\Model\AnalyticsReport;
29
use SWP\Bundle\CoreBundle\Model\AnalyticsReportInterface;
30
use SWP\Bundle\CoreBundle\Model\UserInterface;
31
use SWP\Component\Common\Criteria\Criteria;
32
use SWP\Component\Common\Model\DateTime as PublisherDateTime;
33
use SWP\Component\Common\Pagination\PaginationData;
34
use SWP\Component\Common\Response\ResourcesListResponse;
35
use SWP\Component\Common\Response\ResourcesListResponseInterface;
36
use SWP\Component\Common\Response\ResponseContext;
37
use SWP\Component\Common\Response\SingleResourceResponse;
38
use SWP\Component\Common\Response\SingleResourceResponseInterface;
39
use SWP\Component\Storage\Model\PersistableInterface;
40
use SWP\Component\Storage\Repository\RepositoryInterface;
41
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
42
use Symfony\Component\HttpFoundation\Request;
43
use Symfony\Component\HttpFoundation\Response;
44
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
45
use Symfony\Component\Routing\Annotation\Route;
46
47
class AnalyticsExportController extends AbstractController
48
{
49
    /** @var Cache */
50
    protected $cacheProvider;
51
52
    /** @var RepositoryInterface */
53
    protected $analyticsReportRepository;
54
55
    /** @var Filesystem */
56
    protected $filesystem;
57
58
    /** @var CsvReportFileLocationResolver */
59
    protected $csvReportFileLocationResolver;
60
61
    /** @var CachedTenantContextInterface */
62
    protected $cachedTenantContext;
63
64
    /** @var RouteRepositoryInterface */
65
    protected $routeRepository;
66
67
    public function __construct(
68
        Cache $cacheProvider,
69
        RepositoryInterface $analyticsReportRepository,
70
        Filesystem $filesystem,
71
        CsvReportFileLocationResolver $csvReportFileLocationResolver,
72
        CachedTenantContextInterface $cachedTenantContext,
73
        RouteRepositoryInterface $routeRepository
74
    ) {
75
        $this->cacheProvider = $cacheProvider;
76
        $this->analyticsReportRepository = $analyticsReportRepository;
77
        $this->filesystem = $filesystem;
78
        $this->csvReportFileLocationResolver = $csvReportFileLocationResolver;
79
        $this->cachedTenantContext = $cachedTenantContext;
80
        $this->routeRepository = $routeRepository;
81
    }
82
83
    /**
84
     * @Route("/api/{version}/export/analytics/", options={"expose"=true}, defaults={"version"="v2"}, methods={"POST"}, name="swp_api_core_analytics_export_post")
85
     *
86
     * @throws \Exception
87
     */
88
    public function post(Request $request): SingleResourceResponseInterface
89
    {
90
        /** @var UserInterface $currentlyLoggedInUser */
91
        $currentlyLoggedInUser = $this->getUser();
92
93
        $now = PublisherDateTime::getCurrentDateTime();
94
        $fileName = 'analytics-'.$now->format('Y-m-d-H:i:s').'.csv';
95
96
        $form = $this->get('form.factory')->createNamed('', ExportAnalyticsType::class, null, ['method' => $request->getMethod()]);
97
        $form->handleRequest($request);
98
99
        if ($form->isSubmitted() && $form->isValid()) {
100
            $data = $form->getData();
101
102
            $analyticsReport = new AnalyticsReport();
103
            $analyticsReport->setAssetId($fileName);
104
            $analyticsReport->setFileExtension('csv');
105
            $analyticsReport->setUser($currentlyLoggedInUser);
106
107
            $exportAnalytics = new ExportAnalytics(
108
                $data['start'],
109
                $data['end'],
110
                $this->cachedTenantContext->getTenant()->getCode(),
111
                $fileName,
112
                $currentlyLoggedInUser->getEmail(),
113
                !empty($data['routes']) ? $this->toIds($data['routes']) : [],
114
                !empty($data['authors']) ? $this->toIds($data['authors']) : [],
115
                $data['term'] ?? ''
116
            );
117
118
            $filters = $this->processFilters(
119
                $exportAnalytics->getFilters(),
120
                !empty($data['routes']) ? $data['routes'] : [],
121
                !empty($data['authors']) ? $data['authors'] : []
122
            );
123
124
            $analyticsReport->setFilters($filters);
125
126
            $this->analyticsReportRepository->add($analyticsReport);
127
128
            $this->dispatchMessage($exportAnalytics);
129
130
            return new SingleResourceResponse(['status' => 'OK'], new ResponseContext(201));
131
        }
132
133
        return new SingleResourceResponse($form, new ResponseContext(400));
134
    }
135
136
    /**
137
     * @Route("/api/{version}/export/analytics/", methods={"GET"}, options={"expose"=true}, defaults={"version"="v2"}, name="swp_api_core_list_analytics_reports")
138
     */
139
    public function listAction(Request $request): ResourcesListResponseInterface
140
    {
141
        $reports = $this->analyticsReportRepository->getPaginatedByCriteria(
142
            new Criteria(),
143
            $request->query->get('sorting', []),
144
            new PaginationData($request)
145
        );
146
147
        return new ResourcesListResponse($reports);
148
    }
149
150
    /**
151
     * @Route("/analytics/export/{fileName}", methods={"GET"}, options={"expose"=true}, requirements={"mediaId"=".+"}, name="swp_export_analytics_download")
152
     */
153
    public function downloadFile(string $fileName): Response
154
    {
155
        $cacheKey = md5(serialize(['analytics_report', $fileName]));
156
        if (!$this->cacheProvider->contains($cacheKey)) {
157
            /** @var AnalyticsReportInterface $analyticsReport */
158
            $analyticsReport = $this->analyticsReportRepository->findOneBy(['assetId' => $fileName]);
159
            $this->cacheProvider->save($cacheKey, $analyticsReport, 63072000);
160
        } else {
161
            $analyticsReport = $this->cacheProvider->fetch($cacheKey);
162
        }
163
164
        if (null === $analyticsReport) {
165
            throw new NotFoundHttpException('Report file was not found.');
166
        }
167
168
        $response = new Response();
169
        $disposition = $response->headers->makeDisposition(
170
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
171
            str_replace('/', '_', $fileName)
172
        );
173
174
        $response->headers->set('Content-Disposition', $disposition);
175
        $response->headers->set('Content-Type', Mime::getMimeFromExtension($analyticsReport->getFileExtension()));
176
177
        $response->setPublic();
178
        $response->setMaxAge(63072000);
179
        $response->setSharedMaxAge(63072000);
180
        $response->setLastModified($analyticsReport->getUpdatedAt() ?: $analyticsReport->getCreatedAt());
181
        $response->setContent($this->filesystem->read($this->csvReportFileLocationResolver->getMediaBasePath().'/'.$analyticsReport->getAssetId()));
182
183
        return $response;
184
    }
185
186
    private function toIds(array $items): array
187
    {
188
        $ids = [];
189
        foreach ($items as $item) {
190
            foreach ($item as $entity) {
191
                if (!$entity instanceof PersistableInterface) {
192
                    continue;
193
                }
194
195
                $ids[] = $entity->getId();
196
            }
197
        }
198
199
        return $ids;
200
    }
201
202
    private function processFilters(array $filters, array $routes, array $authors): array
203
    {
204
        $routeNames = [];
205
206
        foreach ($routes as $route) {
207
            foreach ($route as $entity) {
208
                $routeNames[] = $entity->getName();
209
            }
210
        }
211
212
        $filters['routes'] = $routeNames;
213
214
        $authorNames = [];
215
        /** @var ArticleAuthorInterface $author */
216
        foreach ($authors as $author) {
217
            foreach ($author as $entity) {
218
                $authorNames[] = $entity->getName();
219
            }
220
221
        }
222
223
        $filters['authors'] = $authorNames;
224
225
        return $filters;
226
    }
227
}
228