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 | } |