Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 73 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
SolvResultParser | |
0.00% |
0 / 73 |
|
0.00% |
0 / 3 |
420 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
parse_solv_yearly_results_json | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
90 | |||
parse_solv_event_result_html | |
0.00% |
0 / 53 |
|
0.00% |
0 / 1 |
110 |
1 | <?php |
2 | |
3 | namespace Olz\Parsers; |
4 | |
5 | use Olz\Entity\SolvResult; |
6 | |
7 | class SolvResultParser { |
8 | protected TimeParser $timeParser; |
9 | |
10 | public function __construct() { |
11 | $this->timeParser = new TimeParser(); |
12 | } |
13 | |
14 | /** @return array<int, array{result_list_id: string}> */ |
15 | public function parse_solv_yearly_results_json(string $json_content): array { |
16 | $hacky_sanitized_json = str_replace(["\n", "\t"], ['', ' '], $json_content); |
17 | if (!json_validate($hacky_sanitized_json, 512, JSON_INVALID_UTF8_IGNORE)) { |
18 | $msg = json_last_error_msg(); |
19 | throw new \Exception("Invalid JSON in parse_solv_yearly_results_json (hackyly sanitized): {$msg}\n\n{$hacky_sanitized_json}"); |
20 | } |
21 | $data = json_decode($hacky_sanitized_json, true, 512, JSON_INVALID_UTF8_SUBSTITUTE); |
22 | |
23 | if (!$data || !isset($data['ResultLists']) || !is_array($data['ResultLists'])) { |
24 | return []; |
25 | } |
26 | |
27 | $result_by_uid = []; |
28 | for ($res_ind = 0; $res_ind < count($data['ResultLists']); $res_ind++) { |
29 | $res = $data['ResultLists'][$res_ind]; |
30 | if (!isset($res['UniqueID']) || $res['UniqueID'] == 0) { |
31 | continue; |
32 | } |
33 | $uid = $res['UniqueID']; |
34 | // TODO: find better solution to deal with multiple results |
35 | if (isset($result_by_uid[$uid])) { |
36 | continue; |
37 | } |
38 | $result_by_uid[$uid] = [ |
39 | 'result_list_id' => strval($res['ResultListID']), |
40 | ]; |
41 | } |
42 | return $result_by_uid; |
43 | } |
44 | |
45 | /** @return array<SolvResult> */ |
46 | public function parse_solv_event_result_html(string $html_content, int $event_uid): array { |
47 | $class_headers_count = preg_match_all('/<b>(?:<p><\/p>)?<a href="results\?type=rang&year=([0-9]+)&rl_id=([0-9]+)&kat=([^"]+)&zwizt=1">([^<]+)<\/a><\/b>\s*<pre>/is', $html_content, $class_matches); |
48 | $results = []; |
49 | for ($class_ind = 0; $class_ind < $class_headers_count; $class_ind++) { |
50 | $class_name = $class_matches[4][$class_ind]; |
51 | |
52 | $found_at = mb_strpos($html_content, $class_matches[0][$class_ind]); |
53 | $class_header_ends_at = $found_at + mb_strlen($class_matches[0][$class_ind]); |
54 | $class_body_ends_at = mb_strpos($html_content, '</pre>', $class_header_ends_at); |
55 | $class_body_length = $class_body_ends_at - $class_header_ends_at; |
56 | $class_body = mb_substr($html_content, $class_header_ends_at, $class_body_length); |
57 | |
58 | $does_class_info_match = preg_match("/^\\s*\\(\\s*([0-9\\.]+)\\s*km\\s*,\\s*([0-9]+)\\s*m\\s*,\\s*([0-9]+)\\s*Po\\.\\s*\\)\\s*([0-9]+)\\s*Teilnehmer/", $class_body, $class_info_matches); |
59 | $class_info = [ |
60 | 'distance' => $does_class_info_match ? intval(floatval($class_info_matches[1]) * 1000) : 0, |
61 | 'elevation' => $does_class_info_match ? intval($class_info_matches[2]) : 0, |
62 | 'control_count' => $does_class_info_match ? intval($class_info_matches[3]) : 0, |
63 | 'competitor_count' => $does_class_info_match ? intval($class_info_matches[4]) : 0, |
64 | ]; |
65 | |
66 | $competitors_count = preg_match_all('/<b>([^<]+)<\/b>/', $class_body, $competitor_matches); |
67 | for ($competitor_ind = 0; $competitor_ind < $competitors_count; $competitor_ind++) { |
68 | $competitor_line = $competitor_matches[1][$competitor_ind]; |
69 | $rank = intval(mb_substr($competitor_line, 0, 3)); |
70 | $name = trim(mb_substr($competitor_line, 5, 22)); |
71 | $birth_year = trim(mb_substr($competitor_line, 28, 2)); |
72 | $domicile = trim(mb_substr($competitor_line, 32, 18)); |
73 | $club = trim(mb_substr($competitor_line, 51, 18)); |
74 | $result = $this->timeParser->time_str_to_seconds(trim(mb_substr($competitor_line, 70, 8))); |
75 | |
76 | $found_at = mb_strpos($class_body, $competitor_matches[0][$competitor_ind]); |
77 | $competitor_line_ends_at = $found_at + mb_strlen($competitor_matches[0][$competitor_ind]); |
78 | $splits_ends_at = mb_strpos($class_body, '<b>', $competitor_line_ends_at); |
79 | if ($splits_ends_at === false) { |
80 | $splits_ends_at = mb_strlen($class_body); |
81 | } |
82 | $splits_length = $splits_ends_at - $competitor_line_ends_at; |
83 | $splits = trim(mb_substr($class_body, $competitor_line_ends_at, $splits_length)); |
84 | |
85 | $finish_offset = mb_strpos($splits, ' Zi '); |
86 | $finish_split = $this->timeParser->time_str_to_seconds(trim(mb_substr($splits, $finish_offset + 4, 6))); |
87 | |
88 | if ($rank !== 1 || preg_match('/zimmerberg/i', $club)) { |
89 | $solv_result = new SolvResult(); |
90 | $solv_result->setPerson(0); |
91 | $solv_result->setEvent($event_uid); |
92 | $solv_result->setClass($class_name); |
93 | $solv_result->setRank($rank); |
94 | $solv_result->setName($name); |
95 | $solv_result->setBirthYear($birth_year); |
96 | $solv_result->setDomicile($domicile); |
97 | $solv_result->setClub($club); |
98 | $solv_result->setResult($result); |
99 | $solv_result->setSplits($splits); |
100 | $solv_result->setFinishSplit($finish_split); |
101 | $solv_result->setClassDistance($class_info['distance']); |
102 | $solv_result->setClassElevation($class_info['elevation']); |
103 | $solv_result->setClassControlCount($class_info['control_count']); |
104 | $solv_result->setClassCompetitorCount($class_info['competitor_count']); |
105 | $results[] = $solv_result; |
106 | } |
107 | } |
108 | } |
109 | return $results; |
110 | } |
111 | } |