Passed
Push — master ( 23707e...d837db )
by MusikAnimal
03:38
created

MetaController   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 173
Duplicated Lines 20.81 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 83.33%

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 6
dl 36
loc 173
ccs 5
cts 6
cp 0.8333
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
B resultAction() 0 61 6
B recordUsage() 22 63 7
A indexAction() 14 14 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * This file contains only the MetaController class.
4
 */
5
6
namespace AppBundle\Controller;
7
8
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
9
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
10
use Symfony\Component\HttpFoundation\Request;
11
use DateTime;
12
use Symfony\Component\HttpFoundation\Response;
13
14
/**
15
 * This controller serves everything for the Meta tool.
16
 */
17
class MetaController extends XtoolsController
18
{
19
    /**
20
     * Display the form.
21
     * @Route("/meta", name="meta")
22
     * @Route("/meta", name="Meta")
23
     * @Route("/meta/", name="MetaSlash")
24
     * @Route("/meta/index.php", name="MetaIndexPhp")
25
     * @param Request $request
26
     * @return Response
27
     */
28 1 View Code Duplication
    public function indexAction(Request $request)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
29
    {
30 1
        $params = $this->parseQueryParams($request);
31
32 1
        if (isset($params['start']) && isset($params['end'])) {
33
            return $this->redirectToRoute('MetaResult', $params);
34
        }
35
36 1
        return $this->render('meta/index.html.twig', [
37 1
            'xtPage' => 'meta',
38
            'xtPageTitle' => 'tool-meta',
39
            'xtSubtitle' => 'tool-meta-desc',
40
        ]);
41
    }
42
43
    /**
44
     * Display the results.
45
     * @Route("/meta/{start}/{end}/{legacy}", name="MetaResult")
46
     * @param string $start    Start date
47
     * @param string $end      End date
48
     * @param string [$legacy] Non-blank value indicates to show stats for legacy XTools
49
     * @return Response
50
     * @codeCoverageIgnore
51
     */
52
    public function resultAction($start, $end, $legacy = false)
53
    {
54
        $db = $legacy ? 'toolsdb' : 'default';
55
        $table = $legacy ? 's51187__metadata.xtools_timeline' : 'usage_timeline';
56
57
        $client = $this->container
58
            ->get('doctrine')
59
            ->getManager($db)
60
            ->getConnection();
61
62
        $query = $client->prepare("SELECT * FROM $table
63
                                 WHERE date >= :start AND date <= :end");
64
        $query->bindParam('start', $start);
65
        $query->bindParam('end', $end);
66
        $query->execute();
67
68
        $data = $query->fetchAll();
69
70
        // Create array of totals, along with formatted timeline data as needed by Chart.js
71
        $totals = [];
72
        $dateLabels = [];
73
        $timeline = [];
74
        $startObj = new DateTime($start);
75
        $endObj = new DateTime($end);
76
        $numDays = (int) $endObj->diff($startObj)->format("%a");
77
        $grandSum = 0;
78
79
        // Generate array of date labels
80
        for ($dateObj = new DateTime($start); $dateObj <= $endObj; $dateObj->modify('+1 day')) {
81
            $dateLabels[] = $dateObj->format('Y-m-d');
82
        }
83
84
        foreach ($data as $entry) {
85
            if (!isset($totals[$entry['tool']])) {
86
                $totals[$entry['tool']] = (int) $entry['count'];
87
88
                // Create arrays for each tool, filled with zeros for each date in the timeline
89
                $timeline[$entry['tool']] = array_fill(0, $numDays, 0);
90
            } else {
91
                $totals[$entry['tool']] += (int) $entry['count'];
92
            }
93
94
            $date = new DateTime($entry['date']);
95
            $dateIndex = (int) $date->diff($startObj)->format("%a");
96
            $timeline[$entry['tool']][$dateIndex] = (int) $entry['count'];
97
98
            $grandSum += $entry['count'];
99
        }
100
        arsort($totals);
101
102
        return $this->render('meta/result.html.twig', [
103
            'xtPage' => 'meta',
104
            'start' => $start,
105
            'end' => $end,
106
            'data' => $data,
107
            'totals' => $totals,
108
            'grandSum' => $grandSum,
109
            'dateLabels' => $dateLabels,
110
            'timeline' => $timeline,
111
        ]);
112
    }
113
114
    /**
115
     * Record usage of a particular XTools tool. This is called automatically
116
     *   in base.html.twig via JavaScript so that it is done asynchronously
117
     * @Route("/meta/usage/{tool}/{project}/{token}")
118
     * @param  $request Request
119
     * @param  string $tool    Internal name of tool
120
     * @param  string $project Project domain such as en.wikipedia.org
121
     * @param  string $token   Unique token for this request, so we don't have people
122
     *                         meddling with these statistics
123
     * @return Response
124
     * @codeCoverageIgnore
125
     */
126
    public function recordUsage(Request $request, $tool, $project, $token)
127
    {
128
        // Validate method and token.
129
        if ($request->getMethod() !== 'PUT' || !$this->isCsrfTokenValid('intention', $token)) {
130
            throw $this->createAccessDeniedException('This endpoint is for internal use only.');
131
        }
132
133
        // Ready the response object.
134
        $response = new Response();
135
        $response->headers->set('Content-Type', 'application/json');
136
137
        // Don't update counts for tools that aren't enabled
138
        if (!$this->container->getParameter("enable.$tool")) {
139
            $response->setStatusCode(Response::HTTP_FORBIDDEN);
140
            $response->setContent(json_encode([
141
                'error' => 'This tool is disabled'
142
            ]));
143
            return $response;
144
        }
145
146
        $conn = $this->container->get('doctrine')->getManager('default')->getConnection();
147
        $date =  date('Y-m-d');
148
149
        // Increment count in timeline
150
        $existsSql = "SELECT 1 FROM usage_timeline
151
                      WHERE date = '$date'
152
                      AND tool = '$tool'";
153
154 View Code Duplication
        if (count($conn->query($existsSql)->fetchAll()) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
            $createSql = "INSERT INTO usage_timeline
156
                          VALUES(NULL, '$date', '$tool', 1)";
157
            $conn->query($createSql);
158
        } else {
159
            $updateSql = "UPDATE usage_timeline
160
                          SET count = count + 1
161
                          WHERE tool = '$tool'
162
                          AND date = '$date'";
163
            $conn->query($updateSql);
164
        }
165
166
        // Update per-project usage, if applicable
167
        if (!$this->container->getParameter('app.single_wiki')) {
168
            $existsSql = "SELECT 1 FROM usage_projects
169
                          WHERE tool = '$tool'
170
                          AND project = '$project'";
171
172 View Code Duplication
            if (count($conn->query($existsSql)->fetchAll()) === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
173
                $createSql = "INSERT INTO usage_projects
174
                              VALUES(NULL, '$tool', '$project', 1)";
175
                $conn->query($createSql);
176
            } else {
177
                $updateSql = "UPDATE usage_projects
178
                              SET count = count + 1
179
                              WHERE tool = '$tool'
180
                              AND project = '$project'";
181
                $conn->query($updateSql);
182
            }
183
        }
184
185
        $response->setStatusCode(Response::HTTP_NO_CONTENT);
186
        $response->setContent(json_encode([]));
187
        return $response;
188
    }
189
}
190