diff --git a/application/models/Logbookadvanced_model.php b/application/models/Logbookadvanced_model.php index 342397e47..dc4243479 100644 --- a/application/models/Logbookadvanced_model.php +++ b/application/models/Logbookadvanced_model.php @@ -1646,6 +1646,8 @@ class Logbookadvanced_model extends CI_Model { return $this->getIncorrectCqZones(); case 'checkincorrectituzones': return $this->getIncorrectItuZones(); + case 'checkiota': + return $this->checkIota(); default: return null; } @@ -2075,4 +2077,117 @@ class Logbookadvanced_model extends CI_Model { return $query->result(); } + + public function checkIota() { + $result1 = $this->checkSingleIota(); + $result2 = $this->checkMultiDxccIota(); + + $merged = array_merge($result1, $result2); + + // Sort merged results by station_profile_name, then col_time_on DESC + usort($merged, function($a, $b) { + $stationCompare = strcmp($a->station_profile_name, $b->station_profile_name); + if ($stationCompare !== 0) { + return $stationCompare; + } + // If same station, sort by time_on descending (newest first) + return strtotime($b->col_time_on) - strtotime($a->col_time_on); + }); + + return $merged; + } + + /* + * Get list of QSOs with IOTA that do not match the IOTAs listed for the DXCC. + * Some islands are excluded as they can be in multiple DXCCs. + * + * These are excluded by not having a dxccid or dxccid = 0 + * + */ + public function checkSingleIota() { + $sql = "select col_primary_key, col_time_on, col_call, col_band, col_gridsquare, col_dxcc, col_country, station_profile_name, col_lotw_qsl_rcvd, col_mode, col_submode, col_iota, iotadxcc.name as correctdxcc + from " . $this->config->item('table_name') . " thcv + join station_profile on thcv.station_id = station_profile.station_id + join dxcc_entities on dxcc_entities.adif = thcv.COL_DXCC + join iota on thcv.col_iota = iota.tag + join dxcc_entities iotadxcc on iota.dxccid = iotadxcc.adif + where station_profile.user_id = ? + and thcv.col_dxcc > 0 + and thcv.col_dxcc <> iota.dxccid + and iota.dxccid > 0 + order by station_profile_name, col_time_on desc"; + + $bindings[] = [$this->session->userdata('user_id')]; + + $query = $this->db->query($sql, $bindings); + return $query->result(); + } + + /* + * Get list of QSOs with multi-DXCC IOTA tags where the DXCC prefix doesn't match + * any of the valid prefixes for that IOTA. + */ + public function checkMultiDxccIota() { + // Define IOTA tags that span multiple DXCCs with their valid prefixes + $multiDxccIotas = [ + 'AS-004' => [215, 283], // 5B4, ZC4 + 'EU-053' => [167, 284], // OJ0, SM + 'EU-115' => [245, 265], // EI, GI + 'EU-117' => [151, 224], // R1M, OH + 'EU-129' => [230, 269], // DL, SP + 'EU-191' => [275, 288], // YO, UR + 'EU-192' => [284, 224], // SM, OH + 'NA-015' => [70, 105], // CO, KG4 + 'NA-096' => [72, 78], // HH, HI + 'NA-105' => [213, 518], // FS, PJ7 + 'OC-034' => [163, 327], // P2, YB + 'OC-088' => [46, 327, 345], // 9M6, V8, YB + 'OC-148' => [327, 511], // YB, 4W + 'SA-008' => [100, 112] // LU, CE + ]; + + $allResults = []; + + foreach ($multiDxccIotas as $iotaTag => $adifList) { + // Build IN clause for SQL + $adifListStr = implode(',', $adifList); + + $sql = "SELECT thcv.col_primary_key, thcv.col_time_on, thcv.col_call, thcv.col_band, thcv.col_gridsquare, + thcv.col_dxcc, thcv.col_country, station_profile.station_profile_name, thcv.col_lotw_qsl_rcvd, + thcv.col_mode, thcv.col_submode, thcv.col_iota, + ( + SELECT GROUP_CONCAT(DISTINCT d.name ORDER BY d.name SEPARATOR ', ') + FROM dxcc_entities d + WHERE d.adif IN ($adifListStr) + ) as correctdxcc + FROM " . $this->config->item('table_name') . " thcv + JOIN station_profile ON thcv.station_id = station_profile.station_id + JOIN dxcc_entities ON dxcc_entities.adif = thcv.COL_DXCC + JOIN iota ON thcv.col_iota = iota.tag + WHERE station_profile.user_id = ? + AND thcv.col_iota = ? + AND dxcc_entities.adif NOT IN ($adifListStr) + ORDER BY station_profile_name, col_time_on DESC"; + + $bindings = [$this->session->userdata('user_id'), $iotaTag]; + $query = $this->db->query($sql, $bindings); + $results = $query->result(); + + if (!empty($results)) { + $allResults = array_merge($allResults, $results); + } + } + + // Sort the merged results by station_profile_name, then col_time_on DESC + usort($allResults, function($a, $b) { + $stationCompare = strcmp($a->station_profile_name, $b->station_profile_name); + if ($stationCompare !== 0) { + return $stationCompare; + } + // If same station, sort by time_on descending (newest first) + return strtotime($b->col_time_on) - strtotime($a->col_time_on); + }); + + return $allResults; + } } diff --git a/application/views/logbookadvanced/checkresult.php b/application/views/logbookadvanced/checkresult.php index 1127bb0e0..15d31875d 100644 --- a/application/views/logbookadvanced/checkresult.php +++ b/application/views/logbookadvanced/checkresult.php @@ -30,6 +30,9 @@ switch ($type) { case 'checkincorrectituzones': check_incorrect_itu_zones($result, $custom_date_format); break; + case 'checkiota': + check_iota($result, $custom_date_format); + break; default: // Invalid type break; @@ -99,7 +102,7 @@ function check_dxcc($result, $custom_date_format) { ?> - + @@ -113,7 +116,7 @@ function check_dxcc($result, $custom_date_format) { ?> - +
@@ -157,7 +160,7 @@ function check_incorrect_gridsquares($result, $custom_date_format) { ?> - + @@ -171,7 +174,7 @@ function check_incorrect_gridsquares($result, $custom_date_format) { ?> col_time_on)); ?> col_band); ?> col_submode ? $qso->col_submode : $qso->col_mode); ?> - col_lotw_qsl_rcvd == 'Y' ? __('Yes') : __('No'); ?> +
col_lotw_qsl_rcvd == 'Y' ? __('Yes') : __('No'); ?>
station_profile_name; ?> col_country), "- (/"), ENT_QUOTES, 'UTF-8'); ?> col_gridsquare; ?> @@ -363,3 +366,62 @@ function check_incorrect_itu_zones($result, $custom_date_format) { ?> echo '
' . __("No incorrect CQ Zones were found.") . '
'; } } + +function check_iota($result, $custom_date_format) { ?> +
+ ' . htmlspecialchars($result['message']) . ''; + return; + } + if ($result) { ?> + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
col_primary_key . ')">' . htmlspecialchars($qso->col_call) . ''; ?>col_time_on)); ?>col_band); ?>col_submode ? $qso->col_submode : $qso->col_mode); ?>
col_lotw_qsl_rcvd == 'Y' ? __('Yes') : __('No'); ?>
station_profile_name; ?>col_country), "- (/"), ENT_QUOTES, 'UTF-8'); ?>col_iota.'","All","All","All","All","IOTA")\'>' . $qso->col_iota . ''; ?> correctdxcc ?? ''), "- (/"), ENT_QUOTES, 'UTF-8'); ?>
+
+ + +
+
+
+

+
+
+ +
+
diff --git a/assets/js/sections/logbookadvanced.js b/assets/js/sections/logbookadvanced.js index f0bab2bc5..1ef7c2747 100644 --- a/assets/js/sections/logbookadvanced.js +++ b/assets/js/sections/logbookadvanced.js @@ -2379,17 +2379,22 @@ function saveOptions() { .appendTo($(column.footer()).empty()) .on('change', function () { var val = $.fn.dataTable.util.escapeRegex($(this).val()); - - column.search(val ? '^' + val + '$' : '', true, false).draw(); + // Search in rendered content, not just data + column.search(val ? val : '', true, false).draw(); }); - column - .data() - .unique() - .sort() - .each(function (d, j) { - select.append(''); - }); + // Extract text from rendered cells to get actual displayed content + column.nodes().flatten().to$().each(function () { + var text = $(this).text().trim(); + if (text && !select.find('option[value="' + text + '"]').length) { + select.append(''); + } + }); + + // Sort options + select.find('option:not(:first)').sort(function(a, b) { + return a.text.localeCompare(b.text); + }).appendTo(select); }); rebind_checkbox_trigger_dxcc(); }, @@ -2437,17 +2442,22 @@ function saveOptions() { .appendTo($(column.footer()).empty()) .on('change', function () { var val = $.fn.dataTable.util.escapeRegex($(this).val()); - - column.search(val ? '^' + val + '$' : '', true, false).draw(); + // Search in rendered content, not just data + column.search(val ? val : '', true, false).draw(); }); - column - .data() - .unique() - .sort() - .each(function (d, j) { - select.append(''); - }); + // Extract text from rendered cells to get actual displayed content + column.nodes().flatten().to$().each(function () { + var text = $(this).text().trim(); + if (text && !select.find('option[value="' + text + '"]').length) { + select.append(''); + } + }); + + // Sort options + select.find('option:not(:first)').sort(function(a, b) { + return a.text.localeCompare(b.text); + }).appendTo(select); }); rebind_checkbox_trigger_cq_zone(); @@ -2502,17 +2512,22 @@ function saveOptions() { .appendTo($(column.footer()).empty()) .on('change', function () { var val = $.fn.dataTable.util.escapeRegex($(this).val()); - - column.search(val ? '^' + val + '$' : '', true, false).draw(); + // Search in rendered content, not just data + column.search(val ? val : '', true, false).draw(); }); - column - .data() - .unique() - .sort() - .each(function (d, j) { - select.append(''); - }); + // Extract text from rendered cells to get actual displayed content + column.nodes().flatten().to$().each(function () { + var text = $(this).text().trim(); + if (text && !select.find('option[value="' + text + '"]').length) { + select.append(''); + } + }); + + // Sort options + select.find('option:not(:first)').sort(function(a, b) { + return a.text.localeCompare(b.text); + }).appendTo(select); }); rebind_checkbox_trigger_itu_zone(); }, @@ -2681,17 +2696,22 @@ function saveOptions() { .appendTo($(column.footer()).empty()) .on('change', function () { var val = $.fn.dataTable.util.escapeRegex($(this).val()); - - column.search(val ? '^' + val + '$' : '', true, false).draw(); + // Search in rendered content, not just data + column.search(val ? val : '', true, false).draw(); }); - column - .data() - .unique() - .sort() - .each(function (d, j) { - select.append(''); - }); + // Extract text from rendered cells to get actual displayed content + column.nodes().flatten().to$().each(function () { + var text = $(this).text().trim(); + if (text && !select.find('option[value="' + text + '"]').length) { + select.append(''); + } + }); + + // Sort options + select.find('option:not(:first)').sort(function(a, b) { + return a.text.localeCompare(b.text); + }).appendTo(select); }); }, }); @@ -2817,3 +2837,80 @@ function saveOptions() { } }); } + + function checkIota() { + $('#checkIotaBtn').prop("disabled", true).addClass("running"); + $('#closeButton').prop("disabled", true); + + $.ajax({ + url: base_url + 'index.php/logbookadvanced/checkDb', + data: { + type: 'checkiota' + }, + type: 'POST', + success: function(response) { + $('#checkIotaBtn').prop("disabled", false).removeClass("running"); + $('#closeButton').prop("disabled", false); + + $('.result').html(response); + $('#iotaCheckTable').DataTable({ + "pageLength": 25, + responsive: false, + ordering: false, + "scrollY": "510px", + "scrollCollapse": true, + "paging": false, + "scrollX": false, + "language": { + url: getDataTablesLanguageUrl(), + }, + initComplete: function () { + this.api() + .columns('.select-filter') + .every(function () { + var column = this; + var select = $('') + .appendTo($(column.footer()).empty()) + .on('change', function () { + var val = $.fn.dataTable.util.escapeRegex($(this).val()); + // Search in rendered content, not just data + column.search(val ? val : '', true, false).draw(); + }); + + // Extract text from rendered cells to get actual displayed content + column.nodes().flatten().to$().each(function () { + // Get text from the first anchor link which contains the IOTA reference + var $anchor = $(this).find('a').first(); + var text = $anchor.length ? $anchor.text().trim() : $(this).text().trim(); + // Remove any extra whitespace + text = text.split(/\s+/)[0]; + if (text && !select.find('option[value="' + text + '"]').length) { + select.append(''); + } + }); + + // Sort options + select.find('option:not(:first)').sort(function(a, b) { + return a.text.localeCompare(b.text); + }).appendTo(select); + }); + }, + }); + }, + error: function(xhr, status, error) { + $('#checkIotaBtn').prop("disabled", false).removeClass("running"); + $('#closeButton').prop('disabled', false); + + let errorMsg = 'Error checking iota information'; + if (xhr.responseJSON && xhr.responseJSON.message) { + errorMsg += ': ' + xhr.responseJSON.message; + } + + BootstrapDialog.alert({ + title: 'Error', + message: errorMsg, + type: BootstrapDialog.TYPE_DANGER + }); + } + }); + }