LeaderboardURL = 'https://registrace.teribear.cz/Leaderboard'; $this->LeaderboardURL = 'https://leaderboard.teribear.cz/'; $this->BaseURL = ''; $this->LinkLocaleExceptions = array(); $this->Title = ''; $this->LapLength = 0; // km $this->MoneyKm = 0; // Kč $this->MaxRunnerSpeed = 20; // km/hour $this->MinRunnerSpeed = 2; // km/hour } function GetLatestYear() { $Year = 0; $DbResult = $this->Database->query('SELECT DISTINCT(Year) AS Year FROM `Runner` ORDER BY Year DESC'); if ($DbResult->num_rows > 0) { $DbRow = $DbResult->fetch_assoc(); $Year = $DbRow['Year']; } return $Year; } function YearList($Path, $SelectedYear, $Table = 'Runner', $Where = '1') { $Output = T('Year').': '; $DbResult = $this->Database->query('SELECT DISTINCT(Year) AS Year FROM `'.$Table.'` WHERE '.$Where.' ORDER BY Year ASC'); while ($DbRow = $DbResult->fetch_assoc()) { $Year = $DbRow['Year']; $Item = ''.$Year.''; if ($SelectedYear == $Year) $Item = ''.$Item.''; $Output .= $Item.' '; } return $Output; } function ItemsYearList($Path, $SelectedId, $Table = 'Runner', $Where = '1') { $Output = T('Year').': '; $DbResult = $this->Database->query('SELECT T1.Id AS Id, T2.Year AS Year FROM (SELECT DISTINCT(Id) AS Id FROM `'.$Table.'` WHERE '.$Where.' ORDER BY Year ASC) AS T1 '. 'LEFT JOIN '.$Table.' AS T2 ON T1.Id=T2.Id'); while ($DbRow = $DbResult->fetch_assoc()) { $Item = ''.$DbRow['Year'].''; if ($SelectedId == $DbRow['Id']) $Item = ''.$Item.''; $Output .= $Item.' '; } return $Output; } function ShowMenu() { $Output = '
'. ''.T('Summary').' '. ''.T('Runners').' '. ''.T('Teams').' '. ''.T('Families').' '. $this->ShowLocaleSelector(). '
'; return $Output; } function ShowLocaleSelector() { //$Output .= '
'; $Output = ' '; return $Output; } function ProcessURL() { if (array_key_exists('REDIRECT_QUERY_STRING', $_SERVER)) $PathString = $_SERVER['REDIRECT_QUERY_STRING']; else $PathString = ''; if (substr($PathString, -1, 1) == '/') $PathString = substr($PathString, 0, -1); $PathItems = explode('/', $PathString); if (array_key_exists('REQUEST_URI', $_SERVER) and (strpos($_SERVER['REQUEST_URI'], '?') !== false)) $_SERVER['QUERY_STRING'] = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], '?') + 1); else $_SERVER['QUERY_STRING'] = ''; parse_str($_SERVER['QUERY_STRING'], $_GET); return $PathItems; } // Query: text from name // Page: index of page, one page is 30 items // Category: '', all, men, women, kids, families, teams // TeamId: id of team // Count: number of items per page, default 30 function QueryRunners($Category, $Page = 0, $TeamId = null, $Query = null, $StartNumber = null, $Count = null) { $URL = $this->LeaderboardURL.'/Home/GetRunners?category='.$Category; if ($Page != 0) $URL .= '&page='.$Page; if ($TeamId != null) $URL .= '&teamId='.$TeamId; if ($Count != null) $URL .= '&count='.$Count; if ($Query != null) $URL .= '&query='.$Query; if ($StartNumber != null) $URL .= '&startNumber='.$StartNumber; $Content = file_get_contents($URL); if (($Content !== false) and !empty($Content)) { $JSON = json_decode($Content, true); } else { $JSON = null; echo('Cannot load data from remote server ('.$URL.').
'."\n"); } return $JSON; } function QueryRunnersAll($Category = 'all', $Count = 0) { $ItemsPerPage = 30; $MaxCount = 250 * 30; if ($Count > 0) $MaxCount = $Count; $Result = array(); $I = 0; while (true) { $Page = $I * floor($MaxCount / $ItemsPerPage); $JSON = $this->QueryRunners($Category, $Page, null, null, null, $MaxCount); if ($JSON != null) { foreach ($JSON['items'] as $Item) { // Use RegistrationNumber as ChipNumber. $Item['ChipNumber'] = $Item['RegistrationNumber'] * 1; unset($Item['RegistrationNumber']); if ($Item['Name'] != null) { // Normalize names $Item['Name'] = trim($Item['Name']); while (strpos($Item['Name'], ' ') !== false) { $Item['Name'] = str_replace(' ', ' ', $Item['Name']); } } $Result[] = $Item; } if (($JSON['last'] == 'true') or (count($JSON) == 0)) break; if ($I > 30) break; // Safe limit if last would not work $I++; } else break; } return $Result; } function ShowEmpty() { $Output = ''; $this->Database->query('DELETE FROM RunnerStat'); $this->Database->query('DELETE FROM Runner'); $this->Database->query('DELETE FROM TeamStat'); $this->Database->query('DELETE FROM Team'); $this->Database->query('DELETE FROM Import'); // Find duplicates /* // ALTER TABLE `RunnerStat` ADD `Prev` INT NULL AFTER `Money`; // UPDATE RunnerStat AS A SET Prev = (SELECT Id FROM RunnerStat AS B WHERE A.Runner = B.Runner AND B.Id < A.Id ORDER BY Id DESC LIMIT 1) // SELECT * FROM `RunnerStat` AS A LEFT JOIN RunnerStat AS B ON B.ID=A.Prev WHERE A.Distance=B.Distance $Table = 'Runner'; $TableStat = $Table.'Stat'; //$Queries = array(); $Output .= 'DELETE FROM RunnerStat WHERE Id IN ('; $DbResult = $this->Database->query('SELECT A.Id FROM `RunnerStat` AS A LEFT JOIN RunnerStat AS B ON B.ID=A.Prev WHERE A.Distance=B.Distance'); while ($Item = $DbResult->fetch_assoc()) { $Output .= $Item[Id].', '; } $Output .= ');
'; */ /* // ALTER TABLE `TeamStat` ADD `Prev` INT NULL AFTER `Money`; // UPDATE TeamStat AS A SET Prev = (SELECT Id FROM TeamStat AS B WHERE A.Team = B.Team AND B.Id < A.Id ORDER BY Id DESC LIMIT 1) // SELECT * FROM `TeamStat` AS A LEFT JOIN TeamStat AS B ON B.ID=A.Prev WHERE A.Distance=B.Distance $Table = 'Team'; $TableStat = $Table.'Stat'; //$Queries = array(); $Output .= 'DELETE FROM TeamStat WHERE Id IN ('; $DbResult = $this->Database->query('SELECT A.Id FROM `TeamStat` AS A LEFT JOIN TeamStat AS B ON B.ID=A.Prev WHERE A.Distance=B.Distance'); while ($Item = $DbResult->fetch_assoc()) { $Output .= $Item[Id].', '; } $Output .= ');
'; */ /* $I = 0; $DbResult2 = $this->Database->query('SELECT * FROM Runner WHERE Year=2020'); while ($Runner = $DbResult2->fetch_assoc()) { $Output .= $Runner['Id'].' '.$Runner['Name'].'
'; $Where = '('.$TableStat.'.Runner='.$Runner['Id'].')'; $DbResult = $this->Database->query('SELECT *'. ', (SELECT '.$TableStat.'.Distance - B.Distance FROM '.$TableStat.' AS B WHERE (B.Id < '.$TableStat.'.Id) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Id DESC LIMIT 1) AS Length'. ' FROM '.$TableStat. ' WHERE '.$Where); while ($Item = $DbResult->fetch_assoc()) { if (($Item['Length'] != null) and ($Item['Length'] == 0)) { $Output .= ' '.$Item['Id'].' '.$Item['Time'].' '.$Item['Distance'].' '.$Item['Length'].'
'; $Queries[] = 'DELETE FROM RunnerStat WHERE Id='.$Item[Id]; } } flush(); $I++; if ($I > 500) break; } echo('START TRANSACTION;
'); foreach ($Queries as $Query) { echo($Query.";
"); } echo('COMMIT;
'); */ return $Output; } function Sync($Items, $Time) { $Year = date("Y", $Time); // Load all runners $DbResult = $this->Database->query('SELECT MAX(Id) AS Id FROM Runner'); $DbRow = $DbResult->fetch_assoc(); $NextRunnerId = $DbRow['Id'] + 1; $Runners = array(); $DbResult = $this->Database->query('SELECT Runner.Id, Runner.ChipNumber, Runner.Team, '. '(SELECT RunnerStat.Distance FROM RunnerStat WHERE (RunnerStat.Runner = Runner.Id) ORDER BY RunnerStat.Id DESC LIMIT 1) AS Distance '. 'FROM Runner WHERE Year='.$Year); while ($DbRow = $DbResult->fetch_assoc()) { $Runners[$DbRow['ChipNumber']] = $DbRow; } // Load all teams $DbResult = $this->Database->query('SELECT MAX(Id) AS Id FROM Team'); $DbRow = $DbResult->fetch_assoc(); $NextTeamId = $DbRow['Id'] + 1; $Teams = array(); $DbResult = $this->Database->query('SELECT Team.Id, Team.WebId, Team.Name, '. '(SELECT TeamStat.Distance FROM TeamStat WHERE (TeamStat.Team = Team.Id) ORDER BY TeamStat.Id DESC LIMIT 1) AS Distance '. 'FROM Team WHERE Year='.$Year); while ($DbRow = $DbResult->fetch_assoc()) { $Teams[$DbRow['WebId']] = $DbRow; } $Queries = array(); foreach ($Items as $Item) { if (($Item['Type'] == 'child') or ($Item['Type'] == 'woman') or ($Item['Type'] == 'man')) { if (!array_key_exists($Item['ChipNumber'], $Runners)) { if ($Item['TeamId'] == null) { $TeamId = null; } else { if (!array_key_exists($Item['TeamId'], $Teams)) { $TeamId = $NextTeamId; $Queries[] = $this->Database->GetInsert('Team', array( 'Id' => $TeamId, 'Name' => '', 'WebId' => $Item['TeamId'], 'Year' => $Year, 'IsFamily' => 0, )); $Teams[$Item['TeamId']] = array('Id' => $TeamId, 'Distance' => -1); $NextTeamId++; } else $TeamId = $Teams[$Item['TeamId']]['Id']; } $Gender = 0; if ($Item['Type'] == 'man') $Gender = 1; if ($Item['Type'] == 'woman') $Gender = 2; if ($Item['Type'] == 'child') $Gender = 3; $RunnerId = $NextRunnerId; $Queries[] = $this->Database->GetInsert('Runner', array( 'Id' => $RunnerId, 'Name' => $Item['Name'], 'Gender' => $Gender, 'Team' => $TeamId, 'ChipNumber' => $Item['ChipNumber'], 'Year' => $Year, )); $Runners[$Item['ChipNumber']] = array('Id' => $RunnerId, 'Distance' => -1, 'Team' => $TeamId); $NextRunnerId++; } else { $RunnerId = $Runners[$Item['ChipNumber']]['Id']; // Update runner team if it was changed $OldRunnerTeamId = $Runners[$Item['ChipNumber']]['Team']; if ($Item['TeamId'] == null) { $NewRunnerTeamId = null; } else { if (!array_key_exists($Item['TeamId'], $Teams)) { $NewRunnerTeamId = $NextTeamId; $Queries[] = $this->Database->GetInsert('Team', array( 'Id' => $NewRunnerTeamId, 'Name' => '', 'WebId' => $Item['TeamId'], 'Year' => $Year, 'IsFamily' => 0, )); $Teams[$Item['TeamId']] = array('Id' => $NewRunnerTeamId, 'Distance' => -1); $NextTeamId++; } else { $NewRunnerTeamId = $Teams[$Item['TeamId']]['Id']; } } if ($OldRunnerTeamId != $NewRunnerTeamId) { $Runners[$Item['ChipNumber']]['Team'] = $NewRunnerTeamId; $Queries[] = $this->Database->GetUpdate('Runner', 'Id='.$RunnerId, array('Team' => $NewRunnerTeamId)); } } if ($Runners[$Item['ChipNumber']]['Distance'] != $Item['OverallDistance']) { $Queries[] = $this->Database->GetInsert('RunnerStat', array( 'Time' => TimeToMysqlDateTime($Time), 'Runner' => $RunnerId, 'Distance' => $Item['OverallDistance'], 'Rank' => $Item['Pos'], 'Money' => $Item['Money'], )); } } else if (($Item['Type'] == 'team') or ($Item['Type'] == 'rodina')) { if ($Item['Name'] == null) $Item['Name'] = ''; if ($Item['Type'] == 'rodina') $IsFamily = 1; else $IsFamily = 0; if (!array_key_exists($Item['GroupId'], $Teams)) { $Queries[] = $this->Database->GetInsert('Team', array( 'Id' => $NextTeamId, 'Name' => $Item['Name'], 'WebId' => $Item['GroupId'], 'IsFamily' => $IsFamily, 'Year' => $Year, )); $TeamId = $NextTeamId; $Teams[$Item['GroupId']] = array('Id' => $NextTeamId, 'Distance' => -1); $NextTeamId++; } else $TeamId = $Teams[$Item['GroupId']]['Id']; // Update missing team names if (!array_key_exists('Name', $Teams[$Item['GroupId']]) or $Teams[$Item['GroupId']]['Name'] == "") { $Queries[] = $this->Database->GetUpdate('Team', 'Id='.$TeamId, array( 'Name' => $Item['Name'], 'IsFamily' => $IsFamily )); $Teams[$Item['GroupId']]['Name'] = $Item['Name']; $Teams[$Item['GroupId']]['IsFamily'] = $IsFamily; } if ($Teams[$Item['GroupId']]['Distance'] != $Item['OverallDistance']) { $Queries[] = $this->Database->GetInsert('TeamStat', array( 'Time' => TimeToMysqlDateTime($Time), 'Team' => $TeamId, 'Distance' => $Item['OverallDistance'], 'Rank' => $Item['Pos'], 'Money' => $Item['Money'], )); } } else if ($Item['Type'] == '') { // Skip empty type } else { echo(T('Unsupported type').' "'.$Item['Type'].'".
'); } } //print_r($Queries); //foreach ($Queries as $Query) $this->Database->query($Query); $this->Database->Transaction($Queries); } function ShowSync() { $Time = time(); $Items = $this->QueryRunnersAll('all'); $Output = T('Loaded items count: '.count($Items).'
'); $ItemsWithoutProgress = array(); foreach ($Items as $Item) { unset($Item['Progress']); $ItemsWithoutProgress[] = $Item; } $Hash = md5(serialize($ItemsWithoutProgress)); $DbResult = $this->Database->query('SELECT * FROM Import ORDER BY Time DESC LIMIT 1'); if ($DbResult->num_rows > 0) { $Import = $DbResult->fetch_assoc(); } else $Import = array('Hash' => ''); if ($Import['Hash'] != $Hash) { $this->Sync($Items, $Time); $this->Database->insert('Import', array( 'Time' => TimeToMysqlDateTime($Time), 'Hash' => $Hash, 'ItemCount' => count($Items) )); } $Output .= T('Data synchronization from leaderboard finished.
'); return $Output; } function ShowTeams() { return $this->ShowTeamsInternal(T('Teams'), T('Team'), 'teams', 'team', 'Team', '(IsFamily=0)'); } function ShowFamilies() { return $this->ShowTeamsInternal(T('Families'), T('Family'), 'families', 'family', 'Team', '(IsFamily=1)'); } function ShowTeamsInternal($Title, $TitleItem, $UrlDir, $UrlDirItem, $Table, $Where = '1') { $Output = ''; $this->Title = $Title.' - '.$this->Title; $Output .= '
'.$Title.'
'; $Year = $this->GetYear(); $Where .= ' AND (Year='.$Year.') AND (Hidden=0)'; if (array_key_exists('query', $_GET) and ($_GET['query'] != '')) { $Where .= ' AND (Name LIKE "%'.addslashes($_GET['query']).'%")'; } $Output .= '
'.$this->YearList('/'.$UrlDir.'/', $Year, $Table).' '.T('Name').': '.$this->ShowSearch().'
'; $DbResult = $this->Database->query('SELECT COUNT(*) FROM `'.$Table.'` WHERE '.$Where); $DbRow = $DbResult->fetch_row(); $PageList = GetPageList($DbRow[0]); $Output .= '
'; $Output .= $PageList['Output']; $TableColumns = array( array('Name' => 'Name', 'Title' => T('Name')), array('Name' => 'RunnersCount', 'Title' => T('Runners')), array('Name' => 'Distance', 'Title' => T('Distance')), array('Name' => 'Money', 'Title' => T('Money')), array('Name' => 'DistanceRunner', 'Title' => T('Distance per runner')), array('Name' => 'MoneyRunner', 'Title' => T('Money per runner')), array('Name' => 'Rank', 'Title' => T('Rank')), ); $Order = GetOrderTableHeader($TableColumns, 'Distance', 1); $Output .= ''; $Output .= $Order['Output']; $DbResult = $this->Database->select('Team', '*, '. '(SELECT COUNT(*) FROM Runner WHERE Runner.Team=Team.Id) AS RunnersCount, '. '(SELECT TeamStat.Distance FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY TeamStat.Time DESC LIMIT 1) AS Distance, '. '(SELECT TeamStat.Money FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Money, '. '(SELECT TeamStat.Time FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Time, '. '(SELECT TeamStat.Rank FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Rank, '. 'ROUND((SELECT TeamStat.Distance FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY TeamStat.Time DESC LIMIT 1) / (SELECT COUNT(*) FROM Runner WHERE Runner.Team=Team.Id), 1) AS DistanceRunner, '. 'ROUND((SELECT TeamStat.Money FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) / (SELECT COUNT(*) FROM Runner WHERE Runner.Team=Team.Id)) AS MoneyRunner', $Where.$Order['SQL'].$PageList['SQLLimit']); while ($Item = $DbResult->fetch_assoc()) { if ($Item['Name'] == '') $Item['Name'] = $TitleItem.' '.$Item['WebId']; $Output .= ''. ''. ''. ''. ''. ''. ''. ''. ''; } $Output .= '
'.$Item['Name'].''.$Item['RunnersCount'].''.$Item['Distance'].''.$Item['Money'].''.$Item['DistanceRunner'].''.$Item['MoneyRunner'].''.$Item['Rank'].'
'; $Output .= $PageList['Output']; $Output .= '
'; return $Output; } function RunningState($Time) { $Output = HumanDateTime($Time); if ($Time > time() - 30 * 60) $Output = ''.$Output.''; return $Output; } function ShowTeam() { return $this->ShowTeamInternal(T('Team'), 'team'); } function ShowFamily() { return $this->ShowTeamInternal(T('Family'), 'family'); } function ShowTeamInternal($Title, $UrlDir) { $Output = ''; $TeamId = 0; if ((count($this->PathItems) > 0) and ($this->PathItems[count($this->PathItems) - 1] != '')) $TeamId = $this->PathItems[count($this->PathItems) - 1]; if (!is_numeric($TeamId)) die(T('Team id needs to be numeric')); $DbResult = $this->Database->query('SELECT Team.*, '. '(SELECT COUNT(*) FROM Runner WHERE (Runner.Team=Team.Id) AND (Runner.Hidden=0)) AS RunnerCount, '. '(SELECT TeamStat.Distance FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY TeamStat.Time DESC LIMIT 1) AS Distance, '. '(SELECT TeamStat.Money FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Money, '. '(SELECT TeamStat.Rank FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Rank '. 'FROM Team WHERE (Hidden=0) AND (Id='.$TeamId.')'); if ($DbResult->num_rows > 0) { $DbRow = $DbResult->fetch_assoc(); $this->Title = $Title.' '.$DbRow['Name'].' - '.$this->Title; $Output .= '
'.$Title.' '.$DbRow['Name'].'
'; $Output .= '
'.$this->ItemsYearList('/'.$UrlDir.'/', $TeamId, 'Team', 'Name="'.$this->Database->real_escape_string($DbRow['Name']).'"').'
'; $this->LoadYearParameters($DbRow['Year']); $Output .= '
'. T('Runners').': '.$DbRow['RunnerCount'].', '. T('Distance').': '.$DbRow['Distance'].' km, '. T('Money').': '.$DbRow['Money'].' Kč, '. T('Rank').': '.$DbRow['Rank'].'
'; $Where = '(Hidden=0) AND (Team='.$TeamId.')'; // Show runners $DbResult = $this->Database->query('SELECT COUNT(*) FROM `Runner` WHERE '.$Where); $DbRow = $DbResult->fetch_row(); $PageList = GetPageList($DbRow[0]); $Gender = array('', T('Man'), T('Woman'), T('Kid')); $Output .= '
'; $Output .= $PageList['Output']; $TableColumns = array( array('Name' => 'Name', 'Title' => T('Name')), array('Name' => 'Gender', 'Title' => T('Category')), array('Name' => 'Distance', 'Title' => T('Distance')), array('Name' => 'Money', 'Title' => T('Money')), array('Name' => 'Rank', 'Title' => T('Rank')), array('Name' => 'Time', 'Title' => T('Last change')), ); $Order = GetOrderTableHeader($TableColumns, 'Distance', 1); $Output .= ''; $Output .= $Order['Output']; $DbResult = $this->Database->select('Runner', '*, '. '(SELECT RunnerStat.Distance FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Distance'. ', (SELECT RunnerStat.Money FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Money'. ', (SELECT RunnerStat.Time FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Time'. ', (SELECT RunnerStat.Rank FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Rank', $Where.$Order['SQL'].$PageList['SQLLimit']); while ($Item = $DbResult->fetch_assoc()) { $Output .= ''. ''. ''. ''. ''. ''. ''. ''; } $Output .= '
'.$Item['Name'].''.$Gender[$Item['Gender']].''.$Item['Distance'].''.$Item['Money'].''.$Item['Rank'].''.$this->RunningState(MysqlDateTimeToTime($Item['Time'])).'
'; $Output .= $PageList['Output']; $Output .= '

'; $Output .= $this->ShowDetailed('Team', $TeamId); //$Output .= $this->ShowDetailedChart('Team', $TeamId); $Output .= $this->ShowDaily('Team', $TeamId); //$Output .= $this->ShowDailyChart('Team', $TeamId); } else $Output .= T('Team not found.'); return $Output; } function ShowDetailed($Table, $Id) { $PrefixMultiplier = new PrefixMultiplier(); $TableStat = $Table.'Stat'; $Output = '
'.T('Lap progress').'
'; $Where = $TableStat.'.'.$Table.'='.$Id; $DbResult = $this->Database->query('SELECT COUNT(*) FROM '.$TableStat.' WHERE '.$Where); $DbRow = $DbResult->fetch_row(); $PageList = GetPageList($DbRow[0]); $Output .= '
'; $Output .= $PageList['Output']; $TableColumns = array( array('Name' => 'Time', 'Title' => T('Time')), array('Name' => 'Distance', 'Title' => T('Distance').' [km]'), array('Name' => 'Money', 'Title' => T('Money').' [Kč]'), array('Name' => 'Rank', 'Title' => T('Rank')), array('Name' => 'Duration', 'Title' => T('Duration')), array('Name' => 'Length', 'Title' => T('Length').' [km]'), array('Name' => 'Speed', 'Title' => T('Speed').' [km/'.T('hour').']'), ); $Order = GetOrderTableHeader($TableColumns, 'Time', 1); $Output .= ''; $Output .= $Order['Output']; $DbResult = $this->Database->query('SELECT *'. ', (SELECT '.$TableStat.'.Distance - B.Distance FROM '.$TableStat.' AS B WHERE (B.Time < '.$TableStat.'.Time) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Length'. ', (SELECT TIME_TO_SEC(TIMEDIFF('.$TableStat.'.Time, B.Time)) FROM '.$TableStat.' AS B WHERE (B.Time < '.$TableStat.'.Time) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Duration'. ', NULL AS Speed'. ' FROM '.$TableStat. ' WHERE '.$Where.$Order['SQL'].$PageList['SQLLimit']); while ($Item = $DbResult->fetch_assoc()) { $Output .= ''. ''. ''. ''. ''; if (($Item['Duration'] != null) and ($Item['Duration'] < $Item['Length'] / $this->MinRunnerSpeed * 3600)) $Output .= ''; else $Output .= ''; $Output .= ''; if (($Item['Duration'] > 0) and ($Item['Duration'] < $Item['Length'] * 3600 / $this->MinRunnerSpeed)) { $Speed = $Item['Length'] / $Item['Duration'] * 3600; $Output .= ''; } else $Output .= ''; $Output .= ''; } $Output .= '
'.HumanDateTime(MysqlDateTimeToTime($Item['Time'])).''.$Item['Distance'].''.$Item['Money'].''.$Item['Rank'].''.$PrefixMultiplier->Add($Item['Duration'], '', 4, 'Time').' '.$Item['Length'].''.$PrefixMultiplier->Add($Speed, '', 4, 'Decimal').' 
'; $Output .= $PageList['Output']; $Output .= '

'; return $Output; } function ShowDetailedChart($Table, $Id) { $TableStat = $Table.'Stat'; $DbResult = $this->Database->query('SELECT *'. ', (SELECT '.$TableStat.'.Distance - B.Distance FROM '.$TableStat.' AS B WHERE (B.Time < '.$TableStat.'.Time) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Length'. ', (SELECT TIME_TO_SEC(TIMEDIFF('.$TableStat.'.Time, B.Time)) FROM '.$TableStat.' AS B WHERE (B.Time < '.$TableStat.'.Time) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Duration'. ', NULL AS Speed'. ' FROM '.$TableStat. ' WHERE '.$TableStat.'.'.$Table.'='.$Id.' ORDER BY Time'); $ChartValues = array(); while ($Item = $DbResult->fetch_assoc()) { $ChartValues[MysqlDateTimeToTime($Item['Time'])] = $Item['Distance']; } $Output = $this->ShowChart($Table.'Detailed', $ChartValues, T('Lap progress')); return $Output; } function ShowDaily($Table, $Id) { $PrefixMultiplier = new PrefixMultiplier(); $Where = '1'; $TableStat = $Table.'Stat'; $DailyTableMaxId = 'SELECT * FROM (SELECT MAX(Id) AS MaxId FROM '.$TableStat.' AS T1 WHERE T1.'.$Table.'='.$Id.' GROUP BY DATE(Time)) AS T2 LEFT JOIN '.$TableStat.' AS T3 ON T3.Id=T2.MaxId'; $DailyTableMinId = 'SELECT * FROM (SELECT MIN(Id) AS MinId FROM '.$TableStat.' AS T1 WHERE T1.'.$Table.'='.$Id.' GROUP BY DATE(Time)) AS T2 LEFT JOIN '.$TableStat.' AS T3 ON T3.Id=T2.MinId'; $DbResult = $this->Database->query('SELECT COUNT(*) FROM ('.$DailyTableMaxId.') AS B'); $DbRow = $DbResult->fetch_row(); $PageList = GetPageList($DbRow[0]); $Output = '
'.T('Daily progress').'
'; $Output .= '
'; $Output .= $PageList['Output']; $TableColumns = array( array('Name' => 'Time', 'Title' => T('Time')), array('Name' => 'Distance', 'Title' => T('Distance').' [km]'), array('Name' => 'Money', 'Title' => T('Money').' [Kč]'), array('Name' => 'Rank', 'Title' => T('Rank')), array('Name' => 'Duration', 'Title' => T('Duration')), array('Name' => 'Length', 'Title' => T('Length').' [km]'), array('Name' => 'Speed', 'Title' => T('Speed').' [km/'.T('hour').']'), ); $Order = GetOrderTableHeader($TableColumns, 'Time', 1); $Output .= ''; $Output .= $Order['Output']; if ($this->LapLength == 0) { $DbResult = $this->Database->query('SELECT * '. ', COALESCE((SELECT T4.Distance - B.Distance FROM ('.$DailyTableMaxId.') AS B WHERE (DATE(B.Time) < DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.') ORDER BY B.Time DESC LIMIT 1), T4.Distance) AS Length'. ', NULL AS Duration'. ', NULL AS Speed'. ' FROM ('.$DailyTableMaxId.') AS T4'. ' WHERE '.$Where.$Order['SQL'].$PageList['SQLLimit']); } else { $DbResult = $this->Database->query('SELECT * '. ', COALESCE((SELECT T4.Distance - B.Distance FROM ('.$DailyTableMaxId.') AS B WHERE (DATE(B.Time) < DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.') ORDER BY B.Time DESC LIMIT 1), T4.Distance) AS Length'. //', (SELECT COUNT(*) * '.$this->LapLength.' FROM '.$TableStat.' AS B WHERE (DATE(B.Time) = DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.')) AS LapLength'. ', (SELECT TIME_TO_SEC(TIMEDIFF(T4.Time, B.Time)) / (Length - '.$this->LapLength.') * Length FROM ('.$DailyTableMinId.') AS B WHERE (DATE(B.Time) = DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Duration'. ', NULL AS Speed'. ' FROM ('.$DailyTableMaxId.') AS T4'. ' WHERE '.$Where.$Order['SQL'].$PageList['SQLLimit']); } while ($Item = $DbResult->fetch_assoc()) { $Output .= ''. ''. ''. ''. ''; if ($Item['Duration'] != null) $Output .= ''; else $Output .= ''; $Output .= ''; if ($Item['Duration'] > 0) $Output .= ''; else $Output .= ''; $Output .= ''; } $Output .= '
'.HumanDate(MysqlDateTimeToTime($Item['Time'])).''.$Item['Distance'].''.$Item['Money'].''.$Item['Rank'].''.$PrefixMultiplier->Add($Item['Duration'], '', 4, 'Time').' '.$Item['Length'].''.$PrefixMultiplier->Add($Item['Length'] / $Item['Duration'] * 3600, '', 4, 'Decimal').' 
'; $Output .= $PageList['Output']; $Output .= '
'; return $Output; } function ShowDailyChart($Table, $Id) { $TableStat = $Table.'Stat'; $DailyTableMaxId = 'SELECT * FROM (SELECT MAX(Id) AS MaxId FROM '.$TableStat.' AS T1 WHERE T1.'.$Table.'='.$Id.' GROUP BY DATE(Time)) AS T2 LEFT JOIN '.$TableStat.' AS T3 ON T3.Id=T2.MaxId'; $DbResult = $this->Database->query('SELECT * '. ', COALESCE((SELECT T4.Distance - B.Distance FROM ('.$DailyTableMaxId.') AS B WHERE (DATE(B.Time) < DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.') ORDER BY B.Time DESC LIMIT 1), T4.Distance) AS Length'. ' FROM ('.$DailyTableMaxId.') AS T4'. ' ORDER BY Time'); $ChartValues = array(); while ($Item = $DbResult->fetch_assoc()) { $ChartValues[MysqlDateTimeToTime($Item['Time'])] = $Item['Length']; } $Output = $this->ShowChart($Table.'Daily', $ChartValues, T('Daily progress')); return $Output; } function ShowSearch() { if (array_key_exists('query', $_GET)) $Query = $_GET['query']; else $Query = ''; $Output = ''; $Output .= ' '. ''; $Output .= '
'; return $Output; } function ShowRunnersAll() { return $this->ShowRunners(); } function ShowRunnersMen() { return $this->ShowRunners('(Gender=1)'); } function ShowRunnersWomen() { return $this->ShowRunners('(Gender=2)'); } function ShowRunnersKids() { return $this->ShowRunners('(Gender=3)'); } function ShowRunners($Where = '1') { $this->Title = T('Runners').' - '.$this->Title; $Output = '
'.T('Runners').'
'; $Output .= '
'. ''.T('All').' '. ''.T('Men').' '. ''.T('Women').' '. ''.T('Kids').''. '
'; $Year = $this->GetYear(); $Output .= '
'.$this->YearList('/runners/', $Year, 'Runner').' '.T('Name').': '.$this->ShowSearch().'
'; $Where .= ' AND (Year='.$Year.') AND (Hidden=0)'; if (array_key_exists('query', $_GET) and ($_GET['query'] != '')) { $Where .= ' AND (Name LIKE "%'.addslashes($_GET['query']).'%")'; } $DbResult = $this->Database->query('SELECT COUNT(*) FROM `Runner` WHERE '.$Where); $DbRow = $DbResult->fetch_row(); $PageList = GetPageList($DbRow[0]); $Gender = array('', T('Man'), T('Woman'), T('Kid')); $Output .= '
'; $Output .= $PageList['Output']; $TableColumns = array( array('Name' => 'Name', 'Title' => T('Name')), array('Name' => 'Gender', 'Title' => T('Category')), array('Name' => 'Distance', 'Title' => T('Distance')), array('Name' => 'Money', 'Title' => T('Money')), array('Name' => 'Rank', 'Title' => T('Rank')), array('Name' => 'Time', 'Title' => T('Last change')), ); $Order = GetOrderTableHeader($TableColumns, 'Distance', 1); $Output .= ''; $Output .= $Order['Output']; $DbResult = $this->Database->select('Runner', '*, '. '(SELECT RunnerStat.Distance FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Distance'. ', (SELECT RunnerStat.Money FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Money'. ', (SELECT RunnerStat.Time FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Time'. ', (SELECT RunnerStat.Rank FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Rank', $Where.$Order['SQL'].$PageList['SQLLimit']); while ($Item = $DbResult->fetch_assoc()) { $Output .= ''. ''. ''. ''. ''. ''. ''. ''; } $Output .= '
'.$Item['Name'].''.$Gender[$Item['Gender']].''.$Item['Distance'].''.$Item['Money'].''.$Item['Rank'].''.$this->RunningState(MysqlDateTimeToTime($Item['Time'])).'
'; $Output .= $PageList['Output']; $Output .= '
'; return $Output; } function ShowRunner() { $PrefixMultiplier = new PrefixMultiplier(); $Output = ''; $RunnerId = 0; if ((count($this->PathItems) > 0) and ($this->PathItems[count($this->PathItems) - 1] != '')) $RunnerId = $this->PathItems[count($this->PathItems) - 1]; if (!is_numeric($RunnerId)) die(T('Runner id needs to be numeric')); $DbResult = $this->Database->query('SELECT Runner.Name, Team.Name AS TeamName, Team.Id AS TeamId, Runner.Year FROM Runner '. 'LEFT JOIN Team ON Team.Id=Runner.Team WHERE (Runner.Hidden=0) AND (Runner.Id='.$RunnerId.')'); if ($DbResult->num_rows > 0) { $DbRow = $DbResult->fetch_assoc(); $this->Title = T('Runner').' '.$DbRow['Name'].' - '.$this->Title; $Output .= '
'.T('Runner').' '.$DbRow['Name'].'
'; if ($DbRow['TeamName'] != '') $Output .= '
'.$DbRow['TeamName'].'
'; $Output .= '
'.$this->ItemsYearList('/runner/', $RunnerId, 'Runner', 'Name="'.$this->Database->real_escape_string($DbRow['Name']).'"').'
'; $this->LoadYearParameters($DbRow['Year']); $Output .= $this->ShowDetailed('Runner', $RunnerId); //$Output .= $this->ShowDetailedChart('Runner', $RunnerId); $Output .= $this->ShowDaily('Runner', $RunnerId); //$Output .= $this->ShowDailyChart('Runner', $RunnerId); } else $Output .= T('Runner not found.'); return $Output; } function GetTotals($Where = '1', $Table = 'Runner') { $DbResult = $this->Database->query('SELECT (SELECT COUNT(*) FROM '.$Table.' WHERE '.$Where.') AS TotalCount, '. '(SELECT SUM(T1.Distance) FROM (SELECT (SELECT Distance FROM '.$Table.'Stat WHERE '.$Table.'Stat.'.$Table.' = '.$Table.'.Id ORDER BY Time DESC LIMIT 1) AS Distance FROM '.$Table.' WHERE '.$Where.') AS T1) AS TotalDistance, '. '(SELECT SUM(T2.Money) FROM (SELECT (SELECT Money FROM '.$Table.'Stat WHERE '.$Table.'Stat.'.$Table.' = '.$Table.'.Id ORDER BY Time DESC LIMIT 1) AS Money FROM '.$Table.' WHERE '.$Where.') AS T2) AS TotalMoney'); $DbRow = $DbResult->fetch_assoc(); return $DbRow; } function GetYear() { $Year = 0; if (count($this->PathItems) > 0) { $Param = $this->PathItems[count($this->PathItems) - 1]; if (is_numeric($this->PathItems[count($this->PathItems) - 1])) $Year = $this->PathItems[count($this->PathItems) - 1] * 1; } if ($Year == 0) $Year = $this->GetLatestYear(); $this->LoadYearParameters($Year); return $Year; } function ShowMain() { $Output = ''; $Output .= '

'.sprintf(T('This website collects data from official leaderboard site and tracks and presents progress of runners and teams.'), $this->LeaderboardURL).'

'; $Output .= '
'.T('Summary').'
'; $Year = $this->GetYear(); $Output .= '
'.$this->YearList('/', $Year).'
'; $Runners = $this->GetTotals('(Year='.$Year.')'); $Men = $this->GetTotals('(Runner.Gender=1) AND (Year='.$Year.')', 'Runner'); $Women = $this->GetTotals('(Runner.Gender=2) AND (Year='.$Year.')', 'Runner'); $Kids = $this->GetTotals('(Runner.Gender=3) AND (Year='.$Year.')', 'Runner'); $Teams = $this->GetTotals('(Team.IsFamily=0) AND (Year='.$Year.')', 'Team'); $Families = $this->GetTotals('(Team.IsFamily=1) AND (Year='.$Year.')', 'Team'); $Output .= ''; $Output .= ''; $Output .= ''; $Output .= ''; $Output .= ''; $Output .= ''; $Output .= ''; $Output .= ''; $Output .= '
'.T('Category').''.T('Count').''.T('Distance').' [km]'.T('Money').' [Kč]
'.T('Everyone').''.$Runners['TotalCount'].''.$Runners['TotalDistance'].''.$Runners['TotalMoney'].'
'.T('Men').''.$Men['TotalCount'].''.$Men['TotalDistance'].''.$Men['TotalMoney'].'
'.T('Women').''.$Women['TotalCount'].''.$Women['TotalDistance'].''.$Women['TotalMoney'].'
'.T('Kids').''.$Kids['TotalCount'].''.$Kids['TotalDistance'].''.$Kids['TotalMoney'].'
'.T('Teams').''.$Teams['TotalCount'].''.$Teams['TotalDistance'].''.$Teams['TotalMoney'].'
'.T('Families').''.$Families['TotalCount'].''.$Families['TotalDistance'].''.$Families['TotalMoney'].'
'; return $Output; } function ShowPage($Content) { global $Config; $Lang = 'en'; $Output = 'Config['Encoding'].'"?>'."\n". ''. ''. ''. ''. ''. ''. ''; //''; $Output .= ''.$this->Title.''. ''; $Output .= $Content; $Output .= '
'; $Output .= ''; return $Output; } function Link($Target) { if (substr($Target, 0, strlen($this->BaseURL)) == $this->BaseURL) $Remaining = substr($Target, strlen($this->BaseURL)); else $Remaining = $Target; $TargetParts = explode('/', $Remaining); if ((count($TargetParts) > 0) and ($TargetParts[0] == '')) array_splice($TargetParts, 0, 1); if (count($TargetParts) > 0) { if (in_array($TargetParts[0], $this->LinkLocaleExceptions)) { $Result = $this->BaseURL.$Target; } else $Result = $this->LinkLocale($Target); } else $Result = $this->LinkLocale($Target); return $Result; } function TranslateURL($URL, $Locale) { // Try translate URL directory parts from current locale to target locale $Remaining = $URL; $RemainingParts = explode('?', $Remaining); $Directory = $RemainingParts[0]; if (count($RemainingParts) > 1) { $Params = $RemainingParts[1]; } else { $Params = ''; } $TargetLocaleManager = new LocaleManager($this); $TargetLocaleManager->Dir = $this->LocaleManager->Dir; $TargetLocaleManager->Available = $this->LocaleManager->Available; $TargetLocaleManager->LoadLocale($Locale); $DirectoryParts = explode('/', $Directory); foreach ($DirectoryParts as $Index => $Item) { $NewText = $TargetLocaleManager->CurrentLocale->Texts->Translate($Item, 'URL'); $DirectoryParts[$Index] = $NewText; } $Directory = implode('/', $DirectoryParts); $Remaining = $Directory; if ($Params != '') $Remaining .= '?'.$Params; return $Remaining; } function TranslateReverseURL($URL, $Locale) { // Try translate URL directory parts from current locale to target locale $Remaining = $URL; $RemainingParts = explode('?', $Remaining); $Directory = $RemainingParts[0]; if (count($RemainingParts) > 1) { $Params = $RemainingParts[1]; } else { $Params = ''; } $TargetLocaleManager = new LocaleManager($this); $TargetLocaleManager->Dir = $this->LocaleManager->Dir; $TargetLocaleManager->Available = $this->LocaleManager->Available; $TargetLocaleManager->LoadLocale($Locale); $DirectoryParts = explode('/', $Directory); foreach ($DirectoryParts as $Index => $Item) { $NewText = $TargetLocaleManager->CurrentLocale->Texts->TranslateReverse($Item, 'URL'); $DirectoryParts[$Index] = $NewText; } $Directory = implode('/', $DirectoryParts); $Remaining = $Directory; if ($Params != '') $Remaining .= '?'.$Params; return $Remaining; } function LinkLocale($Target, $Locale = '') { if ($Locale == '') $Locale = $this->LocaleManager->LangCode; $Target = $this->TranslateURL($Target, $Locale); if ($Locale == $this->LocaleManager->DefaultLangCode) return $this->BaseURL.$Target; return $this->BaseURL.'/'.$Locale.$Target; } function LoadYearParameters($Year) { $DbResult = $this->Database->query('SELECT * FROM `Year` WHERE `Year`.`Year`='.$Year); if ($DbResult->num_rows > 0) { $DbRow = $DbResult->fetch_assoc(); $this->LapLength = $DbRow['LapLength']; $this->MoneyKm = $DbRow['MoneyKm']; } } function ShowChart($ChartName, $Values, $Title) { $Output = '
'. ' '. '
'. '"; return $Output; } function Run() { global $Config, $GlobalLocaleManager; $this->Config = $Config; $this->Database = new Database(); $this->Database->Connect($this->Config['Database']['Host'], $this->Config['Database']['User'], $this->Config['Database']['Password'], $this->Config['Database']['Database']); $this->Database->Prefix = $this->Config['Database']['Prefix']; $this->Database->charset($this->Config['Database']['Charset']); //$this->Database->ShowSQLError = true; //$this->Database->ShowSQLQuery = true; $this->LocaleManager = new LocaleManager($this); $this->LocaleManager->Dir = dirname(__FILE__).'/Locale'; $this->LocaleManager->Available = array( 'cs' => array('Code' => 'cs', 'Title' => 'Česky'), 'en' => array('Code' => 'en', 'Title' => 'English'), ); $GlobalLocaleManager = $this->LocaleManager; $this->LinkLocaleExceptions = array('style', 'Packages'); $this->PathItems = $this->ProcessURL(); // Detect interface locale if (isset($this->Config['Locale'])) $this->LocaleManager->DefaultLangCode = $this->Config['Locale']; $this->LocaleManager->LangCode = $this->LocaleManager->DefaultLangCode; if (count($this->PathItems) > 0) { $NewLangCode = $this->PathItems[0]; if (array_key_exists($NewLangCode, $this->LocaleManager->Available)) { array_shift($this->PathItems); $this->LocaleManager->LangCode = $NewLangCode; } } if (array_key_exists($this->LocaleManager->LangCode, $this->LocaleManager->Available)) { $this->LocaleManager->LoadLocale($this->LocaleManager->LangCode); } InitPrefixMultipliers(); if (GetRemoteAddress() != '') { $this->BaseURL = $_SERVER["CONTEXT_PREFIX"]; if (substr($this->BaseURL, -1, 1) == '/') $this->BaseURL = substr($this->BaseURL, 0, -1); } $this->Title = T('Teribear stats'); $Output = ''; $this->Year = 0; if (count($this->PathItems) > 0) { $Item = $this->PathItems[0]; $Item = $this->LocaleManager->CurrentLocale->Texts->TranslateReverse($Item, 'URL'); if ($Item == 'sync') $Output .= $this->ShowSync(); //else if ($Item == 'empty') $Output .= $this->ShowEmpty(); else if ($Item == 'runner') $Output .= $this->ShowRunner(); else if ($Item == 'runners') $Output .= $this->ShowRunnersAll(); else if ($Item == 'men') $Output .= $this->ShowRunnersMen(); else if ($Item == 'women') $Output .= $this->ShowRunnersWomen(); else if ($Item == 'kids') $Output .= $this->ShowRunnersKids(); else if ($Item == 'team') $Output .= $this->ShowTeam(); else if ($Item == 'teams') $Output .= $this->ShowTeams(); else if ($Item == 'family') $Output .= $this->ShowFamily(); else if ($Item == 'families') $Output .= $this->ShowFamilies(); else $Output .= $this->ShowMain(); } else $Output .= $this->ShowMain(); if (!$this->NoFullPage) { $Output = $this->ShowMenu().$Output; echo($this->ShowPage($Output)); } else echo($Output); } } $Application = new MyApplication(); $Application->Run();