Completed
Push — master ( 9fe0d2...0e0f1f )
by Sam
03:08
created

AutomatedEditsController::resultAction()   F

Complexity

Conditions 18
Paths 2166

Size

Total Lines 177
Code Lines 97

Duplication

Lines 4
Ratio 2.26 %

Importance

Changes 0
Metric Value
dl 4
loc 177
rs 2
c 0
b 0
f 0
cc 18
eloc 97
nc 2166
nop 4

How to fix   Long Method    Complexity   

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 AutomatedEditsController 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\RedirectResponse;
11
use Symfony\Component\HttpFoundation\Request;
12
use Symfony\Component\HttpFoundation\Response;
13
use Xtools\ProjectRepository;
14
use Xtools\User;
15
16
/**
17
 * This controller serves the AutomatedEdits tool.
18
 */
19
class AutomatedEditsController extends Controller
20
{
21
22
    /**
23
     * Get the tool's shortname.
24
     * @return string
25
     */
26
    public function getToolShortname()
27
    {
28
        return 'autoedits';
29
    }
30
31
    /**
32
     * Display the search form.
33
     * @Route("/autoedits", name="autoedits")
34
     * @Route("/automatededits", name="autoeditsLong")
35
     * @Route("/autoedits/index.php", name="autoeditsIndexPhp")
36
     * @Route("/automatededits/index.php", name="autoeditsLongIndexPhp")
37
     * @param Request $request The HTTP request.
38
     * @return Response
39
     */
40
    public function indexAction(Request $request)
41
    {
42
        // Pull the values out of the query string. These values default to
43
        // empty strings.
44
        $project = $request->query->get('project');
45
        $username = $request->query->get('username', $request->query->get('user'));
46
        $startDate = $request->query->get('start');
47
        $endDate = $request->query->get('end');
48
49
        // Redirect if the values are set.
50
        if ($project != "" && $username != "" && ($startDate != "" || $endDate != "")) {
51
            // Redirect to the route fully if we have the username project, and any date
52
53
            // Set start date to beginning of time if end date is provided
54
            // This is nasty, but necessary given URL structure
55
            if ($startDate === "") {
56
                $startDate = date('Y-m-d', 0);
57
            }
58
59
            return $this->redirectToRoute(
60
                "autoeditsResult",
61
                [
62
                    'project' => $project,
63
                    'username' => $username,
64
                    'start' => $startDate,
65
                    'end' => $endDate,
66
                ]
67
            );
68
        } elseif ($project != "" && $username != "") {
69
            // Redirect if we have the username and project
70
            return $this->redirectToRoute(
71
                "autoeditsResult",
72
                [
73
                    'project' => $project,
74
                    'username' => $username,
75
                ]
76
            );
77
        } elseif ($project != "") {
78
            // Redirect if we have the project name
79
            return $this->redirectToRoute(
80
                "autoeditsResult",
81
                [
82
                    'project' => $project
83
                ]
84
            );
85
        }
86
87
        /** @var ApiHelper */
88
        $api = $this->get("app.api_helper");
0 ignored issues
show
Unused Code introduced by
$api is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
89
90
        // Set default project so we can populate the namespace selector.
91
        if (!$project) {
92
            $project = $this->container->getParameter('default_project');
93
        }
94
95
        $projectData = ProjectRepository::getProject($project, $this->container);
96
97
        // Default values for the variables to keep the template happy
98
        $namespaces = null;
99
100
        // If the project exists, actually populate the values
101
        if ($projectData->exists()) {
102
            $namespaces = $projectData->getNamespaces();
103
        }
104
105
        return $this->render('autoEdits/index.html.twig', [
106
            'xtPageTitle' => 'tool-autoedits',
107
            'xtSubtitle' => 'tool-autoedits-desc',
108
            'xtPage' => 'autoedits',
109
            'project' => $projectData,
110
            'namespaces' => $namespaces,
111
        ]);
112
    }
113
114
    /**
115
     * Display the results.
116
     * @Route("/autoedits/{project}/{username}/{start}/{end}", name="autoeditsResult")
117
     * @param $project
118
     * @param $username
119
     * @param null $start
120
     * @param null $end
121
     * @return RedirectResponse|Response
122
     */
123
    public function resultAction($project, $username, $start = null, $end = null)
124
    {
125
        // Pull the labs helper and check if enabled
126
        $lh = $this->get('app.labs_helper');
127
128
        // Pull information about the project
129
        $projectData = ProjectRepository::getProject($project, $this->container);
130
131 View Code Duplication
        if (!$projectData->exists()) {
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...
132
            $this->addFlash('notice', ['invalid-project', $project]);
0 ignored issues
show
Documentation introduced by
array('invalid-project', $project) is of type array<integer,?,{"0":"string","1":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
133
            return $this->redirectToRoute('autoedits');
134
        }
135
136
        $dbName = $projectData->getDatabaseName();
137
        $projectUrl = $projectData->getUrl();
0 ignored issues
show
Unused Code introduced by
$projectUrl is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
138
139
        // Grab our database connection
140
        $dbh = $this->get('doctrine')->getManager('replicas')->getConnection();
141
142
        // Validating the dates. If the dates are invalid, we'll redirect
143
        // to the project and username view.
144
        $invalidDates = (
145
            (isset($start) && strtotime($start) === false) ||
146
            (isset($end) && strtotime($end) === false)
147
        );
148
        if ($invalidDates) {
149
            // Make sure to add the flash notice first.
150
            $this->addFlash('notice', ['invalid-date']);
0 ignored issues
show
Documentation introduced by
array('invalid-date') is of type array<integer,string,{"0":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
151
152
            // Then redirect us!
153
            return $this->redirectToRoute(
154
                'autoeditsResult',
155
                [
156
                    'project' => $project,
157
                    'username' => $username,
158
                ]
159
            );
160
        }
161
162
        $user = new User($username);
163
        $username = $user->getUsername(); // use normalized user name
164
165
        // Now, load the semi-automated edit types.
166
        $AEBTypes = $this->getParameter('automated_tools');
167
168
        // Create a collection of queries that we're going to run.
169
        $queries = [];
170
171
        $revisionTable = $lh->getTable('revision', $dbName);
172
        $archiveTable = $lh->getTable('archive', $dbName);
173
174
        $condBegin = $start ? " AND rev_timestamp > :start " : null;
175
        $condEnd = $end ? " AND rev_timestamp < :end ": null;
176
177
        $regexes = [];
178
179
        foreach ($AEBTypes as $toolname => $values) {
180
            $toolname = $dbh->quote($toolname, \PDO::PARAM_STR);
181
            $regexes[] = $values['regex'];
182
            $regex = $dbh->quote($values['regex'], \PDO::PARAM_STR);
183
184
            $queries[] .= "
185
                SELECT $toolname AS toolname, COUNT(*) AS count
186
                FROM $revisionTable
187
                WHERE rev_user_text = :username
188
                AND rev_comment REGEXP $regex
189
                $condBegin
190
                $condEnd
191
            ";
192
        }
193
194
        // Query to get combined (semi)automated using for all edits
195
        // (some automated edits overlap)
196
        $allAETools = $dbh->quote(implode('|', $regexes), \PDO::PARAM_STR);
197
        $queries[] = "
198
            SELECT 'total_live' AS toolname, COUNT(*) AS count
199
            FROM $revisionTable
200
            WHERE rev_user_text = :username
201
            AND rev_comment REGEXP $allAETools
202
            $condBegin
203
            $condEnd
204
        ";
205
206
        // Next, add two simple queries for the live and deleted edits.
207
        $queries[] = "
208
            SELECT 'live' AS toolname, COUNT(*) AS count
209
            FROM $revisionTable
210
            WHERE rev_user_text = :username
211
            $condBegin
212
            $condEnd
213
        ";
214
215
        $condBegin = str_replace('rev_timestamp', 'ar_timestamp', $condBegin);
216
        $condEnd = str_replace('rev_timestamp', 'ar_timestamp', $condEnd);
217
218
        $queries[] = "
219
            SELECT 'deleted' AS toolname, COUNT(*) AS count
220
            FROM $archiveTable
221
            WHERE ar_user_text = :username
222
            $condBegin
223
            $condEnd
224
        ";
225
226
        // Create a big query and execute.
227
        $stmt = implode(' UNION ', $queries);
228
229
        $sth = $dbh->prepare($stmt);
230
231
        $sth->bindParam('username', $username);
232
        $sth->bindParam('start', $start);
233
        $sth->bindParam('end', $end);
234
235
        $sth->execute();
236
237
        // handling results
238
        $results = [];
239
        $total_semi = 0;
240
        $total = 0;
241
242
        while ($row = $sth->fetch()) {
243
            // Different variables need to get set if the tool is
244
            // the live edits or deleted edits.
245
            // If it is neither and greater than 0,
246
            // add them to the array we're rendering and to our running total
247
            $tool = $row['toolname'];
248
            if ($tool === 'live') {
249
                $total += $row['count'];
250
            } elseif ($tool === 'deleted') {
251
                $total += $row['count'];
252
            } elseif ($tool === 'total_live') {
253
                $total_semi = $row['count'];
254
            } elseif ($row['count'] > 0) {
255
                $results[$tool] = [
256
                    'link' => $AEBTypes[$tool]['link'],
257
                    'count' => $row['count'],
258
                ];
259
            }
260
        }
261
262
        // Inform user if no revisions found.
263
        if ($total === 0) {
264
            $this->addFlash('notice', ['no-contribs']);
0 ignored issues
show
Documentation introduced by
array('no-contribs') is of type array<integer,string,{"0":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
265
            return $this->redirectToRoute('autoedits');
266
        }
267
268
        // Sort the array and do some simple math.
269
        uasort($results, function ($a, $b) {
270
            return $b['count'] - $a['count'];
271
        });
272
273
        if ($total != 0) {
274
            $total_pct = ($total_semi / $total) * 100;
275
        } else {
276
            $total_pct = 0;
277
        }
278
279
        $ret = [
280
            'xtPage' => 'autoedits',
281
            'xtTitle' => $username,
282
            'user' => $user,
283
            'project' => $projectData,
284
            'semi_automated' => $results,
285
            'total_semi' => $total_semi,
286
            'total' => $total,
287
            'total_pct' => $total_pct,
288
        ];
289
290
        if (isset($start)) {
291
            $ret['start'] = $start;
292
        }
293
        if (isset($end)) {
294
            $ret['end'] = $end;
295
        }
296
297
        // Render the view with all variables set.
298
        return $this->render('autoEdits/result.html.twig', $ret);
299
    }
300
}
301