Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
39.48% |
107 / 271 |
|
28.57% |
6 / 21 |
CRAP | |
0.00% |
0 / 1 |
TestDAO | |
39.48% |
107 / 271 |
|
28.57% |
6 / 21 |
413.56 | |
0.00% |
0 / 1 |
getTestByPerson | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
6 | |||
createTest | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
2 | |||
getTestById | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
6 | |||
addTestReview | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
addUnitReview | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
getTestState | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
getTestSession | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
updateTestState | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
3.00 | |||
getUnitState | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
updateUnitState | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
2 | |||
lockTest | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
changeTestLockStatus | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
getOrCreateUnitId | |
38.46% |
10 / 26 |
|
0.00% |
0 / 1 |
7.73 | |||
getDataParts | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
2 | |||
updateDataParts | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
2 | |||
deleteAttachmentDataPart | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
addUnitLog | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
addTestLog | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
setTestRunning | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
getCommands | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
3 | |||
setCommandExecuted | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | /** @noinspection PhpUnhandledExceptionInspection */ |
4 | declare(strict_types=1); |
5 | |
6 | class TestDAO extends DAO { |
7 | // TODO unit test |
8 | public function getTestByPerson(int $personId, string $bookletName): TestData|null { |
9 | $test = $this->_( |
10 | 'select tests.locked, tests.name, tests.id, tests.laststate, tests.label, tests.running from tests |
11 | where tests.person_id=:personId and tests.name=:bookletname', |
12 | [ |
13 | ':personId' => $personId, |
14 | ':bookletname' => $bookletName |
15 | ] |
16 | ); |
17 | |
18 | if (!$test) { |
19 | return null; |
20 | } |
21 | return new TestData( |
22 | $test['id'], |
23 | $test['name'], |
24 | $test['label'], |
25 | '', |
26 | (bool) $test['locked'], |
27 | (bool) $test['running'], |
28 | JSON::decode($test['laststate']) |
29 | ); |
30 | } |
31 | |
32 | // TODO unit test |
33 | public function createTest(int $personId, string $bookletId, string $bookletLabel): TestData { |
34 | $this->_( |
35 | 'insert into tests (person_id, name, label) values (:person_id, :name, :label)', |
36 | [ |
37 | ':person_id' => $personId, |
38 | ':name' => $bookletId, |
39 | ':label' => $bookletLabel |
40 | ] |
41 | ); |
42 | |
43 | return new TestData( |
44 | (int) $this->pdoDBhandle->lastInsertId(), |
45 | $bookletId, |
46 | $bookletLabel, |
47 | '', |
48 | false, |
49 | false, |
50 | (object) [] |
51 | ); |
52 | } |
53 | |
54 | // TODO unit test |
55 | public function getTestById(int $testId): TestData|null { |
56 | $test = $this->_( |
57 | 'select tests.locked, tests.name, tests.id, tests.laststate, tests.label, tests.running from tests where id = :id', |
58 | [ |
59 | ':id' => $testId |
60 | ] |
61 | ); |
62 | |
63 | if (!$test) { |
64 | return null; |
65 | } |
66 | |
67 | return new TestData( |
68 | $test['id'], |
69 | $test['name'], |
70 | $test['label'], |
71 | '', |
72 | (bool) $test['locked'], |
73 | (bool) $test['running'], |
74 | JSON::decode($test['laststate']) |
75 | ); |
76 | } |
77 | |
78 | // TODO unit test |
79 | public function addTestReview( |
80 | int $testId, |
81 | int $priority, |
82 | string $categories, |
83 | string $entry, |
84 | string $userAgent |
85 | ): void { |
86 | $this->_( |
87 | 'insert into test_reviews (booklet_id, reviewtime, priority, categories, entry, user_agent) values(:b, :t, :p, :c, :e, :u)', |
88 | [ |
89 | ':b' => $testId, |
90 | ':t' => TimeStamp::toSQLFormat(TimeStamp::now()), |
91 | ':p' => $priority, |
92 | ':c' => $categories, |
93 | ':e' => $entry, |
94 | ':u' => $userAgent |
95 | ] |
96 | ); |
97 | } |
98 | |
99 | // TODO unit test |
100 | public function addUnitReview( |
101 | int $testId, |
102 | string $unit, |
103 | int $priority, |
104 | string $categories, |
105 | string $entry, |
106 | string $userAgent, |
107 | string $originalUnitId, |
108 | ?int $page = null, |
109 | ?string $pageLabel = null, |
110 | ): void { |
111 | $unitDbId = $this->getOrCreateUnitId($testId, $unit, $originalUnitId); |
112 | $this->_( |
113 | 'insert into unit_reviews (unit_id, reviewtime, priority, categories, entry, page, pagelabel, user_agent) values(:u, :t, :p, :c, :e, :pa, :pl, :ua)', |
114 | [ |
115 | ':u' => $unitDbId, |
116 | ':t' => TimeStamp::toSQLFormat(TimeStamp::now()), |
117 | ':p' => $priority, |
118 | ':c' => $categories, |
119 | ':e' => $entry, |
120 | ':pa' => $page, |
121 | ':pl' => $pageLabel, |
122 | ':ua' => $userAgent, |
123 | ] |
124 | ); |
125 | } |
126 | |
127 | public function getTestState(int $testId): array { |
128 | $test = $this->_( |
129 | 'select tests.laststate from tests where tests.id=:testId', |
130 | [ |
131 | ':testId' => $testId |
132 | ] |
133 | ); |
134 | |
135 | return ($test) ? JSON::decode($test['laststate'], true) : []; |
136 | } |
137 | |
138 | public function getTestSession(int $testId): array { |
139 | $testSession = $this->_( |
140 | 'select |
141 | login_sessions.id as login_id, |
142 | logins.mode, |
143 | login_sessions.workspace_id, |
144 | logins.group_name as group_name, |
145 | login_sessions.token as login_token, |
146 | person_sessions.code, |
147 | person_sessions.token as person_token, |
148 | tests.person_id, |
149 | tests.laststate as testState, |
150 | tests.id, |
151 | tests.locked, |
152 | tests.running, |
153 | tests.label |
154 | from |
155 | tests |
156 | left join person_sessions on person_sessions.id = tests.person_id |
157 | left join login_sessions on person_sessions.login_sessions_id = login_sessions.id |
158 | left join logins on logins.name = login_sessions.name |
159 | where |
160 | tests.id=:testId', |
161 | [ |
162 | ':testId' => $testId |
163 | ] |
164 | ); |
165 | |
166 | if ($testSession == null) { |
167 | throw new HttpError("Test not found", 404); |
168 | } |
169 | |
170 | $testSession['laststate'] = $this->getTestFullState($testSession); |
171 | |
172 | return $testSession; |
173 | } |
174 | |
175 | // TODO use data-collection class for $statePatch (key-vale pairs) |
176 | public function updateTestState(int $testId, array $statePatch): array { |
177 | $testData = $this->_( |
178 | 'select tests.laststate from tests where tests.id=:testId', |
179 | [ |
180 | ':testId' => $testId |
181 | ] |
182 | ); |
183 | |
184 | if ($testData == null) { |
185 | throw new HttpError("Test not found", 404); |
186 | } |
187 | |
188 | $oldState = $testData['laststate'] ? JSON::decode($testData['laststate'], true) : []; |
189 | $newState = array_merge($oldState, $statePatch); |
190 | |
191 | $this->_( |
192 | 'update tests set laststate = :laststate, timestamp_server = :timestamp where id = :id', |
193 | [ |
194 | ':laststate' => json_encode($newState), |
195 | ':id' => $testId, |
196 | ':timestamp' => TimeStamp::toSQLFormat(TimeStamp::now()) |
197 | ] |
198 | ); |
199 | |
200 | return $newState; |
201 | } |
202 | |
203 | public function getUnitState(int $testId, string $unitName): array { |
204 | $unitData = $this->_( |
205 | 'select units.laststate from units where units.name = :unitname and units.booklet_id = :testId', |
206 | [ |
207 | ':unitname' => $unitName, |
208 | ':testId' => $testId |
209 | ] |
210 | ); |
211 | |
212 | return $unitData ? JSON::decode($unitData['laststate'], true) : []; |
213 | } |
214 | |
215 | // TODO unit test |
216 | public function updateUnitState(int $testId, string $unitName, array $statePatch, string $originalUnitId = ''): array { |
217 | $unitDbId = $this->getOrCreateUnitId($testId, $unitName, $originalUnitId); |
218 | |
219 | $unitData = $this->_( |
220 | 'select units.laststate from units where units.id=:unitId', |
221 | [ |
222 | ':unitId' => $unitDbId |
223 | ] |
224 | ); |
225 | |
226 | $oldState = $unitData['laststate'] ? JSON::decode($unitData['laststate'], true) : []; |
227 | $newState = array_merge($oldState, $statePatch); |
228 | |
229 | // todo save states in separate key-value table instead of JSON blob |
230 | $this->_( |
231 | 'update units set laststate = :laststate where id = :id', |
232 | [ |
233 | ':laststate' => json_encode($newState), |
234 | ':id' => $unitDbId |
235 | ] |
236 | ); |
237 | |
238 | return $newState; |
239 | } |
240 | |
241 | // TODO unit test |
242 | public function lockTest(int $testId): void { |
243 | $this->_( |
244 | 'update tests set locked = :locked , timestamp_server = :timestamp where id = :id', |
245 | [ |
246 | ':locked' => '1', |
247 | ':id' => $testId, |
248 | ':timestamp' => TimeStamp::toSQLFormat(TimeStamp::now()) |
249 | ] |
250 | ); |
251 | } |
252 | |
253 | // TODO unit test |
254 | public function changeTestLockStatus(int $testId, bool $unlock = true): void { |
255 | $this->_( |
256 | 'update tests set locked = :locked , timestamp_server = :timestamp where id = :id', |
257 | [ |
258 | ':locked' => $unlock ? '0' : '1', |
259 | ':id' => $testId, |
260 | ':timestamp' => TimeStamp::toSQLFormat(TimeStamp::now()) |
261 | ] |
262 | ); |
263 | } |
264 | |
265 | |
266 | // TODO unit test |
267 | // todo reduce nr of queries by using replace...into syntax |
268 | private function getOrCreateUnitId(int $testId, string $unitName, string $originalUnitId = ''): string { |
269 | $unit = $this->_( |
270 | 'select units.id from units where units.name = :unitname and units.booklet_id = :testId', |
271 | [ |
272 | ':unitname' => $unitName, |
273 | ':testId' => $testId |
274 | ] |
275 | ); |
276 | |
277 | if ($unit && !empty($originalUnitId)) { |
278 | $this->_( |
279 | 'update units set original_unit_id = :originalUnitId where id = :unitId', |
280 | [ |
281 | ':unitId' => $unit['id'], |
282 | ':originalUnitId' => $originalUnitId |
283 | ] |
284 | ); |
285 | } |
286 | |
287 | if (!$unit) { |
288 | $this->_( |
289 | 'insert into units (booklet_id, name, original_unit_id) values(:testId, :name, :originalUnitId)', |
290 | [ |
291 | ':testId' => $testId, |
292 | ':name' => $unitName, |
293 | ':originalUnitId' => $originalUnitId |
294 | ] |
295 | ); |
296 | return $this->pdoDBhandle->lastInsertId(); |
297 | } |
298 | |
299 | return (string) $unit['id']; |
300 | } |
301 | |
302 | public function getDataParts(int $testId, string $unitName): array { |
303 | $result = $this->_( |
304 | 'select |
305 | unit_data.part_id, |
306 | unit_data.content, |
307 | unit_data.response_type |
308 | from |
309 | unit_data |
310 | left join units on units.id = unit_data.unit_id |
311 | where |
312 | units.name = :unitname |
313 | and units.booklet_id = :testId |
314 | ', |
315 | [ |
316 | ':unitname' => $unitName, |
317 | ':testId' => $testId |
318 | ], |
319 | true |
320 | ); |
321 | |
322 | $unitData = []; |
323 | foreach ($result as $row) { |
324 | $unitData[$row['part_id']] = $row['content']; |
325 | } |
326 | |
327 | return [ |
328 | "dataParts" => $unitData, |
329 | "dataType" => $row['response_type'] ?? '' |
330 | ]; |
331 | } |
332 | |
333 | public function updateDataParts(int $testId, string $unitName, array $dataParts, string $type, int $timestamp, string $originalUnitId = ''): void { |
334 | $unitDbId = $this->getOrCreateUnitId($testId, $unitName, $originalUnitId); |
335 | foreach ($dataParts as $partId => $content) { |
336 | $this->_( |
337 | 'replace into unit_data(unit_id, part_id, content, ts, response_type) |
338 | values (:unit_id, :part_id, :content, :ts, :response_type)', |
339 | [ |
340 | ':part_id' => $partId, |
341 | ':content' => $content, |
342 | ':ts' => $timestamp, |
343 | ':response_type' => $type, |
344 | ':unit_id' => $unitDbId |
345 | ] |
346 | ); |
347 | } |
348 | } |
349 | |
350 | // TODO unit test |
351 | public function deleteAttachmentDataPart(string $partId): void { |
352 | // unitId is not necessary for identification, because partId contains unitName and TestId in case of attachments |
353 | $this->_( |
354 | 'delete from unit_data where part_id = :partId', |
355 | [':partId' => $partId] |
356 | ); |
357 | } |
358 | |
359 | // TODO unit test |
360 | public function addUnitLog( |
361 | int $testId, |
362 | string $unitName, |
363 | string $logKey, |
364 | int $timestamp, |
365 | string $logContent = "", |
366 | string $originalUnitId = '' |
367 | ): void { |
368 | $unitId = $this->getOrCreateUnitId($testId, $unitName, $originalUnitId); |
369 | |
370 | $this->_( |
371 | 'insert into unit_logs (unit_id, logentry, timestamp) values (:unitId, :logentry, :ts)', |
372 | [ |
373 | ':unitId' => $unitId, |
374 | ':logentry' => $logKey . ($logContent ? ' = ' . $logContent : ''), |
375 | ':ts' => $timestamp |
376 | ] |
377 | ); |
378 | } |
379 | |
380 | // TODO unit test |
381 | public function addTestLog(int $testId, string $logKey, int $timestamp, string $logContent = ""): void { |
382 | $this->_( |
383 | 'insert into test_logs (booklet_id, logentry, timestamp) values (:bookletId, :logentry, :timestamp)', |
384 | [ |
385 | ':bookletId' => $testId, |
386 | ':logentry' => $logKey . ($logContent ? ' : ' . $logContent : ''), |
387 | // TODO add value-column to log-tables instead of this shit |
388 | ':timestamp' => $timestamp |
389 | ] |
390 | ); |
391 | } |
392 | |
393 | // TODO unit test |
394 | public function setTestRunning(int $testId): void { |
395 | $this->_( |
396 | 'update tests set running = :running , timestamp_server = :timestamp where id = :id', |
397 | [ |
398 | ':running' => '1', |
399 | ':id' => $testId, |
400 | ':timestamp' => TimeStamp::toSQLFormat(TimeStamp::now()) |
401 | ] |
402 | ); |
403 | } |
404 | |
405 | public function getCommands(int $testId, ?int $lastCommandId = null): array { |
406 | $sql = "select * from test_commands where test_id = :test_id and executed = 0 order by timestamp"; |
407 | $replacements = [':test_id' => $testId]; |
408 | if ($lastCommandId) { |
409 | $replacements[':last_id'] = $lastCommandId; |
410 | $sql = str_replace( |
411 | 'where', |
412 | 'where timestamp > (select timestamp from test_commands where id = :last_id) and ', |
413 | $sql |
414 | ); |
415 | } |
416 | |
417 | $commands = []; |
418 | foreach ($this->_($sql, $replacements, true) as $line) { |
419 | $commands[] = new Command( |
420 | (int) $line['id'], |
421 | $line['keyword'], |
422 | TimeStamp::fromSQLFormat($line['timestamp']), |
423 | ...JSON::decode($line['parameter'], true) |
424 | ); |
425 | } |
426 | return $commands; |
427 | } |
428 | |
429 | public function setCommandExecuted(int $testId, int $commandId): bool { |
430 | $command = $this->_( |
431 | 'select executed from test_commands where test_id = :testId and id = :commandId', |
432 | [':testId' => $testId, ':commandId' => $commandId] |
433 | ); |
434 | |
435 | if (!$command) { |
436 | throw new HttpError("Command `$commandId` not found on test `$testId`", 404); |
437 | } |
438 | |
439 | if ($command['executed']) { |
440 | return false; |
441 | } |
442 | |
443 | $this->_( |
444 | 'update test_commands set executed = 1 where test_id = :testId and id = :commandId', |
445 | [':testId' => $testId, ':commandId' => $commandId] |
446 | ); |
447 | |
448 | return true; |
449 | } |
450 | } |