StatsController::getUnregistrationsPerDay()   B
last analyzed

Complexity

Conditions 5
Paths 9

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 14
nc 9
nop 1
1
<?php
2
3
/**
4
 * @author    Markus Tacker <[email protected]>
5
 * @copyright 2013-2016 Verein zur Förderung der Netzkultur im Rhein-Main-Gebiet e.V. | http://netzkultur-rheinmain.de/
6
 */
7
8
namespace BCRM\WebBundle\Controller;
9
10
use BCRM\BackendBundle\Entity\Event\Event;
11
use BCRM\BackendBundle\Entity\Event\EventRepository;
12
use BCRM\BackendBundle\Entity\Event\Registration;
13
use BCRM\BackendBundle\Entity\Event\Ticket;
14
use BCRM\BackendBundle\Entity\Event\TicketRepository;
15
use BCRM\BackendBundle\Entity\Event\Unregistration;
16
use BCRM\BackendBundle\Entity\Event\UnregistrationRepository;
17
use BCRM\WebBundle\Content\ContentReader;
18
use BCRM\WebBundle\Exception\AccesDeniedHttpException;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
22
23
/**
24
 * Stats API endpoints.
25
 */
26
class StatsController
27
{
28
    public function __construct(
29
        ContentReader $reader,
30
        EventRepository $eventRepo,
31
        TicketRepository $ticketRepo,
32
        UnregistrationRepository $unregistrationRepo,
33
        EngineInterface $renderer
34
    )
35
    {
36
        $this->eventRepo          = $eventRepo;
0 ignored issues
show
Bug introduced by
The property eventRepo does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
37
        $this->ticketRepo         = $ticketRepo;
0 ignored issues
show
Bug introduced by
The property ticketRepo does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
38
        $this->unregistrationRepo = $unregistrationRepo;
0 ignored issues
show
Bug introduced by
The property unregistrationRepo does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
39
        $this->reader             = $reader;
0 ignored issues
show
Bug introduced by
The property reader does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
40
        $this->renderer           = $renderer;
0 ignored issues
show
Bug introduced by
The property renderer does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
41
    }
42
43
    /**
44
     * Generates the event's statistics as json.
45
     *
46
     * @param Request $request
47
     *
48
     * @return Response
49
     */
50
    public function statsAction(Request $request)
51
    {
52
        $format = $request->get('_format');
53
        if ($format === 'json') {
54
            return $this->statsJson($request);
55
        }
56
57
        $response = new Response();
58
        $response->setTtl(60 * 5);
59
        $response->setPublic();
60
        if ($response->isNotModified($request)) {
61
            return $response;
62
        }
63
64
        $data = array(
65
            'sponsors' => $this->reader->getPage('Sponsoren/Index.md')
66
        );
67
68
        return $this->renderer->renderResponse('BCRMWebBundle:Stats:stats.html.twig', $data, $response);
69
    }
70
71
    /**
72
     * Creates the JSON used to render the stats.
73
     *
74
     * @param Request $request
75
     *
76
     * @return Response
77
     */
78
    protected function statsJson(Request $request)
79
    {
80
        $response = new Response();
81
        $response->setTtl(10);
82
        $response->setPublic();
83
        if ($response->isNotModified($request)) {
84
            return $response;
85
        }
86
        $event           = $this->eventRepo->getNextEvent()->getOrThrow(new AccesDeniedHttpException('No event.'));
87
        $tickets         = $this->ticketRepo->getTicketsForEvent($event);
88
        $unregistrations = $this->unregistrationRepo->getUnregistrationsForEvent($event);
89
90
        $stats = array(
91
            'checkins'        => array(
92
                'sa'      => $this->getCheckinsPerDay($tickets, Ticket::DAY_SATURDAY),
93
                'sa_hour' => $this->getCheckinsPerHour($tickets, Ticket::DAY_SATURDAY),
94
                'su'      => $this->getCheckinsPerDay($tickets, Ticket::DAY_SUNDAY),
95
                'su_hour' => $this->getCheckinsPerHour($tickets, Ticket::DAY_SUNDAY),
96
                'unique'  => array(
97
                    'sa'   => $this->getUniqueDayCheckins($tickets, Ticket::DAY_SATURDAY),
98
                    'su'   => $this->getUniqueDayCheckins($tickets, Ticket::DAY_SUNDAY),
99
                    'both' => $this->getUniqueDayCheckins($tickets, Ticket::DAY_SUNDAY, true),
100
                ),
101
                'noshows' => array(
102
                    'sa' => $this->getNoShows($tickets, Ticket::DAY_SATURDAY),
103
                    'su' => $this->getNoShows($tickets, Ticket::DAY_SUNDAY),
104
                )
105
            ),
106
            'unregistrations' => $this->getUnregistrationsPerDay($unregistrations)
107
        );
108
109
        $data = array('stats' => $stats);
110
        $response->setContent(json_encode($data));
111
        $response->setCharset('utf-8');
112
        $response->headers->set('Content-Type', 'application/json');
113
        return $response;
114
    }
115
116
    /**
117
     * Returns the number of checkins per day.
118
     *
119
     * @param Ticket[] $tickets
120
     * @param integer  $day
121
     *
122
     * @return mixed
123
     */
124
    protected function getCheckinsPerDay(array $tickets, $day)
125
    {
126 View Code Duplication
        return array_reduce($tickets, function ($count, Ticket $ticket) use ($day) {
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...
127
            return $count + ($ticket->getType() === Registration::TYPE_NORMAL && $ticket->isCheckedIn() && $ticket->getDay() == $day ? 1 : 0);
128
        }, 0);
129
    }
130
131
    /**
132
     * Returns the number of attendees who have checked in only on the given day
133
     *
134
     * @param Ticket[] $tickets
135
     * @param integer  $day
136
     * @param boolean  $both
137
     *
138
     * @return integer
139
     */
140
    protected function getUniqueDayCheckins(array $tickets, $day, $both = false)
141
    {
142
        $otherDayCheckins = array_map(
143
            function (Ticket $ticket) {
144
                return $ticket->getEmail();
145
            },
146 View Code Duplication
            array_filter($tickets, function (Ticket $ticket) use ($day) {
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...
147
                return
148
                    $ticket->getType() === Registration::TYPE_NORMAL
149
                    && $ticket->isCheckedIn()
150
                    && $ticket->getDay() != $day;
151
            })
152
        );
153
154
        return array_reduce($tickets, function ($count, Ticket $ticket) use ($otherDayCheckins, $day, $both) {
155
            return $count + (
156
            $ticket->getType() === Registration::TYPE_NORMAL
157
            && $ticket->isCheckedIn()
158
            && $ticket->getDay() == $day
159
            && ($both ? in_array($ticket->getEmail(), $otherDayCheckins) : !in_array($ticket->getEmail(), $otherDayCheckins))
160
                ? 1 : 0);
161
        }, 0);
162
    }
163
164
    protected function getNoShows(array $tickets, $day)
165
    {
166 View Code Duplication
        return array_reduce($tickets, function ($count, Ticket $ticket) use ($day) {
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...
167
            return $count + (
168
            $ticket->getType() === Registration::TYPE_NORMAL
169
            && !$ticket->isCheckedIn()
170
            && $ticket->getPayment() !== null
171
            && $ticket->getDay() == $day
172
                ? 1 : 0);
173
        }, 0);
174
    }
175
176
    /**
177
     * Returns the number of checkins per hour summarized for every 10 minutes.
178
     *
179
     * @param Ticket[] $tickets
180
     * @param integer  $day
181
     *
182
     * @return array
183
     */
184
    protected function getCheckinsPerHour(array $tickets, $day)
185
    {
186
        $hourCount = array();
187
        foreach ($tickets as $ticket) {
188
            if ($ticket->getDay() != $day
189
                || !$ticket->isCheckedIn()
190
            ) {
191
                continue;
192
            }
193
            $slot = min(max(800, intval(substr(ltrim($ticket->getCheckinTime()->format('Hi'), '0'), 0, -1)) * 10), 1600);
0 ignored issues
show
Bug introduced by
The method format cannot be called on $ticket->getCheckinTime() (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
194
            if (!isset($hourCount[$slot])) {
195
                $hourCount[$slot] = 1;
196
            } else {
197
                $hourCount[$slot]++;
198
            }
199
        }
200
        asort($hourCount);
201
        return $hourCount;
202
    }
203
204
    /**
205
     * Returns the number of checkins per day.
206
     *
207
     * @param Unregistration[] $unregistrations
208
     *
209
     * @return array
210
     */
211
    protected function getUnregistrationsPerDay($unregistrations)
212
    {
213
        $data = array();
214
        $sort = array();
215
        foreach ($unregistrations as $unregistration) {
216
            $slot = $unregistration->getCreated()->format('j.m.');
217
            if (!isset($data[$slot])) {
218
                $data[$slot] = array('sa' => 0, 'su' => 0);
219
                $sort[] = $unregistration->getCreated()->format('Y-m-d');
220
            }
221
            if ($unregistration->getSaturday()) {
222
                $data[$slot]['sa'] += 1;
223
            }
224
            if ($unregistration->getSunday()) {
225
                $data[$slot]['su'] += 1;
226
            }
227
        }
228
        array_multisort($sort, SORT_ASC, $data);
229
        return $data;
230
    }
231
}
232