From 1b924d1ac5c95704f4b56a12a22b7ecc4fdd9a27 Mon Sep 17 00:00:00 2001 From: Jacob Penner Date: Mon, 19 Jan 2026 13:17:58 -0500 Subject: [PATCH] feat: Add phonetic name support for racers in registration and editing --- website/ajax/action.racer.add.inc | 8 + website/ajax/action.racer.edit.inc | 8 + website/ajax/action.racer.import.inc | 6 + website/ajax/query.poll.inc | 34 + website/checkin.php | 7 +- website/inc/checkin-table.inc | 2 + website/inc/json-current-racers.inc | 61 +- website/inc/name-mangler.inc | 8 +- website/inc/newracer.inc | 206 +++--- website/inc/schema_version.inc | 3 +- website/js/checkin.js | 772 +++++++++++++---------- website/js/now-racing-poller-phonetic.js | 30 + website/kiosks/now-racing-phonetic.kiosk | 106 ++++ website/sql/access/schema.inc | 2 + website/sql/access/update-schema.inc | 22 +- website/sql/sqlite/schema.inc | 2 + website/sql/sqlite/update-schema.inc | 173 ++--- 17 files changed, 835 insertions(+), 615 deletions(-) create mode 100644 website/js/now-racing-poller-phonetic.js create mode 100644 website/kiosks/now-racing-phonetic.kiosk diff --git a/website/ajax/action.racer.add.inc b/website/ajax/action.racer.add.inc index b8171b04c..e6c8cb924 100644 --- a/website/ajax/action.racer.add.inc +++ b/website/ajax/action.racer.add.inc @@ -18,6 +18,14 @@ if (have_permission(REGISTER_NEW_RACER_PERMISSION)) { 'note_from' => $_POST['note_from'], 'partitionid' => $partitionid, 'exclude' => @$_POST['exclude'] ? true : false); + + if (isset($_POST['phonetic_firstname'])) { + $options['phonetic_firstname'] = $_POST['phonetic_firstname']; + } + if (isset($_POST['phonetic_lastname'])) { + $options['phonetic_lastname'] = $_POST['phonetic_lastname']; + } + if (isset($_POST['partitionid']) && $_POST['partitionid'] > 0) { $options['partitionid'] = $_POST['partitionid']; } diff --git a/website/ajax/action.racer.edit.inc b/website/ajax/action.racer.edit.inc index 600e3902a..01997328d 100644 --- a/website/ajax/action.racer.edit.inc +++ b/website/ajax/action.racer.edit.inc @@ -60,6 +60,14 @@ if (have_permission(EDIT_RACER_PERMISSION)) { $assignments .= ', lastname = :lastname'; $values[':lastname'] = trim($_POST['lastname']); } + if (isset($_POST['phonetic_firstname'])) { + $assignments .= ', phonetic_firstname = :phonetic_firstname'; + $values[':phonetic_firstname'] = trim($_POST['phonetic_firstname']); + } + if (isset($_POST['phonetic_lastname'])) { + $assignments .= ', phonetic_lastname = :phonetic_lastname'; + $values[':phonetic_lastname'] = trim($_POST['phonetic_lastname']); + } if ($ok && isset($_POST['carno'])) { $assignments .= ', carnumber = :carnumber'; $values[':carnumber'] = trim($_POST['carno']); diff --git a/website/ajax/action.racer.import.inc b/website/ajax/action.racer.import.inc index c4ee0ed5c..d7c223297 100644 --- a/website/ajax/action.racer.import.inc +++ b/website/ajax/action.racer.import.inc @@ -86,6 +86,12 @@ if ($ok) { if (isset($_POST['partition'])) { $options['partition'] = trim(@$_POST['partition']); } + if (isset($_POST['phonetic_firstname'])) { + $options['phonetic_firstname'] = trim($_POST['phonetic_firstname']); + } + if (isset($_POST['phonetic_lastname'])) { + $options['phonetic_lastname'] = trim($_POST['phonetic_lastname']); + } $racerid = insert_new_racer($options); json_out('racerid', $racerid); diff --git a/website/ajax/query.poll.inc b/website/ajax/query.poll.inc index 9a3f29468..0da942623 100644 --- a/website/ajax/query.poll.inc +++ b/website/ajax/query.poll.inc @@ -122,6 +122,40 @@ function json_common_1($key, &$now_running) { json_current_racers($now_running, read_name_style(), $render, $render2)); } break; + case 'racers-phonetic': // Same as 'racers' but forces phonetic name style + require_once('inc/name-mangler.inc'); + require_once('inc/json-current-racers.inc'); + if ($now_running['roundid'] == TIMER_TEST_ROUNDID) { + $racers = array(); + $nlanes = get_lane_count(); + for ($lane = 1; $lane <= $nlanes; ++$lane) { + if (($tt['mask'] & (1 << ($lane - 1))) == 0) { + $racers[] = array('lane' => $lane); + } + } + json_out('racers', $racers); + } else { + require_once('inc/photos-on-now-racing.inc'); + $render = false; + $render2 = false; + if (isset($_GET['head-size'])) { + $render = photo_repository('head')->lookup_or_any($_GET['head-size']); + if (isset($_GET['car-size'])) { + $render2 = photo_repository('car')->lookup_or_any($_GET['car-size']); + } + } else if (isset($_GET['row-height']) && $_GET['row-height'] > 0) { + if (isset($_GET['repo'])) { + $photos = $_GET['repo']; + if (!photo_repository($photos)) { $photos = 0; } + } else { + $photos = read_photos_on_now_racing(); + } if ($photos) { + $render_name = (2 * $_GET['row-height']).'x'.$_GET['row-height']; + $render = photo_repository($photos)->lookup_or_any($render_name); + } + } + json_out('racers', json_current_racers($now_running, PHONETIC_NAME, $render, $render2)); + } case 'replay-state': require_once('inc/json-replay.inc'); json_out('replay-state', json_replay()); diff --git a/website/checkin.php b/website/checkin.php index bdb8c5fcb..b9f7ac011 100644 --- a/website/checkin.php +++ b/website/checkin.php @@ -238,6 +238,11 @@ function addrow0(racer) { + + + + + @@ -390,7 +395,7 @@ class="delete_button" data-off-text="Excluded" data-on-text="Eligible"/> - + diff --git a/website/inc/checkin-table.inc b/website/inc/checkin-table.inc index 153e531ba..b3e28a8de 100644 --- a/website/inc/checkin-table.inc +++ b/website/inc/checkin-table.inc @@ -9,6 +9,8 @@ function checkin_table_SELECT_FROM_sql() { $schema_version = schema_version(); return 'SELECT racerid, carnumber, lastname, firstname, carname, imagefile,' + .($schema_version < PHONETIC_NAMES_SCHEMA ? " '' AS phonetic_lastname, '' AS phonetic_firstname," + : " phonetic_lastname, phonetic_firstname,") .($schema_version < 2 ? " '' AS carphoto," : " carphoto,") .($schema_version < PARTITION_SCHEMA ? " 1 AS partitionid, 1 AS partition_sortorder, '".DEFAULT_PARTITION_NAME."' AS partition_name," diff --git a/website/inc/json-current-racers.inc b/website/inc/json-current-racers.inc index af4ed4da4..12d10a42d 100644 --- a/website/inc/json-current-racers.inc +++ b/website/inc/json-current-racers.inc @@ -11,47 +11,26 @@ require_once('inc/schema_version.inc'); // $photo_render if a PhotoRender or false function json_current_racers(&$now_running, $name_style, $photo_render, $photo_render2 = false) { global $db; - + $use_subgroups = use_subgroups(); $time_format = get_finishtime_formatting_string(); - $stmt = $db->prepare('SELECT lane, RegistrationInfo.racerid,' - .' carnumber, lastname, firstname, carname, rank,' - .' finishtime, finishplace, imagefile, ' - .(schema_version() >= 2 ? 'carphoto' : "'' AS carphoto").', ' - .(schema_version() >= PARTITION_SCHEMA ? ' note' : "'' AS note") - .' FROM ' - .inner_join('RaceChart', 'RegistrationInfo', - 'RaceChart.racerid = RegistrationInfo.racerid', - 'Ranks', - 'RegistrationInfo.rankid = Ranks.rankid') - .' WHERE roundid = :roundid' - .' AND heat = :heat' - .' ORDER BY lane'); - $stmt->execute(array(':roundid' => $now_running['roundid'], - ':heat' => $now_running['heat'])); - $racers = array(); - - foreach ($stmt as $row) { - $racer = array( - 'lane' => $row['lane'], - 'racerid' => $row['racerid'], - 'name' => mangled_name($row, $name_style), - 'carname' => $row['carname'], - 'carnumber' => $row['carnumber'], - 'note' => $row['note'], - 'photo' => $photo_render ? $photo_render->url_for_row($row) : '', - 'finishtime' => $row['finishtime'] ? sprintf($time_format, $row['finishtime']) : '', - 'finishplace' => $row['finishplace'] ? $row['finishplace'] : ''); - if ($photo_render2) { - $racer['photo2'] = $photo_render2->url_for_row($row); - } - if ($use_subgroups) { - $racer['subgroup'] = $row['rank']; - } - $racers[] = $racer; - } - - return $racers; -} -?> \ No newline at end of file + $stmt = $db->prepare('SELECT lane, RegistrationInfo.racerid,' .' carnumber, +lastname, firstname,' .(schema_version() >= PHONETIC_NAMES_SCHEMA ? ' +phonetic_lastname, phonetic_firstname,' : ' \'\' AS phonetic_lastname, \'\' AS +phonetic_firstname,') .' carname, rank,' .' finishtime, finishplace, imagefile, +' .(schema_version() >= 2 ? 'carphoto' : "'' AS carphoto").', ' +.(schema_version() >= PARTITION_SCHEMA ? ' note' : "'' AS note") .' FROM ' +.inner_join('RaceChart', 'RegistrationInfo', 'RaceChart.racerid = +RegistrationInfo.racerid', 'Ranks', 'RegistrationInfo.rankid = Ranks.rankid') .' +WHERE roundid = :roundid' .' AND heat = :heat' .' ORDER BY lane'); +$stmt->execute(array(':roundid' => $now_running['roundid'], ':heat' => +$now_running['heat'])); $racers = array(); foreach ($stmt as $row) { $racer = +array( 'lane' => $row['lane'], 'racerid' => $row['racerid'], 'name' => +mangled_name($row, $name_style), 'carname' => $row['carname'], 'carnumber' => +$row['carnumber'], 'note' => $row['note'], 'photo' => $photo_render ? +$photo_render->url_for_row($row) : '', 'finishtime' => $row['finishtime'] ? +sprintf($time_format, $row['finishtime']) : '', 'finishplace' => +$row['finishplace'] ? $row['finishplace'] : ''); if ($photo_render2) { +$racer['photo2'] = $photo_render2->url_for_row($row); } if ($use_subgroups) { +$racer['subgroup'] = $row['rank']; } $racers[] = $racer; } return $racers; } ?> diff --git a/website/inc/name-mangler.inc b/website/inc/name-mangler.inc index 3eb20eee6..4be7ad7d5 100644 --- a/website/inc/name-mangler.inc +++ b/website/inc/name-mangler.inc @@ -2,6 +2,7 @@ define('FULL_NAME', 0); define('FIRST_NAME_LAST_INITIAL', 1); +define('PHONETIC_NAME', 2); function read_name_style() { return read_raceinfo('name-style', FULL_NAME); @@ -10,8 +11,13 @@ function read_name_style() { function mangled_name(&$racer, $style) { if ($style == FIRST_NAME_LAST_INITIAL) { return $racer['firstname'].' '.substr($racer['lastname'], 0, 1).'.'; + } else if ($style == PHONETIC_NAME) { + // Use phonetic names if available, fall back to regular names + $firstname = !empty($racer['phonetic_firstname']) ? $racer['phonetic_firstname'] : $racer['firstname']; + $lastname = !empty($racer['phonetic_lastname']) ? $racer['phonetic_lastname'] : $racer['lastname']; + return $firstname.' '.$lastname; } else { return $racer['firstname'].' '.$racer['lastname']; } } -?> \ No newline at end of file +?> diff --git a/website/inc/newracer.inc b/website/inc/newracer.inc index a4062795e..88d74b29e 100644 --- a/website/inc/newracer.inc +++ b/website/inc/newracer.inc @@ -21,130 +21,82 @@ function insert_new_racer($values) { if (!isset($values['classid']) && isset($values['rankid'])) { $values['classid'] = read_single_value('SELECT classid FROM Ranks WHERE rankid = :rankid', - array(':rankid' => $values['rankid'])); - } - - if (!isset($values['partitionid']) && !isset($values['partition']) && isset($values['classid'])) { - // TODO This is an interim arrangement only - $values['partitionid'] = read_single_value('SELECT partitionid FROM Partitions' - .' WHERE name = (SELECT class FROM Classes' - .' WHERE classid = :classid)', - array(':classid' => $values['classid'])); - } - - if (!isset($values['partitionid'])) { - $values['partitionid'] = find_or_create_partition( - (isset($values['partition']) && !empty($values['partition'])) - ? $values['partition'] : DEFAULT_PARTITION_NAME); - } - - if (!isset($values['carnumber']) || $values['carnumber'] == '') { - $values['carnumber'] = next_car_number_for_partition($values['partitionid']); - } - - if (read_single_value('SELECT COUNT(*) FROM RegistrationInfo' - .' WHERE carnumber = :carnumber', - array(':carnumber' => $values['carnumber'])) > 0) { - json_out('warnings', array("Duplicate car number $values[carnumber]")); - } - - if (!isset($values['classid'])) { - list($classid, $rankid) = read_single_row('SELECT classid, rankid FROM Ranks' - .' WHERE rankid = (SELECT rankid FROM Partitions' - .' WHERE partitionid = :partitionid)', - array(':partitionid' => $values['partitionid'])); - $values['classid'] = $classid; - $values['rankid'] = $rankid; - } - - - $stmt = $db->prepare('INSERT INTO RegistrationInfo (carnumber, lastname, firstname, carname,' - .' note, partitionid, rankid, classid, exclude)' - .' VALUES(:carnumber, :lastname, :firstname, :carname,' - .' :note, :partitionid, :rankid, :classid, :exclude)'); - $stmt->execute(array(':carnumber' => trim($values['carnumber']), - ':carname' => trim($values['carname']), - ':note' => trim($values['note_from']), - ':firstname' => trim($values['firstname']), - ':lastname' => trim($values['lastname']), - ':partitionid' => $values['partitionid'], - ':rankid' => $values['rankid'], - ':classid' => $values['classid'], - ':exclude' => (isset($values['exclude']) && $values['exclude']) ? 1 : 0)); - - $racerid = 0; - try { - // Sqlite3 supports this, other databases might not - $racerid = read_single_value('SELECT last_insert_rowid()'); - } catch (Exception $e) { - } - if ($racerid == 0) { - // If last_insert_rowid() didn't work, then this approximation will have to do - $racerid = read_single_value('SELECT MAX(racerid) FROM RegistrationInfo' - .' WHERE firstname = :firstname and lastname = :lastname' - .' AND partitionid = :partitionid', - array(':firstname' => trim($values['firstname']), - ':lastname' => trim($values['lastname']), - ':partitionid' => $values['partitionid'])); - } - - // The new racer won't be recognized without a Roster record to go with it. - fill_in_missing_roster_entries(); - - record_action(array('action' => 'racer.*create', - 'racerid' => $racerid, - 'firstname' => trim($values['firstname']), - 'lastname' => trim($values['lastname']), - 'carnumber' => trim($values['carnumber']), - 'carname' => trim($values['carname']), - 'partitionid' => $values['partitionid'], - 'rankid' => $values['rankid'], - 'classid' => $values['classid'])); - return $racerid; -} - -// This just enrolls everyone into Round 1 for their Class. -function fill_in_missing_roster_entries() { - global $db; - $db->exec('INSERT INTO Roster(roundid, classid, racerid)' - .' SELECT roundid, RegistrationInfo.classid, racerid' - .' FROM Rounds' - .' INNER JOIN RegistrationInfo' - .' ON Rounds.classid = RegistrationInfo.classid' - .' WHERE round = 1' - .' AND NOT EXISTS(SELECT 1 FROM Roster' - .' WHERE Roster.roundid = Rounds.roundid' - // The classid field on the Roster is a mis-design inherited from - // GPRM. The field is completely redundant, which just means, - // sooner or later, it'll get the wrong value. - // - // A racer is enrolled in a round if there's a Roster entry connecting - // the racerid to the roundid; the classid shouldn't matter. - // - // .' AND Roster.classid = RegistrationInfo.classid' - .' AND Roster.racerid = RegistrationInfo.racerid)'); -} - -// Manufactures a classid and rankid when there are none. This arises only if -// creating racers from an empty roster on the check-in page. -function force_populate_a_rankid() { - global $db; - if (read_single_value('SELECT COUNT(*) FROM Classes') == 0) { - $classid = find_or_create_class(UNSPECIFIED_CLASS); - } else { - // If there are classes but not ranks, we'll pick a class at random, but most likely, - // the only class in the database is the one we just created. - $classid = read_single_value('SELECT classid FROM Classes'); - } - - if (read_single_value('SELECT COUNT(*) FROM Ranks WHERE classid = :classid', - array(':classid' => $classid)) == 0) { - $rankid = find_or_create_rank(UNSPECIFIED_RANK, $classid); - } else { - $rankid = read_single_value('SELECT rankid FROM Ranks WHERE classid = :classid', - array(':classid' => $classid)); - } - - return $rankid; -} -?> + array(':rankid' => +$values['rankid'])); } if (!isset($values['partitionid']) && +!isset($values['partition']) && isset($values['classid'])) { // TODO This is an +interim arrangement only $values['partitionid'] = read_single_value('SELECT +partitionid FROM Partitions' .' WHERE name = (SELECT class FROM Classes' .' +WHERE classid = :classid)', array(':classid' => $values['classid'])); } if +(!isset($values['partitionid'])) { $values['partitionid'] = +find_or_create_partition( (isset($values['partition']) && +!empty($values['partition'])) ? $values['partition'] : DEFAULT_PARTITION_NAME); +} if (!isset($values['carnumber']) || $values['carnumber'] == '') { +$values['carnumber'] = next_car_number_for_partition($values['partitionid']); } +if (read_single_value('SELECT COUNT(*) FROM RegistrationInfo' .' WHERE carnumber += :carnumber', array(':carnumber' => $values['carnumber'])) > 0) { +json_out('warnings', array("Duplicate car number $values[carnumber]")); } if +(!isset($values['classid'])) { list($classid, $rankid) = read_single_row('SELECT +classid, rankid FROM Ranks' .' WHERE rankid = (SELECT rankid FROM Partitions' .' +WHERE partitionid = :partitionid)', array(':partitionid' => +$values['partitionid'])); $values['classid'] = $classid; $values['rankid'] = +$rankid; } // Build the INSERT query based on schema version to support phonetic +names if available if (schema_version() >= PHONETIC_NAMES_SCHEMA) { $stmt = +$db->prepare('INSERT INTO RegistrationInfo (carnumber, lastname, firstname, +carname,' .' phonetic_lastname, phonetic_firstname,' .' note, partitionid, +rankid, classid, exclude)' .' VALUES(:carnumber, :lastname, :firstname, +:carname,' .' :phonetic_lastname, :phonetic_firstname,' .' :note, :partitionid, +:rankid, :classid, :exclude)'); $stmt->execute(array(':carnumber' => +trim($values['carnumber']), ':carname' => trim($values['carname']), ':note' => +trim($values['note_from']), ':firstname' => trim($values['firstname']), +':lastname' => trim($values['lastname']), ':phonetic_firstname' => +isset($values['phonetic_firstname']) ? trim($values['phonetic_firstname']) : '', +':phonetic_lastname' => isset($values['phonetic_lastname']) ? +trim($values['phonetic_lastname']) : '', ':partitionid' => +$values['partitionid'], ':rankid' => $values['rankid'], ':classid' => +$values['classid'], ':exclude' => (isset($values['exclude']) && +$values['exclude']) ? 1 : 0)); } else { $stmt = $db->prepare('INSERT INTO +RegistrationInfo (carnumber, lastname, firstname, carname,' .' note, +partitionid, rankid, classid, exclude)' .' VALUES(:carnumber, :lastname, +:firstname, :carname,' .' :note, :partitionid, :rankid, :classid, :exclude)'); +$stmt->execute(array(':carnumber' => trim($values['carnumber']), ':carname' => +trim($values['carname']), ':note' => trim($values['note_from']), ':firstname' => +trim($values['firstname']), ':lastname' => trim($values['lastname']), +':partitionid' => $values['partitionid'], ':rankid' => $values['rankid'], +':classid' => $values['classid'], ':exclude' => (isset($values['exclude']) && +$values['exclude']) ? 1 : 0)); } $racerid = 0; try { // Sqlite3 supports this, +other databases might not $racerid = read_single_value('SELECT +last_insert_rowid()'); } catch (Exception $e) { } if ($racerid == 0) { // If +last_insert_rowid() didn't work, then this approximation will have to do +$racerid = read_single_value('SELECT MAX(racerid) FROM RegistrationInfo' .' +WHERE firstname = :firstname and lastname = :lastname' .' AND partitionid = +:partitionid', array(':firstname' => trim($values['firstname']), ':lastname' => +trim($values['lastname']), ':partitionid' => $values['partitionid'])); } // The +new racer won't be recognized without a Roster record to go with it. +fill_in_missing_roster_entries(); record_action(array('action' => +'racer.*create', 'racerid' => $racerid, 'firstname' => +trim($values['firstname']), 'lastname' => trim($values['lastname']), 'carnumber' +=> trim($values['carnumber']), 'carname' => trim($values['carname']), +'partitionid' => $values['partitionid'], 'rankid' => $values['rankid'], +'classid' => $values['classid'])); return $racerid; } // This just enrolls +everyone into Round 1 for their Class. function fill_in_missing_roster_entries() +{ global $db; $db->exec('INSERT INTO Roster(roundid, classid, racerid)' .' +SELECT roundid, RegistrationInfo.classid, racerid' .' FROM Rounds' .' INNER JOIN +RegistrationInfo' .' ON Rounds.classid = RegistrationInfo.classid' .' WHERE +round = 1' .' AND NOT EXISTS(SELECT 1 FROM Roster' .' WHERE Roster.roundid = +Rounds.roundid' // The classid field on the Roster is a mis-design inherited +from // GPRM. The field is completely redundant, which just means, // sooner or +later, it'll get the wrong value. // // A racer is enrolled in a round if +there's a Roster entry connecting // the racerid to the roundid; the classid +shouldn't matter. // // .' AND Roster.classid = RegistrationInfo.classid' .' AND +Roster.racerid = RegistrationInfo.racerid)'); } // Manufactures a classid and +rankid when there are none. This arises only if // creating racers from an empty +roster on the check-in page. function force_populate_a_rankid() { global $db; if +(read_single_value('SELECT COUNT(*) FROM Classes') == 0) { $classid = +find_or_create_class(UNSPECIFIED_CLASS); } else { // If there are classes but +not ranks, we'll pick a class at random, but most likely, // the only class in +the database is the one we just created. $classid = read_single_value('SELECT +classid FROM Classes'); } if (read_single_value('SELECT COUNT(*) FROM Ranks +WHERE classid = :classid', array(':classid' => $classid)) == 0) { $rankid = +find_or_create_rank(UNSPECIFIED_RANK, $classid); } else { $rankid = +read_single_value('SELECT rankid FROM Ranks WHERE classid = :classid', +array(':classid' => $classid)); } return $rankid; } ?> diff --git a/website/inc/schema_version.inc b/website/inc/schema_version.inc index 2fa6baa44..9d7b36a50 100644 --- a/website/inc/schema_version.inc +++ b/website/inc/schema_version.inc @@ -5,7 +5,7 @@ function schema_version() { } function expected_schema_version() { - return 10; + return 11; } define('RANK_AGGREGATE_SCHEMA', 5); // Classes.rankids for aggregates formed from ranks @@ -15,4 +15,5 @@ define('PARTITION_SCHEMA', 8); define('TIMER_RESULT_SCHEMA', 9); define('PER_SUBGROUP_AWARDS_SCHEMA', 10); define('REGISTRATION_CHECKIN_SCHEMA', 10); +define('PHONETIC_NAMES_SCHEMA', 11); ?> diff --git a/website/js/checkin.js b/website/js/checkin.js index 7b81bb6bd..edb92387a 100644 --- a/website/js/checkin.js +++ b/website/js/checkin.js @@ -5,21 +5,20 @@ g_next_carnumbers = []; g_poll_max_interval = 0; function poll_max_carnumbers() { - $.ajax(g_action_url, - {type: 'GET', - data: {query: 'poll', - values: 'car-numbers'}, - success: function (data) { - if (data["cease"]) { - clearInterval(g_poll_max_interval); - window.location.href = '../index.php'; - return; - } - if (data.hasOwnProperty('car-numbers')) { - read_next_carnumbers(data['car-numbers']); - } - } - }); + $.ajax(g_action_url, { + type: "GET", + data: { query: "poll", values: "car-numbers" }, + success: function (data) { + if (data["cease"]) { + clearInterval(g_poll_max_interval); + window.location.href = "../index.php"; + return; + } + if (data.hasOwnProperty("car-numbers")) { + read_next_carnumbers(data["car-numbers"]); + } + }, + }); } // carnos is an array of {partitionid, next_carnumber} @@ -32,13 +31,13 @@ function read_next_carnumbers(carnos) { function next_carnumber(partitionid) { return g_next_carnumbers[partitionid] || 999; } -$(function() { +$(function () { g_poll_max_interval = setInterval(poll_max_carnumbers, 10000); poll_max_carnumbers(); - $("#edit_partition").on('change', function(event) { + $("#edit_partition").on("change", function (event) { // The car number field is updatable only for new racers - if ($("#edit_carno").attr('data-updatable')) { + if ($("#edit_carno").attr("data-updatable")) { var p = parseInt($("#edit_partition").val()); if (p && p >= 0) { $("#edit_carno").val(next_carnumber(p)); @@ -52,20 +51,21 @@ $(function() { // This executes when a checkbox for "Passed" is clicked. function handlechange_passed(cb, racer) { // cb is the checkbox element, with name "passed-" plus the racer id, e.g., passed-1234 - if (!cb.checked && !confirm("Are you sure you want to unregister " + racer + "?")) { - cb.checked = true; - return; + if ( + !cb.checked && + !confirm("Are you sure you want to unregister " + racer + "?") + ) { + cb.checked = true; + return; } // 7 = length of "passed-" prefix var racer = cb.name.substring(7); var value = cb.checked ? 1 : 0; - $.ajax(g_action_url, - {type: 'POST', - data: {action: 'racer.pass', - racer: racer, - value: value}, - }); + $.ajax(g_action_url, { + type: "POST", + data: { action: "racer.pass", racer: racer, value: value }, + }); } // This executes when a checkbox for "Exclusively by Scout" is clicked. @@ -75,12 +75,10 @@ function handlechange_xbs(cb) { var racer = cb.name.substring(4); var value = cb.checked ? 1 : 0; - $.ajax(g_action_url, - {type: 'POST', - data: {action: 'award.xbs', - racer: racer, - value: value}, - }); + $.ajax(g_action_url, { + type: "POST", + data: { action: "award.xbs", racer: racer, value: value }, + }); } // Two ')); - - tr.append($('') - .attr('id', 'div-' + racer.racerid) - .attr('data-partitionid', racer.partitionid) - .attr('data-div-sortorder', racer.partition_sortorder) - .text(racer.partition)); - - tr.append($('') - .attr('data-car-number', racer.carnumber) - .attr('id', 'car-number-' + racer.racerid) - .text(racer.carnumber)); - - tr.append($('').attr('id', 'photo-' + racer.racerid) - .append($('') - .append($('') - .attr('src', racer.headshot))) - .append($('') - .append($('') - .attr('src', racer.carphoto)))); - - tr.append($('') - .attr('id', 'lastname-' + racer.racerid) - .attr('data-exclude', racer.exclude ? 1 : 0) - .text(racer.lastname)); - tr.append($('') - .attr('id', 'firstname-' + racer.racerid) - .text(racer.firstname)); - tr.append($('') - .append($("
") - .attr('id', 'car-name-' + racer.racerid) - .addClass('carname') - .text(racer.carname)) - .append($("
") - .attr('id', 'note-from-' + racer.racerid) - .text(racer.note))); + var tr = $("") + .attr("data-racerid", racer.racerid) + .addClass("d" + (racer.rowno & 1)) + .toggleClass("den_scheduled", racer.denscheduled) + .toggleClass("exclude", racer.exclude); + tr.append( + $("").append( + '' + ) + ); + + tr.append( + $("") + .attr("id", "div-" + racer.racerid) + .attr("data-partitionid", racer.partitionid) + .attr("data-div-sortorder", racer.partition_sortorder) + .attr("data-phonetic-firstname", racer.phonetic_firstname || "") + .attr("data-phonetic-lastname", racer.phonetic_lastname || "") + .text(racer.partition) + ); + + tr.append( + $('') + .attr("data-car-number", racer.carnumber) + .attr("id", "car-number-" + racer.racerid) + .text(racer.carnumber) + ); + + tr.append( + $("") + .attr("id", "photo-" + racer.racerid) + .append( + $( + '' + ).append( + $('').attr( + "src", + racer.headshot + ) + ) + ) + .append( + $( + '' + ).append( + $('').attr( + "src", + racer.carphoto + ) + ) + ) + ); + + tr.append( + $('') + .attr("id", "lastname-" + racer.racerid) + .attr("data-exclude", racer.exclude ? 1 : 0) + .text(racer.lastname) + ); + tr.append( + $('') + .attr("id", "firstname-" + racer.racerid) + .text(racer.firstname) + ); + tr.append( + $("") + .append( + $("
") + .attr("id", "car-name-" + racer.racerid) + .addClass("carname") + .text(racer.carname) + ) + .append( + $("
") + .attr("id", "note-from-" + racer.racerid) + .text(racer.note) + ) + ); var checkin = $('').appendTo(tr); - checkin.append('
'); - checkin.append($('') - .attr('id', 'passed-' + racer.racerid) - .attr('name', 'passed-' + racer.racerid) - .prop('checked', racer.passed) - .attr('data-on-text', 'Yes') - .attr('data-off-text', 'No') - // prop onchange doesn't seem to allow a string, but attr does - .attr('onchange', 'handlechange_passed(this, ' + - JSON.stringify(racer.firstname + ' ' + racer.lastname) + - ')')); + checkin.append("
"); + checkin.append( + $('') + .attr("id", "passed-" + racer.racerid) + .attr("name", "passed-" + racer.racerid) + .prop("checked", racer.passed) + .attr("data-on-text", "Yes") + .attr("data-off-text", "No") + // prop onchange doesn't seem to allow a string, but attr does + .attr( + "onchange", + "handlechange_passed(this, " + + JSON.stringify(racer.firstname + " " + racer.lastname) + + ")" + ) + ); if (racer.scheduled) { if (racer.passed) { - checkin.append(' Racing!'); + checkin.append(" Racing!"); } else { - checkin.append(' Scheduled but not passed'); + checkin.append(" Scheduled but not passed"); } } else if (racer.denscheduled) { // denscheduled means their racing group has a schedule - checkin.append(' Late!'); + checkin.append(" Late!"); } if (xbs) { - tr.append($('') - .append($('
'."\n"; + } +?> + +

Number of lanes not yet recorded...

+ + + + +
+
+ +
+
+ +

Check timer.

+
+
+ +

Schedule adjustment needed.

+
+ + diff --git a/website/sql/access/schema.inc b/website/sql/access/schema.inc index 7a71cc565..39ddd14c2 100644 --- a/website/sql/access/schema.inc +++ b/website/sql/access/schema.inc @@ -216,6 +216,8 @@ array( .' CarName VARCHAR (30),' .' LastName VARCHAR (30),' .' FirstName VARCHAR (30),' +.(expected_schema_version() < PHONETIC_NAMES_SCHEMA ? '' : ' PhoneticLastName VARCHAR (30),') +.(expected_schema_version() < PHONETIC_NAMES_SCHEMA ? '' : ' PhoneticFirstName VARCHAR (30),') .' ClassID INTEGER,' .' RankID INTEGER,' .' PartitionID INTEGER,' diff --git a/website/sql/access/update-schema.inc b/website/sql/access/update-schema.inc index 912124a7a..dc71dbe3b 100644 --- a/website/sql/access/update-schema.inc +++ b/website/sql/access/update-schema.inc @@ -10,7 +10,7 @@ if (schema_version() < 2) { $updates[] = "ALTER TABLE Ranks ADD COLUMN SortOrder INTEGER"; $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN CarPhoto VARCHAR(255)"; - + $updates[] = "DELETE FROM RaceInfo WHERE ItemKey = 'schema'"; $updates[] = "INSERT INTO RaceInfo(ItemKey, ItemValue) VALUES('schema', '2')"; } @@ -65,7 +65,7 @@ if (schema_version() < 8) { $updates[] = "CREATE TABLE ConstituentClasses (" ." antecedent INTEGER, dependent INTEGER)"; $updates[] = "CREATE UNIQUE INDEX AnteDependent ON ConstituentClasses(antecedent, dependent)"; - + $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '8')"; } @@ -80,6 +80,24 @@ if (schema_version() < 9) { $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '9')"; } +if (schema_version() < 10) { + $updates = array_merge($updates, + @include(sql_file_path('action-history'))); + $updates[] = "ALTER TABLE Ranks ADD COLUMN ntrophies INTEGER DEFAULT -1"; + $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN checkin_time INTEGER"; + + $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; + $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '10')"; +} + +if (schema_version() < 11) { + $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN PhoneticLastName VARCHAR(30)"; + $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN PhoneticFirstName VARCHAR(30)"; + + $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; + $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '11')"; +} + // For each new schema version, copy the DELETE and INSERT for schema version return $updates; diff --git a/website/sql/sqlite/schema.inc b/website/sql/sqlite/schema.inc index 910b8c793..21d4305cc 100644 --- a/website/sql/sqlite/schema.inc +++ b/website/sql/sqlite/schema.inc @@ -129,6 +129,8 @@ make_index("Ranks", "rank"), ." `carname` VARCHAR(30), " ." `lastname` VARCHAR(30) NOT NULL COLLATE NOCASE, " ." `firstname` VARCHAR(30) NOT NULL COLLATE NOCASE, " +.(expected_schema_version() < PHONETIC_NAMES_SCHEMA ? "" : " `phonetic_lastname` VARCHAR(30) COLLATE NOCASE, ") +.(expected_schema_version() < PHONETIC_NAMES_SCHEMA ? "" : " `phonetic_firstname` VARCHAR(30) COLLATE NOCASE, ") ." `classid` INTEGER NOT NULL, " ." `rankid` INTEGER NOT NULL, " ." `partitionid` INTEGER NOT NULL," diff --git a/website/sql/sqlite/update-schema.inc b/website/sql/sqlite/update-schema.inc index 88bcf015d..b505ede32 100644 --- a/website/sql/sqlite/update-schema.inc +++ b/website/sql/sqlite/update-schema.inc @@ -8,118 +8,61 @@ if (!function_exists('make_index')) { function table_exists($table_name) { return read_single_value("SELECT COUNT(*) FROM sqlite_master" - ." WHERE type='table' AND name='$table_name'") > 0; - } -} - -$updates = array(); - -if (schema_version() < 2) { - - // There's no ALTER COLUMN in sqlite - $updates[] = "CREATE TABLE TempRaceInfo AS SELECT * FROM RaceInfo"; - $updates[] = "DROP TABLE RaceInfo"; - $updates[] = "CREATE TABLE `RaceInfo` (" - ." `raceinfoid` INTEGER PRIMARY KEY, " - ." `itemkey` VARCHAR(20) NOT NULL, " - ." `itemvalue` VARCHAR(200)" - .")"; - $updates[] = make_index("RaceInfo", "itemkey"); - $updates[] = "INSERT INTO RaceInfo SELECT * FROM TempRaceInfo"; - $updates[] = "DROP TABLE TempRaceInfo"; - - $updates[] = "ALTER TABLE Classes ADD COLUMN sortorder INTEGER"; - $updates[] = "ALTER TABLE Ranks ADD COLUMN sortorder INTEGER"; - - $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN carphoto VARCHAR(255)"; - - $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; - $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '2')"; -} - -if (schema_version() < 3) { - $updates[] = "ALTER TABLE Classes ADD COLUMN constituents VARCHAR(100) DEFAULT ''"; - $updates[] = "ALTER TABLE Classes ADD COLUMN durable INTEGER"; - $updates[] = "ALTER TABLE Classes ADD COLUMN ntrophies INTEGER DEFAULT -1"; - - $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; - $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '3')"; -} - -if (schema_version() < 4) { - if (!table_exists('MessageQueue')) { - $updates = array_merge($updates, - @include(sql_file_path('message-queue-table'))); - } - if (!table_exists('Scenes')) { - $updates = array_merge($updates, - @include(sql_file_path('scene-tables'))); - } - if (!table_exists('Playlist')) { - $updates = array_merge($updates, - @include(sql_file_path('playlist-table'))); - } - - $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; - $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '4')"; -} - -if (schema_version() < 5) { - $updates[] = "ALTER TABLE Classes ADD COLUMN rankids VARCHAR(100) DEFAULT ''"; - - $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; - $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '5')"; -} - -if (schema_version() < 6) { - $updates = array_merge($updates, - @include(sql_file_path('balloting'))); - - $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; - $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '6')"; -} - -if (schema_version() < 7) { - $updates = array_merge($updates, - @include(sql_file_path('timer-settings'))); - - $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; - $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '7')"; -} - -if (schema_version() < 8) { - $updates = array_merge($updates, - @include(sql_file_path('partitions'))); - $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN partitionid INTEGER"; - $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN note VARCHAR(255)"; - $updates[] = "CREATE TABLE ConstituentClasses (" - ."antecedent INTEGER, dependent INTEGER, UNIQUE (antecedent, dependent))"; - - $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; - $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '8')"; -} - -if (schema_version() < 9) { - $updates = array_merge($updates, - @include(sql_file_path('event-lane-details'))); - // $updates[] = "ALTER TABLE Events DROP COLUMN lane"; - $updates[] = "ALTER TABLE Events ADD COLUMN finishid INTEGER"; - - $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; - $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '9')"; -} - -if (schema_version() < 10) { - $updates = array_merge($updates, - @include(sql_file_path('action-history'))); - $updates[] = "ALTER TABLE Ranks ADD COLUMN ntrophies INTEGER DEFAULT -1"; - $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN checkin_time INTEGER"; - - $updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; - $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '10')"; -} - -// For each new schema version, copy the DELETE and INSERT for schema version - -return $updates; -?> + ." WHERE type='table' AND name='$table_name'") > +0; } } $updates = array(); if (schema_version() < 2) { // There's no ALTER +COLUMN in sqlite $updates[] = "CREATE TABLE TempRaceInfo AS SELECT * FROM +RaceInfo"; $updates[] = "DROP TABLE RaceInfo"; $updates[] = "CREATE TABLE +`RaceInfo` (" ." `raceinfoid` INTEGER PRIMARY KEY, " ." `itemkey` VARCHAR(20) +NOT NULL, " ." `itemvalue` VARCHAR(200)" .")"; $updates[] = +make_index("RaceInfo", "itemkey"); $updates[] = "INSERT INTO RaceInfo SELECT * +FROM TempRaceInfo"; $updates[] = "DROP TABLE TempRaceInfo"; $updates[] = "ALTER +TABLE Classes ADD COLUMN sortorder INTEGER"; $updates[] = "ALTER TABLE Ranks ADD +COLUMN sortorder INTEGER"; $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN +carphoto VARCHAR(255)"; $updates[] = "DELETE FROM RaceInfo WHERE itemkey = +'schema'"; $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) +VALUES('schema', '2')"; } if (schema_version() < 3) { $updates[] = "ALTER TABLE +Classes ADD COLUMN constituents VARCHAR(100) DEFAULT ''"; $updates[] = "ALTER +TABLE Classes ADD COLUMN durable INTEGER"; $updates[] = "ALTER TABLE Classes ADD +COLUMN ntrophies INTEGER DEFAULT -1"; $updates[] = "DELETE FROM RaceInfo WHERE +itemkey = 'schema'"; $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) +VALUES('schema', '3')"; } if (schema_version() < 4) { if +(!table_exists('MessageQueue')) { $updates = array_merge($updates, +@include(sql_file_path('message-queue-table'))); } if (!table_exists('Scenes')) +{ $updates = array_merge($updates, @include(sql_file_path('scene-tables'))); } +if (!table_exists('Playlist')) { $updates = array_merge($updates, +@include(sql_file_path('playlist-table'))); } $updates[] = "DELETE FROM RaceInfo +WHERE itemkey = 'schema'"; $updates[] = "INSERT INTO RaceInfo(itemkey, +itemvalue) VALUES('schema', '4')"; } if (schema_version() < 5) { $updates[] = +"ALTER TABLE Classes ADD COLUMN rankids VARCHAR(100) DEFAULT ''"; $updates[] = +"DELETE FROM RaceInfo WHERE itemkey = 'schema'"; $updates[] = "INSERT INTO +RaceInfo(itemkey, itemvalue) VALUES('schema', '5')"; } if (schema_version() < 6) +{ $updates = array_merge($updates, @include(sql_file_path('balloting'))); +$updates[] = "DELETE FROM RaceInfo WHERE itemkey = 'schema'"; $updates[] = +"INSERT INTO RaceInfo(itemkey, itemvalue) VALUES('schema', '6')"; } if +(schema_version() < 7) { $updates = array_merge($updates, +@include(sql_file_path('timer-settings'))); $updates[] = "DELETE FROM RaceInfo +WHERE itemkey = 'schema'"; $updates[] = "INSERT INTO RaceInfo(itemkey, +itemvalue) VALUES('schema', '7')"; } if (schema_version() < 8) { $updates = +array_merge($updates, @include(sql_file_path('partitions'))); $updates[] = +"ALTER TABLE RegistrationInfo ADD COLUMN partitionid INTEGER"; $updates[] = +"ALTER TABLE RegistrationInfo ADD COLUMN note VARCHAR(255)"; $updates[] = +"CREATE TABLE ConstituentClasses (" ."antecedent INTEGER, dependent INTEGER, +UNIQUE (antecedent, dependent))"; $updates[] = "DELETE FROM RaceInfo WHERE +itemkey = 'schema'"; $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) +VALUES('schema', '8')"; } if (schema_version() < 9) { $updates = +array_merge($updates, @include(sql_file_path('event-lane-details'))); // +$updates[] = "ALTER TABLE Events DROP COLUMN lane"; $updates[] = "ALTER TABLE +Events ADD COLUMN finishid INTEGER"; $updates[] = "DELETE FROM RaceInfo WHERE +itemkey = 'schema'"; $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) +VALUES('schema', '9')"; } if (schema_version() < 10) { $updates = +array_merge($updates, @include(sql_file_path('action-history'))); $updates[] = +"ALTER TABLE Ranks ADD COLUMN ntrophies INTEGER DEFAULT -1"; $updates[] = "ALTER +TABLE RegistrationInfo ADD COLUMN checkin_time INTEGER"; $updates[] = "DELETE +FROM RaceInfo WHERE itemkey = 'schema'"; $updates[] = "INSERT INTO +RaceInfo(itemkey, itemvalue) VALUES('schema', '10')"; } if (schema_version() < +11) { $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN phonetic_lastname +VARCHAR(30)"; $updates[] = "ALTER TABLE RegistrationInfo ADD COLUMN +phonetic_firstname VARCHAR(30)"; $updates[] = "DELETE FROM RaceInfo WHERE +itemkey = 'schema'"; $updates[] = "INSERT INTO RaceInfo(itemkey, itemvalue) +VALUES('schema', '11')"; } // For each new schema version, copy the DELETE and +INSERT for schema version return $updates; ?>