[Advanced Logbook] Added a dialog with parameter for duplicate search

This commit is contained in:
Andreas Kristiansen
2025-12-22 08:25:26 +01:00
parent c17a4442b0
commit a4bfa14298
5 changed files with 185 additions and 20 deletions

View File

@@ -145,6 +145,10 @@ class Logbookadvanced extends CI_Controller {
'wwff' => xss_clean($this->input->post('wwff')),
'qslimages' => xss_clean($this->input->post('qslimages')),
'dupes' => xss_clean($this->input->post('dupes')),
'dupedate' => xss_clean($this->input->post('dupedate')),
'dupemode' => xss_clean($this->input->post('dupemode')),
'dupeband' => xss_clean($this->input->post('dupeband')),
'dupesat' => xss_clean($this->input->post('dupesat')),
'operator' => xss_clean($this->input->post('operator')),
'contest' => xss_clean($this->input->post('contest')),
'invalid' => xss_clean($this->input->post('invalid')),
@@ -982,4 +986,10 @@ class Logbookadvanced extends CI_Controller {
$this->load->view('logbookadvanced/showUpdateResult', $data);
}
function dupeSearchDialog() {
if(!clubaccess_check(9)) return;
$this->load->view('logbookadvanced/dupesearchdialog');
}
}

View File

@@ -3,6 +3,81 @@ use Wavelog\QSLManager\QSO;
class Logbookadvanced_model extends CI_Model {
public function dupeSearchQuery($searchCriteria, $binding) {
$group_by_append = '';
$order_by = '';
$order_by .= ' order by col_call';
$id_sql = "select GROUP_CONCAT(col_primary_key separator ',') as qsoids, COL_CALL, station_callsign, min(col_time_on) Mintime, max(col_time_on) Maxtime";
if (isset($searchCriteria['dupemode']) && $searchCriteria['dupemode'] === 'Y') {
$id_sql .= ", COL_MODE, COL_SUBMODE";
$group_by_append .= ", COL_MODE, COL_SUBMODE";
}
if (isset($searchCriteria['dupeband']) && $searchCriteria['dupeband'] === 'Y') {
$id_sql .= ", COL_BAND";
$group_by_append .= ", COL_BAND";
}
if (isset($searchCriteria['dupesat']) && $searchCriteria['dupesat'] === 'Y') {
$id_sql .= ", COL_SAT_NAME";
$group_by_append .= ", COL_SAT_NAME";
}
$id_sql .= " from " . $this->config->item('table_name') . "
join station_profile on " . $this->config->item('table_name') . ".station_id = station_profile.station_id where station_profile.user_id = ?";
$id_sql .= "group by COL_CALL, station_callsign";
$id_sql .= $group_by_append;
$id_sql .= " having count(*) > 1";
if (isset($searchCriteria['dupedate']) && $searchCriteria['dupedate'] === 'Y') {
$id_sql .= " AND TIMESTAMPDIFF(SECOND, Mintime, Maxtime) < 1800";
$order_by .= ' , col_time_on desc';
}
$id_query = $this->db->query($id_sql, array($this->session->userdata('user_id')));
$ids2fetch = '';
foreach ($id_query->result() as $id) {
$ids2fetch .= ','.$id->qsoids;
}
$ids2fetch = ltrim($ids2fetch, ',');
if ($ids2fetch ?? '' !== '') {
$conditions[] = "qsos.COL_PRIMARY_KEY in (".$ids2fetch.")";
} else {
$conditions[] = "1=0";
}
if (($searchCriteria['ids'] ?? '') !== '') {
$conditions[] = "qsos.COL_PRIMARY_KEY in (".implode(",",$searchCriteria['ids']).")";
}
$where = trim(implode(" AND ", $conditions));
if ($where != "") {
$where = "AND $where";
}
$limit = '';
if ($searchCriteria['qsoresults'] != 'All') {
$limit = 'limit ' . $searchCriteria['qsoresults'];
}
$sql = "
SELECT qsos.*, dxcc_entities.*, lotw_users.*, station_profile.*, satellite.*, dxcc_entities.name as dxccname, mydxcc.name AS station_country, exists(select 1 from qsl_images where qsoid = qsos.COL_PRIMARY_KEY) as qslcount, coalesce(contest.name, qsos.col_contest_id) as contestname
FROM " . $this->config->item('table_name') . " qsos
INNER JOIN station_profile ON qsos.station_id=station_profile.station_id
LEFT OUTER JOIN satellite ON qsos.col_prop_mode='SAT' and qsos.COL_SAT_NAME = COALESCE(NULLIF(satellite.name, ''), NULLIF(satellite.displayname, ''))
LEFT OUTER JOIN dxcc_entities ON qsos.col_dxcc = dxcc_entities.adif
left outer join dxcc_entities mydxcc on qsos.col_my_dxcc = mydxcc.adif
LEFT OUTER JOIN lotw_users ON qsos.col_call = lotw_users.callsign
LEFT OUTER JOIN contest ON qsos.col_contest_id = contest.adifname
WHERE station_profile.user_id = ?
$where
$order_by
$limit
";
return $this->db->query($sql, $binding);
}
public function searchDb($searchCriteria) {
$conditions = [];
$binding = [$searchCriteria['user_id']];
@@ -13,20 +88,7 @@ class Logbookadvanced_model extends CI_Model {
}
if ((isset($searchCriteria['dupes'])) && ($searchCriteria['dupes'] !== '')) {
$id_sql="select GROUP_CONCAT(col_primary_key separator ',') as qsoids, COL_CALL, COL_MODE, COL_SUBMODE, station_callsign, COL_SAT_NAME, COL_BAND, min(col_time_on) Mintime, max(col_time_on) Maxtime from " . $this->config->item('table_name') . "
join station_profile on " . $this->config->item('table_name') . ".station_id = station_profile.station_id where station_profile.user_id = ?
group by col_call, col_mode, COL_SUBMODE, STATION_CALLSIGN, col_band, COL_SAT_NAME having count(*) > 1 AND TIMESTAMPDIFF(SECOND, Mintime, Maxtime) < 1500";
$id_query = $this->db->query($id_sql, $searchCriteria['user_id']);
$ids2fetch = '';
foreach ($id_query->result() as $id) {
$ids2fetch .= ','.$id->qsoids;
}
$ids2fetch = ltrim($ids2fetch, ',');
if ($ids2fetch ?? '' !== '') {
$conditions[] = "qsos.COL_PRIMARY_KEY in (".$ids2fetch.")";
} else {
$conditions[] = "1=0";
}
return $this->dupeSearchQuery($searchCriteria, $binding);
}
if ((isset($searchCriteria['invalid'])) && ($searchCriteria['invalid'] !== '')) {
@@ -472,11 +534,6 @@ class Logbookadvanced_model extends CI_Model {
$where = "AND $where";
}
$where = trim(implode(" AND ", $conditions));
if ($where != "") {
$where = "AND $where";
}
$limit = '';
if ($searchCriteria['qsoresults'] != 'All') {

View File

@@ -0,0 +1,49 @@
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-search me-2"></i><?= __("Search for duplicates using:"); ?>
</h5>
</div>
<div class="card-body">
<form method="post">
<div class="row g-3">
<div class="col-md-6">
<div class="form-check form-check-lg border rounded p-3 bg-light h-100">
<input class="form-check-input" type="checkbox" name="date_check" id="date_check" checked>
<label class="form-check-label fw-semibold" for="date_check">
<i class="fas fa-calendar-alt me-2 text-primary"></i><?= __("Date"); ?>
</label>
<small class="d-block text-muted"><?= __("Match QSOs within 1800 (30min) of each other"); ?></small>
</div>
</div>
<div class="col-md-6">
<div class="form-check form-check-lg border rounded p-3 bg-light h-100">
<input class="form-check-input" type="checkbox" name="mode_check" id="mode_check" checked>
<label class="form-check-label fw-semibold" for="mode_check">
<i class="fas fa-broadcast-tower me-2 text-success"></i><?= __("Mode"); ?>
</label>
<small class="d-block text-muted"><?= __("Match QSOs with the same mode (SSB, CW, FM, etc.)"); ?></small>
</div>
</div>
<div class="col-md-6">
<div class="form-check form-check-lg border rounded p-3 bg-light h-100">
<input class="form-check-input" type="checkbox" name="band_check" id="band_check" checked>
<label class="form-check-label fw-semibold" for="band_check">
<i class="fas fa-wave-square me-2 text-warning"></i><?= __("Band"); ?>
</label>
<small class="d-block text-muted"></small><?= __("Match QSOs on the same band"); ?></small>
</div>
</div>
<div class="col-md-6">
<div class="form-check form-check-lg border rounded p-3 bg-light h-100">
<input class="form-check-input" type="checkbox" name="satellite_check" id="satellite_check" checked>
<label class="form-check-label fw-semibold" for="satellite_check">
<i class="fas fa-satellite me-2 text-info"></i><?= __("Satellite"); ?>
</label>
<small class="d-block text-muted"><?= __("Match QSOs using the same satellite"); ?></small>
</div>
</div>
</div>
</form>
</div>
</div>

View File

@@ -71,6 +71,9 @@
let lang_gen_advanced_logbook_distances_updated = '<?= __("Distances updated successfully!"); ?>';
let lang_gen_advanced_logbook_confirm_fix_missing_dxcc = '<?= __("Are you sure you want to fix all QSOs with missing DXCC information? This action cannot be undone."); ?>';
let lang_gen_advanced_logbook_dupe_search = '<?= __("Duplicate Search"); ?>';
let lang_gen_advanced_logbook_search = '<?= __("Search"); ?>';
let homegrid ='<?php echo strtoupper($homegrid[0]); ?>';
<?php
@@ -262,6 +265,10 @@ $options = json_decode($options);
<form id="searchForm" name="searchForm" action="<?php echo base_url() . "index.php/logbookadvanced/search"; ?>" method="post">
<input type="hidden" id="dupes" name="dupes" value="">
<input type="hidden" id="invalid" name="invalid" value="">
<input type="hidden" id="dupedate" name="dupedate" value="">
<input type="hidden" id="dupemode" name="dupemode" value="">
<input type="hidden" id="dupeband" name="dupeband" value="">
<input type="hidden" id="dupesat" name="dupesat" value="">
<div class="row pt-2">
<div class="d-flex flex-wrap btn-group w-auto mx-auto">

View File

@@ -745,6 +745,10 @@ $(document).ready(function () {
wwff: this.wwff.value,
qslimages: this.qslimages.value,
dupes: this.dupes.value,
dupedate: this.dupedate.value,
dupemode: this.dupemode.value,
dupeband: this.dupeband.value,
dupesat: this.dupesat.value,
contest: this.contest.value,
invalid: this.invalid.value,
continent: this.continent.value,
@@ -1088,7 +1092,7 @@ $(document).ready(function () {
});
$('#dupeButton').click(function (event) {
dupeSearch();
dupeSearchDialog();
});
$('#invalidButton').click(function (event) {
@@ -1457,6 +1461,44 @@ $(document).ready(function () {
});
});
function dupeSearchDialog() {
$.ajax({
url: base_url + 'index.php/logbookadvanced/dupeSearchDialog',
type: 'post',
success: function (html) {
BootstrapDialog.show({
title: lang_gen_advanced_logbook_dupe_search,
size: BootstrapDialog.SIZE_NORMAL,
cssClass: 'options',
nl2br: false,
message: html,
buttons: [
{
label: lang_gen_advanced_logbook_search + ' <div class="ld ld-ring ld-spin"></div>',
cssClass: 'btn btn-sm btn-primary ld-ext-right',
id: 'dupeSearchButton',
action: function (dialogItself) {
dialogItself.close();
$('#dupedate').val($('#date_check').is(':checked') ? "Y" : "N");
$('#dupemode').val($('#mode_check').is(':checked') ? "Y" : "N");
$('#dupeband').val($('#band_check').is(':checked') ? "Y" : "N");
$('#dupesat').val($('#satellite_check').is(':checked') ? "Y" : "N");
dupeSearch();
}
},
{
label: lang_admin_close,
cssClass: 'btn btn-sm btn-secondary',
id: 'closeDupeDialogButton',
action: function (dialogItself) {
dialogItself.close();
}
}],
});
}
});
}
function dupeSearch() {
$("#dupes").val("Y");
$('#dupeButton').prop('disabled', true).addClass('running');