Completed
Push — master ( 7c7725...4cc8fe )
by MusikAnimal
02:21
created

MetaController::recordUsage()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 63
Code Lines 40

Duplication

Lines 22
Ratio 34.92 %

Importance

Changes 0
Metric Value
dl 22
loc 63
rs 7.2689
c 0
b 0
f 0
cc 7
eloc 40
nc 8
nop 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 Controller
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
    public function indexAction(Request $request)
29
    {
30
        $start = $request->query->get('start');
31
        $end = $request->query->get('end');
32
33
        if ($start != '' && $end != '') {
34
            return $this->redirectToRoute('MetaResult', [ 'start' => $start, 'end' => $end ]);
35
        }
36
37
        return $this->render('meta/index.html.twig', [
38
            'xtPage' => 'meta',
39
            'xtPageTitle' => 'tool-meta',
40
            'xtSubtitle' => 'tool-meta-desc',
41
        ]);
42
    }
43
44
    /**
45
     * Display the results.
46
     * @Route("/meta/{start}/{end}/{legacy}", name="MetaResult")
47
     * @param string $start    Start date
48
     * @param string $end      End date
49
     * @param string [$legacy] Non-blank value indicates to show stats for legacy XTools
50
     * @return Response
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
     */
125
    public function recordUsage(Request $request, $tool, $project, $token)
126
    {
127
        // Validate method and token.
128
        if ($request->getMethod() !== 'PUT' || !$this->isCsrfTokenValid('intention', $token)) {
129
            throw $this->createAccessDeniedException('This endpoint is for internal use only.');
130
        }
131
132
        // Ready the response object.
133
        $response = new Response();
134
        $response->headers->set('Content-Type', 'application/json');
135
136
        // Don't update counts for tools that aren't enabled
137
        if (!$this->container->getParameter("enable.$tool")) {
138
            $response->setStatusCode(Response::HTTP_FORBIDDEN);
139
            $response->setContent(json_encode([
140
                'error' => 'This tool is disabled'
141
            ]));
142
            return $response;
143
        }
144
145
        $conn = $this->container->get('doctrine')->getManager('default')->getConnection();
146
        $date =  date('Y-m-d');
147
148
        // Increment count in timeline
149
        $existsSql = "SELECT 1 FROM usage_timeline
150
                      WHERE date = '$date'
151
                      AND tool = '$tool'";
152
153 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...
154
            $createSql = "INSERT INTO usage_timeline
155
                          VALUES(NULL, '$date', '$tool', 1)";
156
            $conn->query($createSql);
157
        } else {
158
            $updateSql = "UPDATE usage_timeline
159
                          SET count = count + 1
160
                          WHERE tool = '$tool'
161
                          AND date = '$date'";
162
            $conn->query($updateSql);
163
        }
164
165
        // Update per-project usage, if applicable
166
        if (!$this->container->getParameter('app.single_wiki')) {
167
            $existsSql = "SELECT 1 FROM usage_projects
168
                          WHERE tool = '$tool'
169
                          AND project = '$project'";
170
171 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...
172
                $createSql = "INSERT INTO usage_projects
173
                              VALUES(NULL, '$tool', '$project', 1)";
174
                $conn->query($createSql);
175
            } else {
176
                $updateSql = "UPDATE usage_projects
177
                              SET count = count + 1
178
                              WHERE tool = '$tool'
179
                              AND project = '$project'";
180
                $conn->query($updateSql);
181
            }
182
        }
183
184
        $response->setStatusCode(Response::HTTP_NO_CONTENT);
185
        $response->setContent(json_encode([]));
186
        return $response;
187
    }
188
}
189