[Advanced Logbook] Added gridsquare/cq/itu checker

This commit is contained in:
Andreas Kristiansen
2025-12-27 16:34:37 +01:00
parent a7918bc14c
commit db42a27da7
5 changed files with 508 additions and 20 deletions

View File

@@ -1549,10 +1549,54 @@ class Logbookadvanced_model extends CI_Model {
return $this->check_missing_itu_zones();
case 'checkgrids':
return $this->getMissingGridQsos();
case 'checkincorrectgridsquares':
return $this->getIncorrectGridsquares();
case 'checkincorrectcqzones':
return $this->getIncorrectCqZones();
case 'checkincorrectituzones':
return $this->getIncorrectItuZones();
return null;
}
}
public function getIncorrectGridsquares() {
$sqlcheck = "select count(*) as count from vuccgrids";;
$querycheck = $this->db->query($sqlcheck);
$rowcheck = $querycheck->row();
if ($rowcheck->count == 0) {
return ['status' => 'error', 'message' => __("VuccGrids table is empty. Please import the VUCC grids data first.")];
}
$sql = "select col_primary_key, col_time_on, col_call, col_band, col_gridsquare, col_dxcc, col_country, station_profile_name,
(
select group_concat(distinct gridsquare order by gridsquare separator ', ')
from vuccgrids
where adif = thcv.col_dxcc
order by gridsquare asc
) as correctgridsquare
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
where station_profile.user_id = ?
and thcv.col_dxcc > 0
and not exists (
select 1
from vuccgrids
where adif = thcv.col_dxcc
and gridsquare = substr(thcv.col_gridsquare, 1, 4)
)
and exists (select 1 from vuccgrids where adif = thcv.col_dxcc)
and thcv.col_dxcc > 0
and thcv.col_gridsquare is not null
and thcv.col_gridsquare <> ''
order by station_profile_name, col_time_on desc";
$bindings[] = [$this->session->userdata('user_id')];
$query = $this->db->query($sql, $bindings);
return $query->result();
}
public function check_missing_dxcc() {
$sql = "select count(*) as count from " . $this->config->item('table_name') . "
join station_profile on " . $this->config->item('table_name') . ".station_id = station_profile.station_id
@@ -1980,4 +2024,44 @@ class Logbookadvanced_model extends CI_Model {
$result['count'] = $count;
return $result;
}
function getIncorrectCqZones() {
if(!clubaccess_check(9)) return;
$sql = "select *, (select group_concat(distinct cqzone order by cqzone separator ', ') from dxcc_master where countrycode = thcv.col_dxcc and cqzone <> '' order by cqzone asc) as correctcqzone
from " . $this->config->item('table_name') . " thcv
join station_profile on thcv.station_id = station_profile.station_id
where station_profile.user_id = ?
and not exists (select 1 from dxcc_master where countrycode = thcv.col_dxcc and cqzone = col_cqz) and col_dxcc > 0
";
$params[] = $this->session->userdata('user_id');
$sql .= " order by station_profile.station_profile_name, thcv.col_time_on desc
limit 5000";
$query = $this->db->query($sql, $params);
return $query->result();
}
function getIncorrectItuZones() {
if(!clubaccess_check(9)) return;
$sql = "select *, (select group_concat(distinct ituzone order by ituzone separator ', ') from dxcc_master where countrycode = thcv.col_dxcc and ituzone <> '' order by ituzone asc) as correctituzone
from " . $this->config->item('table_name') . " thcv
join station_profile on thcv.station_id = station_profile.station_id
where station_profile.user_id = ?
and not exists (select 1 from dxcc_master where countrycode = thcv.col_dxcc and ituzone = col_ituz) and col_dxcc > 0
";
$params[] = $this->session->userdata('user_id');
$sql .= " order by station_profile.station_profile_name, thcv.col_time_on desc
limit 5000";
$query = $this->db->query($sql, $params);
return $query->result();
}
}

View File

@@ -31,6 +31,15 @@ switch ($type) {
case 'checkdxcc':
check_dxcc($result, $custom_date_format);
break;
case 'checkincorrectgridsquares':
check_incorrect_gridsquares($result, $custom_date_format);
break;
case 'checkincorrectcqzones':
check_incorrect_cq_zones($result, $custom_date_format);
break;
case 'checkincorrectituzones':
check_incorrect_itu_zones($result, $custom_date_format);
break;
default:
// Invalid type
break;
@@ -154,3 +163,189 @@ function check_dxcc($result, $custom_date_format) { ?>
<?php }
}
function check_incorrect_gridsquares($result, $custom_date_format) { ?>
<h5><?= __("Gridsquare Check Results") ?></h5>
<?php
if (is_array($result) && isset($result['status']) && $result['status'] == 'error') {
echo '<div class="alert alert-danger" role="alert">' . htmlspecialchars($result['message']) . '</div>';
return;
}
if ($result) { ?>
<?= __("These QSOs MAY have incorrect gridsquares.") ?>
<?= __("Results depends on the correct DXCC. The gridsquare list comes from the TQSL gridsquare database.") ?>
<div class="table-responsive">
<table class="table table-sm table-striped table-bordered table-condensed" id="gridsquareCheckTable">
<thead>
<tr>
<th><?= __("Callsign"); ?></th>
<th><?= __("QSO Date"); ?></th>
<th class="select-filter" scope="col"><?= __("Station Profile"); ?></th>
<th class="select-filter" scope="col"><?= __("DXCC"); ?></th>
<th><?= __("Gridsquare"); ?></th>
<th><?= __("DXCC Gridsquare"); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($result as $qso): ?>
<tr id="qsoID-<?php echo $qso->col_primary_key; ?>">
<td><?php echo '<a id="edit_qso" href="javascript:displayQso(' . $qso->col_primary_key . ')">' . htmlspecialchars($qso->col_call) . '</a>'; ?></td>
<td><?php echo date($custom_date_format, strtotime($qso->col_time_on)); ?></td>
<td><?php echo $qso->station_profile_name; ?></td>
<td><?php echo htmlspecialchars(ucwords(strtolower($qso->col_country), "- (/"), ENT_QUOTES, 'UTF-8'); ?></td>
<td><?php echo $qso->col_gridsquare; ?></td>
<td>
<?php
$gridsquare = $qso->correctgridsquare;
$maxChars = 50;
if (strlen($gridsquare) > $maxChars) {
$truncated = substr($gridsquare, 0, $maxChars);
$uniqueId = 'gridsquare-' . $qso->col_primary_key;
echo '<span id="' . $uniqueId . '-short">' . htmlspecialchars($truncated) . '...</span> ';
echo '<span id="' . $uniqueId . '-full" style="display:none;">' . htmlspecialchars($gridsquare) . '</span> ';
echo '<a href="javascript:void(0)" onclick="toggleGridsquare(\'' . $uniqueId . '\')" id="' . $uniqueId . '-link">' . __('Show more') . '</a>';
} else {
echo htmlspecialchars($gridsquare);
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</tfoot>
</table>
</div>
<?php }
}
function check_incorrect_cq_zones($result, $custom_date_format) { ?>
<h5><?= __("CQ Zone Check Results") ?></h5>
<?php if ($result) {
echo __("The following QSOs were found to have an incorrect CQ zone that this DXCC normally has (a maximum of 5000 QSOs are shown):");
echo '<table style="width:100%" class="qsolist table table-sm table-bordered table-hover table-striped table-condensed" id="incorrectcqzonetable">
<thead>
<tr>
<th style=\'text-align: center\'>' . __("Date") . '</th>
<th style=\'text-align: center\'>' . __("Time") . '</th>
<th style=\'text-align: center\'>' . __("Callsign") . '</th>
<th style=\'text-align: center\'>' . __("Mode") . '</th>
<th style=\'text-align: center\'>' . __("Band") . '</th>
<th style=\'text-align: center\'>' . __("Gridsquare") . '</th>
<th style=\'text-align: center\'>' . __("CQ Zone") . '</th>
<th style=\'text-align: center\'>' . __("DXCC CQ Zone") . '</th>
<th style=\'text-align: center\' class="select-filter" scope="col">' . __("DXCC") . '</th>
<th style=\'text-align: center\' class="select-filter" scope="col">' . __("Station") . '</th>
</tr>
</thead><tbody>';
$i = 0;
foreach ($result as $qso) {
echo '<tr id="qso_'. $qso->COL_PRIMARY_KEY .'">';
echo '<td style=\'text-align: center\'>'; $timestamp = strtotime($qso->COL_TIME_ON); echo date($custom_date_format, $timestamp); echo '</td>';
echo '<td style=\'text-align: center\'>'; $timestamp = strtotime($qso->COL_TIME_ON); echo date('H:i', $timestamp); echo '</td>';
echo '<td style=\'text-align: center\'><a id="edit_qso" href="javascript:displayQso(' . $qso->COL_PRIMARY_KEY . ')">' . str_replace("0","&Oslash;",strtoupper($qso->COL_CALL)) . '</a></td>';
echo '<td style=\'text-align: center\'>'; echo $qso->COL_SUBMODE==null?$qso->COL_MODE:$qso->COL_SUBMODE; echo '</td>';
echo '<td style=\'text-align: center\'>'; if($qso->COL_SAT_NAME != null) { echo $qso->COL_SAT_NAME; } else { echo strtolower($qso->COL_BAND); }; echo '</td>';
echo '<td style=\'text-align: center\'>'; echo strlen($qso->COL_GRIDSQUARE ?? '')==0?$qso->COL_VUCC_GRIDS:$qso->COL_GRIDSQUARE; echo '</td>';
echo '<td style=\'text-align: center\'>' . $qso->COL_CQZ . '</td>';
echo '<td style=\'text-align: center\'>' . $qso->correctcqzone . '</td>';
echo '<td style=\'text-align: center\'>' . ucwords(strtolower($qso->COL_COUNTRY), "- (/") . '</td>';
echo '<td style=\'text-align: center\'>' . $qso->station_profile_name . '</td>';
echo '</tr>';
}
echo '</tbody>
<tfoot>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</tfoot>
</table>';
?>
<?php
} else {
echo '<div class="alert alert-success">' . __("No incorrect CQ Zones were found.") . '</div>';
}
}
function check_incorrect_itu_zones($result, $custom_date_format) { ?>
<h5><?= __("ITU Zone Check Results") ?></h5>
<?php if ($result) {
echo __("The following QSOs were found to have an incorrect ITU zone that this DXCC normally has (a maximum of 5000 QSOs are shown):");
echo '<table style="width:100%" class="qsolist table table-sm table-bordered table-hover table-striped table-condensed" id="incorrectituzonetable">
<thead>
<tr>
<th style=\'text-align: center\'>' . __("Date") . '</th>
<th style=\'text-align: center\'>' . __("Time") . '</th>
<th style=\'text-align: center\'>' . __("Callsign") . '</th>
<th style=\'text-align: center\'>' . __("Mode") . '</th>
<th style=\'text-align: center\'>' . __("Band") . '</th>
<th style=\'text-align: center\'>' . __("Gridsquare") . '</th>
<th style=\'text-align: center\'>' . __("ITU Zone") . '</th>
<th style=\'text-align: center\'>' . __("DXCC ITU Zone") . '</th>
<th style=\'text-align: center\' class="select-filter" scope="col">' . __("DXCC") . '</th>
<th style=\'text-align: center\' class="select-filter" scope="col">' . __("Station") . '</th>
</tr>
</thead><tbody>';
$i = 0;
foreach ($result as $qso) {
echo '<tr id="qso_'. $qso->COL_PRIMARY_KEY .'">';
echo '<td style=\'text-align: center\'>'; $timestamp = strtotime($qso->COL_TIME_ON); echo date($custom_date_format, $timestamp); echo '</td>';
echo '<td style=\'text-align: center\'>'; $timestamp = strtotime($qso->COL_TIME_ON); echo date('H:i', $timestamp); echo '</td>';
echo '<td style=\'text-align: center\'><a id="edit_qso" href="javascript:displayQso(' . $qso->COL_PRIMARY_KEY . ')">' . str_replace("0","&Oslash;",strtoupper($qso->COL_CALL)) . '</a></td>';
echo '<td style=\'text-align: center\'>'; echo $qso->COL_SUBMODE==null?$qso->COL_MODE:$qso->COL_SUBMODE; echo '</td>';
echo '<td style=\'text-align: center\'>'; if($qso->COL_SAT_NAME != null) { echo $qso->COL_SAT_NAME; } else { echo strtolower($qso->COL_BAND); }; echo '</td>';
echo '<td style=\'text-align: center\'>'; echo strlen($qso->COL_GRIDSQUARE ?? '')==0?$qso->COL_VUCC_GRIDS:$qso->COL_GRIDSQUARE; echo '</td>';
echo '<td style=\'text-align: center\'>' . $qso->COL_ITUZ . '</td>';
echo '<td style=\'text-align: center\'>' . $qso->correctituzone . '</td>';
echo '<td style=\'text-align: center\'>' . ucwords(strtolower($qso->COL_COUNTRY), "- (/") . '</td>';
echo '<td style=\'text-align: center\'>' . $qso->station_profile_name . '</td>';
echo '</tr>';
}
echo '</tbody>
<tfoot>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</tfoot>
</table>';
?>
<?php
} else {
echo '<div class="alert alert-success">' . __("No incorrect CQ Zones were found.") . '</div>';
}
}

View File

@@ -17,6 +17,17 @@
</button>
</div>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1"><?= __("Check CQ Zones") ?></h6>
<p class="mb-1 small text-muted"><?= __("Check incorrect CQ zone information") ?></p>
</div>
<div class="d-flex nowrap">
<button type="button" class="btn btn-sm btn-success me-1 ld-ext-right" id="checkIncorrectCqZonesBtn" onclick="checkIncorrectCqZones()">
<?= __("Check") ?><div class="ld ld-ring ld-spin"></div>
</button>
</div>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
@@ -30,6 +41,30 @@
</div>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1"><?= __("Check ITU Zones") ?></h6>
<p class="mb-1 small text-muted"><?= __("Check incorrect ITU zone information") ?></p>
</div>
<div class="d-flex nowrap">
<button type="button" class="btn btn-sm btn-success me-1 ld-ext-right" id="checkIncorrectItuZonesBtn" onclick="checkIncorrectItuZones()">
<?= __("Check") ?><div class="ld ld-ring ld-spin"></div>
</button>
</div>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1"><?= __("Check Gridsquares") ?></h6>
<p class="mb-1 small text-muted"><?= __("Check gridsquares that does not match the DXCC") ?></p>
</div>
<div class="d-flex nowrap">
<button type="button" class="btn btn-sm btn-success me-1 ld-ext-right" id="checkIncorrectGridsquaresBtn" onclick="checkIncorrectGridsquares()">
<?= __("Check") ?><div class="ld ld-ring ld-spin"></div>
</button>
</div>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1"><?= __("Fix Continent") ?></h6>

View File

@@ -74,6 +74,10 @@
let lang_gen_advanced_logbook_dupe_search = '<?= __("Duplicate Search"); ?>';
let lang_gen_advanced_logbook_search = '<?= __("Search"); ?>';
let lang_gen_advanced_logbook_show_more = '<?= __("Show more"); ?>';
let lang_gen_advanced_logbook_show_less = '<?= __("Show less"); ?>';
let homegrid ='<?php echo strtoupper($homegrid[0]); ?>';
<?php

View File

@@ -2476,19 +2476,10 @@ function saveOptions() {
$('.result').html(response);
},
error: function(xhr, status, error) {
$('#checkGridsBtn').prop('disabled', false).text('<?= __("Check") ?>');
$('#checkGridsBtn').prop("disabled", false).removeClass("running");
$('#closeButton').prop('disabled', false);
let errorMsg = 'Error checking continent information';
if (xhr.responseJSON && xhr.responseJSON.message) {
errorMsg += ': ' + xhr.responseJSON.message;
}
BootstrapDialog.alert({
title: 'Error',
message: errorMsg,
type: BootstrapDialog.TYPE_DANGER
});
$('.result').html(error);
}
});
}
@@ -2532,19 +2523,125 @@ function saveOptions() {
rebind_checkbox_trigger_dxcc();
},
error: function(xhr, status, error) {
$('#checkDxccBtn').prop('disabled', false).text('<?= __("Check") ?>');
$('#checkDxccBtn').prop("disabled", false).removeClass("running");
$('#closeButton').prop('disabled', false);
$('.result').html(error);
}
});
}
let errorMsg = 'Error checking DXCC information';
if (xhr.responseJSON && xhr.responseJSON.message) {
errorMsg += ': ' + xhr.responseJSON.message;
}
function checkIncorrectCqZones() {
$('#checkIncorrectCqZonesBtn').prop("disabled", true).addClass("running");
$('#closeButton').prop("disabled", true);
BootstrapDialog.alert({
title: 'Error',
message: errorMsg,
type: BootstrapDialog.TYPE_DANGER
$.ajax({
url: base_url + 'index.php/logbookadvanced/checkDb',
data: {
type: 'checkincorrectcqzones'
},
type: 'POST',
success: function(response) {
$('#checkIncorrectCqZonesBtn').prop("disabled", false).removeClass("running");
$('#closeButton').prop("disabled", false);
$('.result').html(response);
$('#incorrectcqzonetable').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 = $('<select class="form-select form-select-sm"><option value=""></option></select>')
.appendTo($(column.footer()).empty())
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column
.data()
.unique()
.sort()
.each(function (d, j) {
select.append('<option value="' + d + '">' + d + '</option>');
});
});
},
});
},
error: function(xhr, status, error) {
$('#checkIncorrectCqZonesBtn').prop("disabled", false).removeClass("running");
$('#closeButton').prop('disabled', false);
$('.result').html(error);
}
});
}
function checkIncorrectItuZones() {
$('#checkIncorrectItuZonesBtn').prop("disabled", true).addClass("running");
$('#closeButton').prop("disabled", true);
$.ajax({
url: base_url + 'index.php/logbookadvanced/checkDb',
data: {
type: 'checkincorrectituzones'
},
type: 'POST',
success: function(response) {
$('#checkIncorrectItuZonesBtn').prop("disabled", false).removeClass("running");
$('#closeButton').prop("disabled", false);
$('.result').html(response);
$('#incorrectituzonetable').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 = $('<select class="form-select form-select-sm"><option value=""></option></select>')
.appendTo($(column.footer()).empty())
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column
.data()
.unique()
.sort()
.each(function (d, j) {
select.append('<option value="' + d + '">' + d + '</option>');
});
});
},
});
},
error: function(xhr, status, error) {
$('#checkIncorrectItuZonesBtn').prop("disabled", false).removeClass("running");
$('#closeButton').prop('disabled', false);
$('.result').html(error);
}
});
}
@@ -2619,3 +2716,76 @@ function saveOptions() {
}
});
}
function checkIncorrectGridsquares() {
$('#checkIncorrectGridsquaresBtn').prop("disabled", true).addClass("running");
$('#closeButton').prop("disabled", true);
$.ajax({
url: base_url + 'index.php/logbookadvanced/checkDb',
data: {
type: 'checkincorrectgridsquares'
},
type: 'POST',
success: function(response) {
$('#checkIncorrectGridsquaresBtn').prop("disabled", false).removeClass("running");
$('#closeButton').prop("disabled", false);
$('.result').html(response);
$('#gridsquareCheckTable').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 = $('<select class="form-select form-select-sm"><option value=""></option></select>')
.appendTo($(column.footer()).empty())
.on('change', function () {
var val = $.fn.dataTable.util.escapeRegex($(this).val());
column.search(val ? '^' + val + '$' : '', true, false).draw();
});
column
.data()
.unique()
.sort()
.each(function (d, j) {
select.append('<option value="' + d + '">' + d + '</option>');
});
});
},
});
},
error: function(xhr, status, error) {
$('#checkIncorrectGridsquaresBtn').prop("disabled", false).removeClass("running");
$('#closeButton').prop('disabled', false);
$('.result').html(error);
}
});
}
function toggleGridsquare(id) {
const shortSpan = document.getElementById(id + '-short');
const fullSpan = document.getElementById(id + '-full');
const link = document.getElementById(id + '-link');
if (shortSpan.style.display === 'none') {
shortSpan.style.display = 'inline';
fullSpan.style.display = 'none';
link.textContent = lang_gen_advanced_logbook_show_more;
} else {
shortSpan.style.display = 'none';
fullSpan.style.display = 'inline';
link.textContent = lang_gen_advanced_logbook_show_less;
}
}