Passed
Push — master ( ea7895...2e7a5b )
by William Johnson S.
02:52
created

HttpApi::getPunchesFromDay()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 19
ccs 0
cts 14
cp 0
rs 9.4285
cc 3
eloc 11
nc 4
nop 3
crap 12
1
<?php
2
3
namespace Katapoka\Ahgora;
4
5
use DateTime;
6
use InvalidArgumentException;
7
use Katapoka\Ahgora\Contracts\IHttpClient;
8
use Katapoka\Ahgora\Contracts\IHttpResponse;
9
use LogicException;
10
11
/**
12
 * Class responsible for getting the data from the Ahgora system.
13
 */
14
class HttpApi extends AbstractApi
15
{
16
    const AHGORA_BASE_URL = 'https://www.ahgora.com.br';
17
    const AHGORA_COMPANY_URL = '%s/externo/index/%s';
18
    const AHGORA_LOGIN_URL = '%s/externo/login';
19
    const AHGORA_PUNCHS_URL = '%s/externo/batidas/%s-%s';
20
21
    /** @var \Katapoka\Ahgora\Contracts\IHttpClient */
22
    private $httpClient;
23
    /** @var string */
24
    private $password;
25
    /** @var string */
26
    private $companyId;
27
    /** @var string */
28
    private $username;
29
    /** @var bool */
30
    private $loggedIn = false;
31
    /** @var HtmlPageParser */
32
    private $htmlPageParser;
33
34
    /**
35
     * Api constructor.
36
     *
37
     * @param IHttpClient $httpClient
38
     */
39 1
    public function __construct(IHttpClient $httpClient)
40
    {
41 1
        $this->httpClient = $httpClient;
42 1
        $this->htmlPageParser = new HtmlPageParser();
43 1
        $this->debug('Api instance created');
44 1
    }
45
46
    /**
47
     * Set the company id of the ahgora system.
48
     *
49
     * @param string $companyId
50
     *
51
     * @return $this
52
     */
53
    public function setCompanyId($companyId)
54
    {
55
        $this->companyId = $companyId;
56
        $this->debug('Company ID set', ['company_id' => $companyId]);
57
58
        return $this;
59
    }
60
61
    /**
62
     * Set the username of the employee, from the company set at the setCompanyId.
63
     *
64
     * @param string $username
65
     *
66
     * @return $this
67
     */
68
    public function setUsername($username)
69
    {
70
        $this->username = $username;
71
        $this->debug('Username set', ['username' => $username]);
72
73
        return $this;
74
    }
75
76
    /**
77
     * Set the password of the employee, from the company set at the setCompanyId.
78
     *
79
     * @param string $password
80
     *
81
     * @return $this
82
     */
83
    public function setPassword($password)
84
    {
85
        $this->password = $password;
86
        $this->debug('Password set', ['password' => $password]);
87
88
        return $this;
89
    }
90
91
    /**
92
     * Try to execute the login on the page.
93
     * To execute some actions the user needs to be loggedin.
94
     * After a successful login, the status loggedin is saved as true.
95
     *
96
     * @return bool Returns true if the login was successful and false otherwise
97
     */
98
    public function doLogin()
99
    {
100
        $hasLoggedIn = false;
101
        $this->debug('Started login proccess');
102
103
        $accessEnabled = $this->checkAccessEnabled();
104
105
        if ($accessEnabled) {
106
            $response = $this->executeLogin();
107
            $hasLoggedIn = $this->checkLoginStatus($response);
108
        }
109
110
        $this->debug($accessEnabled ? "Company has external access enabled" : "Company hasn't external access enabled");
111
112
        $this->setLoggedIn($hasLoggedIn);
113
114
        return $hasLoggedIn;
115
    }
116
117
    /**
118
     * Get the punches at the given parameters.
119
     *
120
     * @param int|null $month The month you want to get the punches - Must be between 01 and 12 (both included)
121
     * @param int|null $year  The year you want to get the punches
122
     *
123
     * @return array
124
     */
125
    public function getPunches($month = null, $year = null)
126
    {
127
        if (!$this->loggedIn) {
128
            throw new LogicException('To get punches you need to be loggedIn');
129
        }
130
131
        $month = IntHelper::parseNullableInt($month);
132
        $year = IntHelper::parseNullableInt($year);
133
134
        $month = $month ?: (int)date('m') + (intval(date('d')) >= 20 ? 1 : 0);
135
        $year = $year ?: (int)date('Y');
136
137
        return $this->getPunchesFromPage($this->getPunchesPage($month, $year));
138
    }
139
140
    /**
141
     * Get the punches from some given day.
142
     *
143
     * @param int $day
144
     * @param int $month
145
     * @param int $year
146
     *
147
     * @return mixed
148
     */
149
    public function getPunchesFromDay($day, $month, $year)
150
    {
151
        $date = date("Y-m-d", mktime(0, 0, 0, $month, $day, $year));
152
        list($year, $month, $day) = explode('-', $date);
153
154
        if ($day > 19) {
155
            $month++;
156
        }
157
158
        if ($month > 12) {
159
            $month = 1;
160
            $year++;
161
        }
162
        $punches = $this->getPunches($month, $year);
163
164
        return array_filter($punches['punches'], function (\DateTime $punchDateTime) use ($day) {
165
            return (int) $punchDateTime->format('d') === (int) $day;
166
        });
167
    }
168
169
    /**
170
     * Gets the employer name.
171
     *
172
     * @return string
173
     */
174
    public function getEmployeeRole()
175
    {
176
        return "NOT IMPLEMENTED YET";
177
    }
178
179
    /**
180
     * Get the employer department.
181
     *
182
     * @return string
183
     */
184
    public function getDepartment()
185
    {
186
        return "NOT IMPLEMENTED YET";
187
    }
188
189
    /**
190
     * Retrive all the punches for the given string.
191
     *
192
     * @param string $punchesStr
193
     *
194
     * @return array
195
     */
196
    private function parsePunches($punchesStr)
197
    {
198
        $punches = [];
199
        if (!!preg_match_all('/(\d{2}:\d{2})/is', $punchesStr, $matches)) {
200
            $punches = $matches[0];
201
        }
202
203
        return $punches;
204
    }
205
206
    /**
207
     * Execute the login on the server and returns the server response.
208
     *
209
     * @return IHttpResponse
210
     */
211
    private function executeLogin()
212
    {
213
        return $this->httpClient->post($this->loginUrl(), [
214
            'empresa'   => $this->companyId,
215
            'matricula' => $this->username,
216
            'senha'     => $this->password,
217
        ]);
218
    }
219
220
    /**
221
     * Check if the company has external access on the Ahgora system.
222
     *
223
     * @return bool
224
     */
225
    private function checkAccessEnabled()
226
    {
227
        $response = $this->httpClient->get($this->companyUrl());
228
229
        return stripos($response->getBody(), 'Sua Empresa não liberou o acesso a essa ferramenta') === false;
230
    }
231
232
    /**
233
     * Check the return of the login action.
234
     * How it works: If statusCode 200 and no body, login ok, otherwise, login failed.
235
     * Should return a json with property "r" with "error" and "text" with the message
236
     *
237
     * @param IHttpResponse $response
238
     *
239
     * @return bool
240
     */
241
    private function checkLoginStatus(IHttpResponse $response)
242
    {
243
        try {
244
            return $response->getHttpStatus() === IHttpClient::HTTP_STATUS_OK && $this->getResponseLoginStatus($response);
245
        } catch (InvalidArgumentException $iaex) {
246
            $this->error('checkLoginStatus', ['expcetion' => $iaex]);
247
248
            return false;
249
        }
250
    }
251
252
    /**
253
     * Check if the response can be decoded as json, has the property r and r is 'success'.
254
     *
255
     * @param IHttpResponse $response
256
     *
257
     * @return bool
258
     */
259
    private function getResponseLoginStatus(IHttpResponse $response)
260
    {
261
        try {
262
            $json = $response->json();
263
264
            return array_key_exists('r', $json) && $json->r === 'success';
265
        } catch (InvalidArgumentException $iaex) {
266
            $this->debug('getResponseLoginStatus', ['exception', $iaex]);
267
268
            return false;
269
        }
270
    }
271
272
    /**
273
     * Safely set if the user is loggedin or not.
274
     * Did a separate method do eventually trigger events, if necessary.
275
     *
276
     * @param bool $loggedIn
277
     *
278
     * @return $this
279
     */
280
    private function setLoggedIn($loggedIn = true)
281
    {
282
        if (!is_bool($loggedIn)) {
283
            throw new InvalidArgumentException('LoggedIn parameter must be boolean');
284
        }
285
286
        $this->debug('setLoggedIn', ['logged_in' => $loggedIn]);
287
        $this->loggedIn = $loggedIn;
288
289
        return $this;
290
    }
291
292
    /**
293
     * Build the company url string.
294
     *
295
     * @return string
296
     */
297
    private function companyUrl()
298
    {
299
        $companyUrl = sprintf(self::AHGORA_COMPANY_URL, self::AHGORA_BASE_URL, $this->companyId);
300
        $this->debug('CompanyURL', ['company_url' => $companyUrl]);
301
302
        return $companyUrl;
303
    }
304
305
    /**
306
     * Build the login url.
307
     *
308
     * @return string
309
     */
310
    private function loginUrl()
311
    {
312
        $loginUrl = sprintf(self::AHGORA_LOGIN_URL, self::AHGORA_BASE_URL);
313
        $this->debug('loginUrl', ['login_url' => $loginUrl]);
314
315
        return $loginUrl;
316
    }
317
318
    /**
319
     * Get the built punchesUrl with the given month and year.
320
     *
321
     * @param int $month
322
     * @param int $year
323
     *
324
     * @return string
325
     */
326
    private function punchesUrl($month, $year)
327
    {
328
        $month = str_pad(strval($month), 2, '0', STR_PAD_LEFT);
329
        $punchesUrl = sprintf(self::AHGORA_PUNCHS_URL, self::AHGORA_BASE_URL, $month, $year);
330
        $this->debug('punchesUrl', [
331
            'punches_url' => $punchesUrl,
332
            'month'       => $month,
333
            'year'        => $year
334
        ]);
335
336
        return $punchesUrl;
337
    }
338
339
    /**
340
     * Make the request to the punches page of the requested time period.
341
     *
342
     * @param int $month
343
     * @param int $year
344
     *
345
     * @return IHttpResponse
346
     */
347
    private function getPunchesPage($month, $year)
348
    {
349
        return $this->httpClient->get($this->punchesUrl($month, $year));
350
    }
351
352
    /**
353
     * Get the punches from the given response of the punches page.
354
     *
355
     * @param IHttpResponse $punchesPageResponse
356
     *
357
     * @return array
358
     */
359
    private function getPunchesFromPage(IHttpResponse $punchesPageResponse)
360
    {
361
        if ($punchesPageResponse->getHttpStatus() !== IHttpClient::HTTP_STATUS_OK) {
362
            throw new InvalidArgumentException('The request returned http status ' . $punchesPageResponse->getHttpStatus());
363
        }
364
365
        $punches = $this->parsePunchesPage($punchesPageResponse);
366
367
        return [
368
            'punches' => $punches,
369
        ];
370
    }
371
372
    private function parsePunchesPage(IHttpResponse $punchesPageResponse)
373
    {
374
        $rows = $this->htmlPageParser->getPunchesRows($punchesPageResponse);
375
        $punchCollection = [];
376
377
        foreach ($rows as $row) {
378
            $punches = $this->parsePunches($row['punches']);
379
            $punchCollection = array_merge($punchCollection, $this->createPunchesDate($row['date'], $punches));
380
        }
381
382
        return $punchCollection;
383
    }
384
385
    /**
386
     * Convert the date string and the datepunch array to an array of DateTime's.
387
     *
388
     * @param string $date
389
     * @param array  $punches
390
     *
391
     * @return \DateTime[]
392
     */
393
    private function createPunchesDate($date, array $punches = [])
394
    {
395
        $dates = [];
396
        foreach ($punches as $punch) {
397
            $dates[] = DateTime::createFromFormat($this->datetimeFormat, sprintf('%s %s', $date, $punch));
398
        }
399
400
        return $dates;
401
    }
402
}
403