[Logbookadvanced] First version of a logbook with filtering and batchprocessing of QSL handling.

这个提交包含在:
Andreas 2022-12-18 16:49:54 +01:00
父节点 96219b634a
当前提交 dbc8a5451a
共有 8 个文件被更改,包括 1576 次插入2 次删除

查看文件

@ -0,0 +1,170 @@
<?php
use Cloudlog\QSLManager\QSO;
if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Logbookadvanced extends CI_Controller {
function __construct()
{
parent::__construct();
$this->load->helper(array('form', 'url', 'psr4_autoloader'));
$this->load->model('user_model');
if (!$this->user_model->authorize(2)) {
$this->session->set_flashdata('notice', 'You\'re not allowed to do that!');
redirect('dashboard');
}
}
function index() {
$this->load->model('stations');
$this->load->model('logbookadvanced_model');
$this->load->model('logbook_model');
$this->load->model('bands');
$this->load->model('iota');
$this->load->model('dxcc');
$stationIds = [];
$deOptions = [];
foreach ($this->stations->all_of_user()->result() as $station) {
$deOptions[$station->station_callsign] = 1;
$stationIds[] = $station->station_id;
}
ksort($deOptions);
$deOptions = array_keys($deOptions);
$modes = [];
if ($stationIds !== []) {
foreach ($this->logbookadvanced_model->get_worked_modes($stationIds) as $mode) {
$key = $mode['mode'];
if ($mode['submode'] !== null) {
$key .= "|" . $mode['submode'];
}
if ($mode['submode'] == null) {
$modes[$key] = $mode['mode'];
} else {
$modes[$key] = $mode['submode'];
}
}
}
$data = [];
$data['page_title'] = "Advanced logbook";
$data['hasDatePicker'] = true;
$pageData = [];
$pageData['datePlaceholder'] = 'DD/MM/YYYY';
$pageData['deOptions'] = $deOptions;
$pageData['modes'] = $modes;
$pageData['dxccarray'] = $this->logbook_model->fetchDxcc();
$pageData['iotaarray'] = $this->logbook_model->fetchIota();
$pageData['bands'] = $this->bands->get_worked_bands();
$footerData = [];
$footerData['scripts'] = [
'assets/js/moment.min.js',
'assets/js/tempusdominus-bootstrap-4.min.js',
'assets/js/sections/logbookadvanced.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/logbookadvanced.js"))
];
$this->load->view('interface_assets/header', $data);
$this->load->view('logbookadvanced/index', $pageData);
$this->load->view('interface_assets/footer', $footerData);
}
public function search() {
$this->load->model('logbookadvanced_model');
$searchCriteria = array(
'user_id' => (int)$this->session->userdata('user_id'),
'dateFrom' => xss_clean($this->input->post('dateFrom')),
'dateTo' => xss_clean($this->input->post('dateTo')),
'de' => xss_clean($this->input->post('de')),
'dx' => xss_clean($this->input->post('dx')),
'mode' => xss_clean($this->input->post('mode')),
'band' => xss_clean($this->input->post('band')),
'qslSent' => xss_clean($this->input->post('qslSent')),
'qslReceived' => xss_clean($this->input->post('qslReceived')),
'iota' => xss_clean($this->input->post('iota')),
'dxcc' => xss_clean($this->input->post('dxcc')),
'propmode' => xss_clean($this->input->post('propmode')),
'gridsquare' => xss_clean($this->input->post('gridsquare')),
'state' => xss_clean($this->input->post('state')),
);
$qsos = [];
foreach ($this->logbookadvanced_model->searchQsos($searchCriteria) as $qso) {
$qsos[] = $qso->toArray();
}
header("Content-Type: application/json");
print json_encode($qsos);
}
public function updateFromCallbook() {
$this->load->model('logbook_model');
$qsoID = xss_clean($this->input->post('qsoID'));
$qso = $this->logbook_model->qso_info($qsoID)->row_array();
if ($qso === null) {
header("Content-Type: application/json");
echo json_encode([]);
return;
}
$callbook = $this->logbook_model->loadCallBook($qso['COL_CALL'], $this->config->item('use_fullname'));
if ($callbook['callsign'] !== "") {
$qso['COL_NAME'] = trim($callbook['name']);
$qso['COL_QSL_VIA'] = trim($callbook['qslmgr']);
}
$qsoObj = new QSO($qso);
header("Content-Type: application/json");
echo json_encode($qsoObj->toArray());
}
function export_to_adif() {
$this->load->model('logbookadvanced_model');
$ids = xss_clean($this->input->post('id'));
$user_id = (int)$this->session->userdata('user_id');
$data['qsos'] = $this->logbookadvanced_model->getQsosForAdif($ids, $user_id);
$this->load->view('adif/data/exportall', $data);
}
function update_qsl() {
$this->load->model('logbookadvanced_model');
$ids = xss_clean($this->input->post('id'));
$user_id = (int)$this->session->userdata('user_id');
$method = xss_clean($this->input->post('method'));
$sent = xss_clean($this->input->post('sent'));
$status = $this->logbookadvanced_model->updateQsl($ids, $user_id, $method, $sent);
$data = $this->logbookadvanced_model->getQsosForAdif($ids, $user_id);
$results = $data->result('array');
$qsos = [];
foreach ($results as $data) {
$qsos[] = new QSO($data);
}
$q = [];
foreach ($qsos as $qso) {
$q[] = $qso->toArray();
}
header("Content-Type: application/json");
print json_encode($q);
}
}

查看文件

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
defined('BASEPATH') OR exit('No direct script access allowed');
/*
* Extremely simple autoloader helper, has lots of shortcomings
*/
spl_autoload_register(function ($class) {
if (mb_substr($class, 0, 9) !== 'Cloudlog\\') {
return false;
}
$class=substr($class,9);
$file = str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php';
$file = __DIR__ . "/../../src/" . $file;
if (file_exists($file)) {
require $file;
return true;
}
return false;
});

查看文件

@ -0,0 +1,184 @@
<?php
use Cloudlog\QSLManager\QSO;
class Logbookadvanced_model extends CI_Model {
/**
* Returns worked modes in the supplied stations as a simple array
* @param array $stationIds
* @return array
*/
function get_worked_modes(array $stationIds): array {
$CI =& get_instance();
$CI->load->model('logbooks_model');
$ids = "'".implode("','",$stationIds)."'";
$sql = "
SELECT distinct `COL_MODE`, `COL_SUBMODE`
FROM `" . $this->config->item('table_name') . "` qsos
WHERE qsos.station_id IN (".$ids.")
ORDER BY COL_MODE, COL_SUBMODE";
$data = $this->db->query($sql);
$results = [];
foreach ($data->result() as $row) {
$results[] = [
'mode' => $row->COL_MODE,
'submode' => $row->COL_SUBMODE
];
}
return $results;
}
/*
* @param array $searchCriteria
* @return array
*/
public function searchQsos($searchCriteria) : array {
$conditions = [];
$binding = [$searchCriteria['user_id']];
if ($searchCriteria['dateFrom'] !== '') {
$from = DateTime::createFromFormat('d/m/Y', $searchCriteria['dateFrom']);
$from = $from->format('Y-m-d');
$conditions[] = "date(COL_TIME_ON) >= ?";
$binding[] = $from;
}
if ($searchCriteria['dateTo'] !== '') {
$to = DateTime::createFromFormat('d/m/Y', $searchCriteria['dateTo']);
$to = $to->format('Y-m-d');
$conditions[] = "date(COL_TIME_ON) <= ?";
$binding[] = $to;
}
if ($searchCriteria['de'] !== '') {
$conditions[] = "COL_STATION_CALLSIGN = ?";
$binding[] = trim($searchCriteria['de']);
}
if ($searchCriteria['dx'] !== '') {
$conditions[] = "COL_CALL LIKE ?";
$binding[] = '%' . trim($searchCriteria['dx']) . '%';
}
if ($searchCriteria['mode'] !== '') {
list ($mode, $subMode) = explode("|", trim($searchCriteria['mode']));
$mode = trim($mode);
$subMode = trim($subMode);
$conditions[] = "COL_MODE = ?";
$binding[] = $mode;
if ($subMode !== '') {
$conditions[] = "COL_SUBMODE = ?";
$binding[] = $subMode;
}
}
if ($searchCriteria['band'] !== '') {
$conditions[] = "COL_BAND = ?";
$binding[] = trim($searchCriteria['band']);
}
if ($searchCriteria['qslSent'] !== '') {
$conditions[] = "COL_QSL_SENT = ?";
$binding[] = $searchCriteria['qslSent'];
}
if ($searchCriteria['qslReceived'] !== '') {
$conditions[] = "COL_QSL_RCVD = ?";
$binding[] = $searchCriteria['qslReceived'];
}
if ($searchCriteria['iota'] !== '') {
$conditions[] = "COL_IOTA = ?";
$binding[] = $searchCriteria['iota'];
}
if ($searchCriteria['dxcc'] !== '') {
$conditions[] = "COL_DXCC = ?";
$binding[] = $searchCriteria['dxcc'];
}
if ($searchCriteria['state'] !== '') {
$conditions[] = "COL_STATE = ?";
$binding[] = $searchCriteria['state'];
}
if ($searchCriteria['gridsquare'] !== '') {
$conditions[] = "(COL_GRIDSQUARE like ? or COL_VUCC_GRIDS like ?)";
$binding[] = '%' . $searchCriteria['gridsquare'] . '%';
$binding[] = '%' . $searchCriteria['gridsquare'] . '%';
}
if ($searchCriteria['propmode'] !== '') {
$conditions[] = "COL_PROP_MODE = ?";
$binding[] = $searchCriteria['propmode'];
}
$where = trim(implode(" AND ", $conditions));
if ($where != "") {
$where = "AND $where";
}
if ($where === '') {
$limit = 250;
} else {
$limit = 1000;
}
$sql = "
SELECT *
FROM " . $this->config->item('table_name') . " qsos
INNER JOIN station_profile ON qsos.station_id=station_profile.station_id
WHERE station_profile.user_id = ?
$where
ORDER BY qsos.COL_TIME_ON desc
LIMIT $limit
";
$data = $this->db->query($sql, $binding);
$results = $data->result('array');
$qsos = [];
foreach ($results as $data) {
$qsos[] = new QSO($data);
}
return $qsos;
}
public function getQsosForAdif($ids, $user_id) : object {
$binding = [$user_id];
$conditions[] = "COL_PRIMARY_KEY in ?";
$binding[] = json_decode($ids, true);
$where = trim(implode(" AND ", $conditions));
if ($where != "") {
$where = "AND $where";
}
$sql = "
SELECT *
FROM " . $this->config->item('table_name') . " qsos
INNER JOIN station_profile ON qsos.station_id = station_profile.station_id
WHERE station_profile.user_id = ?
$where
ORDER BY qsos.COL_TIME_ON desc
";
return $this->db->query($sql, $binding);
}
public function updateQsl($ids, $user_id, $method, $sent) {
$this->load->model('user_model');
if(!$this->user_model->authorize(2)) {
return array('message' => 'Error');
} else {
$data = array(
'COL_QSLSDATE' => date('Y-m-d H:i:s'),
'COL_QSL_SENT' => $sent,
'COL_QSL_SENT_VIA' => $method
);
$this->db->where_in('COL_PRIMARY_KEY', json_decode($ids, true));
$this->db->update($this->config->item('table_name'), $data);
return array('message' => 'OK');
}
}
}

查看文件

@ -3187,5 +3187,14 @@ function deleteQsl(id) {
<?php } ?>
<?php } ?>
<?php
if (isset($scripts) && is_array($scripts)){
foreach($scripts as $script){
?><script type="text/javascript" src="<?php echo base_url() . $script ;?>"></script>
<?php
}
}
?>
</body>
</html>

查看文件

@ -36,7 +36,7 @@
<link rel="stylesheet" type="text/css" href="<?php echo base_url(); ?>assets/css/datatables.min.css"/>
<?php if ($this->uri->segment(1) == "adif") { ?>
<?php if ($this->uri->segment(1) == "adif" || (isset($hasDatePicker) && $hasDatePicker)) { ?>
<link rel="stylesheet" href="<?php echo base_url(); ?>assets/css/datepicker.css" />
<?php } ?>
@ -58,7 +58,7 @@
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="<?php echo site_url('logbook');?>">Logbook</a>
<a class="nav-link" href="<?php echo site_url('logbookadvanced');?>">Logbook</a>
<?php if(($this->config->item('use_auth')) && ($this->session->userdata('user_type') >= 2)) { ?>
<!-- QSO Menu Dropdown -->

查看文件

@ -0,0 +1,193 @@
<div class="container-fluid qso_manager pt-3 pl-4 pr-4">
<?php if ($this->session->flashdata('message')) { ?>
<!-- Display Message -->
<div class="alert-message error">
<p><?php echo $this->session->flashdata('message'); ?></p>
</div>
<?php } ?>
<form id="searchForm" name="searchForm" action="<?php echo base_url()."index.php/logbookadvanced/search";?>" method="post">
<div class="form-row">
<div class="form-group col-1">
<label class="form-label" for="dateFrom">From</label>
<div class="input-group input-group-sm date" id="dateFrom" data-target-input="nearest">
<input name="dateFrom" type="text" placeholder="<?php echo $datePlaceholder;?>" class="form-control" data-target="#dateFrom"/>
<div class="input-group-append" data-target="#dateFrom" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
</div>
<div class="form-group col-1">
<label for="dateTo">To</label>
<div class="input-group input-group-sm date" id="dateTo" data-target-input="nearest">
<input name="dateTo" type="text" placeholder="<?php echo $datePlaceholder;?>" class="form-control" data-target="#dateTo"/>
<div class="input-group-append" data-target="#dateTo" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
</div>
<div class="form-group col">
<label class="form-label" for="de">De</label>
<select id="de" name="de" class="form-control form-control-sm">
<option value="">All</option>
<?php
foreach($deOptions as $deOption){
?><option value="<?php echo htmlentities($deOption);?>"><?php echo htmlspecialchars($deOption);?></option><?php
}
?>
</select>
</div>
<div class="form-group col">
<label class="form-label" for="dx">Dx</label>
<input type="text" name="dx" id="dx" class="form-control form-control-sm" value="">
</div>
<div class="form-group col">
<label class="form-label" for="dxcc">DXCC</label>
<select class="form-control form-control-sm" id="dxcc" name="dxcc">
<option value="">-</option>
<option value="0">None (/MM, /AM)</option>
<?php
foreach($dxccarray as $dxcc){
echo '<option value=' . $dxcc->adif;
echo '>' . $dxcc->prefix . ' - ' . ucwords(strtolower($dxcc->name), "- (/");
if ($dxcc->Enddate != null) {
echo ' - (Deleted DXCC)';
}
echo '</option>';
}
?>
</select>
</div>
<div class="form-group col">
<label class="form-label" for="iota">IOTA</label>
<select class="form-control form-control-sm" id="iota" name="iota">
<option value ="">-</option>
<?php
foreach($iotaarray as $iota){
echo '<option value=' . $iota->tag;
echo '>' . $iota->tag . ' - ' . $iota->name . '</option>';
}
?>
</select>
</div>
<div class="form-group col">
<label class="form-label" for="state">State</label>
<input type="text" name="state" id="state" class="form-control form-control-sm" value="">
</div>
<div class="form-group col">
<label class="form-label" for="gridsquare">Gridsquare</label>
<input type="text" name="gridsquare" id="gridsquare" class="form-control form-control-sm" value="">
</div>
<div class="form-group col">
<label class="form-label" for="mode">Mode</label>
<select id="mode" name="mode" class="form-control form-control-sm">
<option value="">All</option>
<?php
foreach($modes as $modeId => $mode){
?><option value="<?php echo htmlentities($modeId);?>"><?php echo htmlspecialchars($mode);?></option><?php
}
?>
</select>
</div>
<div class="form-group col">
<label class="form-label" for="band">Band</label>
<select id="band" name="band" class="form-control form-control-sm">
<option value="">All</option>
<?php
foreach($bands as $band){
?><option value="<?php echo htmlentities($band);?>"><?php echo htmlspecialchars($band);?></option><?php
}
?>
</select>
</div>
<div class="form-group col">
<label class="form-label" for="selectPropagation">Propagation Mode</label>
<select id="selectPropagation" name="selectPropagation" class="form-control form-control-sm">
<option value="">All</option>
<option value="AS">Aircraft Scatter</option>
<option value="AUR">Aurora</option>
<option value="AUE">Aurora-E</option>
<option value="BS">Back scatter</option>
<option value="ECH">EchoLink</option>
<option value="EME">Earth-Moon-Earth</option>
<option value="ES">Sporadic E</option>
<option value="FAI">Field Aligned Irregularities</option>
<option value="F2">F2 Reflection</option>
<option value="INTERNET">Internet-assisted</option>
<option value="ION">Ionoscatter</option>
<option value="IRL">IRLP</option>
<option value="MS">Meteor scatter</option>
<option value="RPT">Terrestrial or atmospheric repeater or transponder</option>
<option value="RS">Rain scatter</option>
<option value="SAT">Satellite</option>
<option value="TEP">Trans-equatorial</option>
<option value="TR">Tropospheric ducting</option>
</select>
</div>
<div class="form-group col">
<label for="qslSent">QSL Sent</label>
<select id="qslSent" name="qslSent" class="form-control form-control-sm">
<option value="">All</option>
<option value="Y">Yes</option>
<option value="N">No</option>
<option value="R">Requested</option>
<option value="Q">Queued</option>
<option value="I">Ignore/Invalid</option>
</select>
</div>
<div class="form-group col">
<label for="qslReceived">QSL Received</label>
<select id="qslReceived" name="qslReceived" class="form-control form-control-sm">
<option value="">All</option>
<option value="Y">Yes</option>
<option value="N">No</option>
<option value="R">Requested</option>
<option value="I">Ignore/Invalid</option>
<option value="V">Verified</option>
</select>
</div>
<div class="form-group col">
<label>&nbsp;</label><br>
<button type="submit" class="btn btn-sm btn-primary" id="searchButton">Search</button>
<button type="reset" class="btn btn-sm btn-danger" id="resetButton">Reset</button>
</div>
</div>
</form>
<div class="mb-2">
<span class="h6">With selected :</span>
<button type="button" class="btn btn-sm btn-primary" id="btnUpdateFromCallbook">Update from Callbook</button>
<button type="button" class="btn btn-sm btn-primary" id="queueBureau">Queue Bureau</button>
<button type="button" class="btn btn-sm btn-primary" id="queueDirect">Queue Direct</button>
<button type="button" class="btn btn-sm btn-success" id="sentBureau">Sent Bureau</button>
<button type="button" class="btn btn-sm btn-success" id="sentDirect">Sent Direct</button>
<button type="button" class="btn btn-sm btn-warning" id="dontSend">Don't Send</button>
<button type="button" class="btn btn-sm btn-info" id="exportAdif">Create ADIF</button>
<button type="button" class="btn btn-sm btn-danger" id="deleteQsos">Delete</button>
<span id="infoBox"></span>
</div>
<table style="width:100%" class="table-sm table table-bordered table-hover table-condensed text-center" id="qsoList">
<thead>
<tr>
<th><div class="form-check" style="margin-top: -1.5em"><input class="form-check-input" type="checkbox" id="checkBoxAll" /></div></th>
<th>Date/Time</th>
<th>De</th>
<th>Dx</th>
<th>Mode</th>
<th>RST (S)</th>
<th>RST (R)</th>
<th>Band</th>
<th>My Refs</th>
<th>Refs</th>
<th>Name</th>
<th>QSL Via</th>
<th>QSL Sent</th>
<th>QSL Received</th>
<th>QSL Msg</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>

查看文件

@ -0,0 +1,345 @@
var callBookProcessingDialog = null;
var inCallbookProcessing = false;
var inCallbookItemProcessing = false;
function updateRow(qso) {
let row = $('#qsoID-' + qso.qsoID);
let cells = row.find('td');
let c = 1;
cells.eq(c++).text(qso.qsoDateTime);
cells.eq(c++).text(qso.de);
cells.eq(c++).text(qso.dx);
cells.eq(c++).text(qso.mode);
cells.eq(c++).text(qso.rstS);
cells.eq(c++).text(qso.rstR);
cells.eq(c++).text(qso.band);
cells.eq(c++).text(qso.deRefs);
cells.eq(c++).text(qso.dxRefs);
cells.eq(c++).text(qso.name);
cells.eq(c++).text(qso.qslVia);
cells.eq(c++).text(qso.qslSent);
cells.eq(c++).text(qso.qslReceived);
cells.eq(c++).text(qso.qslMessage);
return row;
}
function loadQSOTable(rows) {
var uninitialized = $('#qsoList').filter(function() {
return !$.fn.DataTable.fnIsDataTable(this);
});
uninitialized.each(function() {
$(this).DataTable({
searching: false,
responsive: false,
ordering: true,
"scrollY": window.innerHeight - 280,
"scrollCollapse": true,
"paging": false,
"scrollX": true,
"order": [ 0, 'asc' ],
});
});
var table = $('#qsoList').DataTable();
table.clear();
for (i = 0; i < rows.length; i++) {
let qso = rows[i];
var data = [
'<div class="form-check"><input class="form-check-input" type="checkbox" /></div>',
qso.qsoDateTime,
qso.de,
qso.dx,
qso.mode,
qso.rstS,
qso.rstR,
qso.band,
qso.deRefs,
qso.dxRefs,
qso.name,
qso.qslVia,
qso.qslSent,
qso.qslReceived,
qso.qslMessage,
];
let createdRow = table.row.add(data).index();
table.rows(createdRow).nodes().to$().data('qsoID', qso.qsoID);
table.row(createdRow).node().id = 'qsoID-' + qso.qsoID;
}
table.draw();
}
function processNextCallbookItem() {
if (!inCallbookProcessing) return;
var elements = $('#qsoList tbody input:checked');
var nElements = elements.length;
if (nElements == 0) {
inCallbookProcessing = false;
callBookProcessingDialog.close();
return;
}
callBookProcessingDialog.setMessage("Retrieving callbook data : " + nElements + " remaining");
unselectQsoID(elements.first().closest('tr').data('qsoID'));
$.ajax({
url: site_url + '/logbookadvanced/updateFromCallbook',
type: 'post',
data: {
qsoID: elements.first().closest('tr').data('qsoID')
},
dataType: 'json',
success: function (data) {
if (data !== []) {
updateRow(data);
}
setTimeout("processNextCallbookItem()", 50);
},
error: function (data) {
setTimeout("processNextCallbookItem()", 50);
},
});
}
function selectQsoID(qsoID) {
var element = $("#qsoID-" + qsoID);
element.find("input[type=checkbox]").prop("checked", true);
element.addClass('alert-success')
}
function unselectQsoID(qsoID) {
var element = $("#qsoID-" + qsoID);
element.find("input[type=checkbox]").prop("checked", false);
element.removeClass('alert-success')
}
$(document).ready(function () {
$('#dateFrom').datetimepicker({
format: 'DD/MM/YYYY',
});
$('#dateTo').datetimepicker({
format: 'DD/MM/YYYY',
});
$('#searchForm').submit(function (e) {
$('#searchButton').prop("disabled", true);
$.ajax({
url: this.action,
type: 'post',
data: {
dateFrom: this.dateFrom.value,
dateTo: this.dateTo.value,
de: this.de.value,
dx: this.dx.value,
mode: this.mode.value,
band: this.band.value,
qslSent: this.qslSent.value,
qslReceived: this.qslReceived.value,
iota: this.iota.value,
dxcc: this.dxcc.value,
propmode: this.selectPropagation.value,
gridsquare: this.gridsquare.value,
state: this.state.value,
},
dataType: 'json',
success: function (data) {
$('#searchButton').prop("disabled", false);
loadQSOTable(data);
},
error: function (data) {
$('#searchButton').prop("disabled", false);
BootstrapDialog.alert({
title: 'ERROR',
message: 'An error ocurred while making the request',
type: BootstrapDialog.TYPE_DANGER,
closable: false,
draggable: false,
callback: function (result) {
}
});
},
});
return false;
});
$('#qsoList').on('click', 'input[type="checkbox"]', function() {
if ($(this).is(":checked")) {
$(this).closest('tr').addClass('alert-success');
} else {
$(this).closest('tr').removeClass('alert-success');
}
});
$('#btnUpdateFromCallbook').click(function (event) {
var elements = $('#qsoList tbody input:checked');
var nElements = elements.length;
if (nElements == 0) {
return;
}
inCallbookProcessing = true;
callBookProcessingDialog = BootstrapDialog.show({
title: "Retrieving callbook data for " + nElements + " QSOs",
message: "Retrieving callbook data for " + nElements + " QSOs",
type: BootstrapDialog.TYPE_DANGER,
closable: false,
draggable: false,
buttons: [{
label: 'Cancel',
action: function(dialog) {
inCallbookProcessing = false;
dialog.close();
}
}]
});
processNextCallbookItem();
});
$('#deleteQsos').click(function (event) {
var elements = $('#qsoList tbody input:checked');
var nElements = elements.length;
if (nElements == 0) {
return;
}
$('#deleteQsos').prop("disabled", true);
var table = $('#qsoList').DataTable();
BootstrapDialog.confirm({
title: 'DANGER',
message: 'Warning! Are you sure you want to delete the marked QSO(s)?' ,
type: BootstrapDialog.TYPE_DANGER,
closable: true,
draggable: true,
btnOKClass: 'btn-danger',
callback: function(result) {
if(result) {
elements.each(function() {
let id = $(this).first().closest('tr').data('qsoID')
$.ajax({
url: base_url + 'index.php/qso/delete_ajax',
type: 'post',
data: {'id': id
},
success: function(data) {
var row = $("#qsoID-" + id);
table.row(row).remove().draw(false);
}
});
$('#deleteQsos').prop("disabled", false);
})
}
}
});
});
$('#exportAdif').click(function (event) {
$('#exportAdif').prop("disabled", true);
var elements = $('#qsoList tbody input:checked');
var nElements = elements.length;
if (nElements == 0) {
return;
}
var id_list=[];
elements.each(function() {
let id = $(this).first().closest('tr').data('qsoID')
id_list.push(id);
unselectQsoID(id);
});
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
var a;
if (xhttp.readyState === 4 && xhttp.status === 200) {
// Trick for making downloadable link
a = document.createElement('a');
a.href = window.URL.createObjectURL(xhttp.response);
// Give filename you wish to download
a.download = "logbook_export.adi";
a.style.display = 'none';
document.body.appendChild(a);
a.click();
}
};
// Post data to URL which handles post request
xhttp.open("POST", site_url+'/logbookadvanced/export_to_adif', true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send("id=" + JSON.stringify(id_list, null, 2));
$('#exportAdif').prop("disabled", false);
});
$('#queueBureau').click(function (event) {
handleQsl('Q','B', 'queueBureau');
});
$('#queueDirect').click(function (event) {
handleQsl('Q','D', 'queueDirect');
});
$('#sentBureau').click(function (event) {
handleQsl('Y','B', 'sentBureau');
});
$('#sentDirect').click(function (event) {
handleQsl('Y','D', 'sentDirect');
});
$('#dontSend').click(function (event) {
handleQsl('N','', 'dontSend');
});
function handleQsl(sent, method, tag) {
var elements = $('#qsoList tbody input:checked');
var nElements = elements.length;
if (nElements == 0) {
return;
}
$('#'+tag).prop("disabled", true);
var id_list=[];
elements.each(function() {
let id = $(this).first().closest('tr').data('qsoID')
id_list.push(id);
});
$.ajax({
url: base_url + 'index.php/logbookadvanced/update_qsl',
type: 'post',
data: {'id': JSON.stringify(id_list, null, 2),
'sent' : sent,
'method' : method
},
success: function(data) {
if (data !== []) {
$.each(data, function(k, v) {
updateRow(this);
unselectQsoID(this.qsoID);
});
}
$('#'+tag).prop("disabled", false);
}
});
}
$('#checkBoxAll').change(function (event) {
if (this.checked) {
$('#qsoList tbody tr').each(function (i) {
selectQsoID($(this).data('qsoID'))
});
}else{
$('#qsoList tbody tr').each(function (i) {
unselectQsoID($(this).data('qsoID'))
});
}
});
$('#searchForm').submit();
});

652
src/QSLManager/QSO.php 普通文件
查看文件

@ -0,0 +1,652 @@
<?php
declare(strict_types=1);
namespace Cloudlog\QSLManager;
use DateTime;
use DateTimeZone;
use DomainException;
class QSO
{
private string $qsoID;
private DateTime $qsoDateTime;
private string $de;
private string $dx;
private string $mode;
private string $submode;
private string $band;
private string $bandRX;
private string $rstR;
private string $rstS;
private string $propagationMode;
private string $satelliteMode;
private string $satelliteName;
private string $name;
private string $email;
private string $address;
private string $deGridsquare;
private string $deIOTA;
private string $deSig;
private string $deSigInfo;
private string $deIOTAIslandID;
private string $deSOTAReference;
/** @var string[] */
private array $deVUCCGridsquares;
private string $dxGridsquare;
private string $dxIOTA;
private string $dxSig;
private string $dxSigInfo;
private string $dxDARCDOK;
private string $dxSOTAReference;
/** @var string[] */
private array $dxVUCCGridsquares;
private string $QSLMsg;
private ?DateTime $QSLReceivedDate;
private string $QSLReceived;
private string $QSLReceivedVia;
private ?DateTime $QSLSentDate;
private string $QSLSent;
private string $QSLSentVia;
private string $QSLVia;
/**
* @param array $data Does no validation, it's assumed to be a row from the database in array format
*/
public function __construct(array $data)
{
$requiredKeys = [
'COL_PRIMARY_KEY',
'COL_ADDRESS',
'COL_BAND',
'COL_BAND_RX',
'COL_CALL',
'COL_EMAIL',
'COL_GRIDSQUARE',
'COL_IOTA',
'COL_MODE',
'COL_MY_GRIDSQUARE',
'COL_MY_IOTA',
'COL_MY_SIG',
'COL_MY_SIG_INFO',
'COL_NAME',
'COL_PROP_MODE',
'COL_QSLMSG',
'COL_QSLRDATE',
'COL_QSLSDATE',
'COL_QSL_RCVD',
'COL_QSL_RCVD_VIA',
'COL_QSL_SENT',
'COL_QSL_SENT_VIA',
'COL_QSL_VIA',
'COL_RST_RCVD',
'COL_RST_SENT',
'COL_SAT_MODE',
'COL_SAT_NAME',
'COL_SIG',
'COL_SIG_INFO',
'COL_STATION_CALLSIGN',
'COL_TIME_ON',
'COL_DARC_DOK',
'COL_MY_IOTA_ISLAND_ID',
'COL_MY_SOTA_REF',
'COL_MY_VUCC_GRIDS',
'COL_SOTA_REF',
'COL_SUBMODE',
'COL_VUCC_GRIDS'
];
foreach ($requiredKeys as $requiredKey) {
if (!array_key_exists($requiredKey, $data)) {
throw new DomainException("Required key $requiredKey does not exist");
}
}
$this->qsoID = $data['COL_PRIMARY_KEY'];
$this->qsoDateTime = DateTime::createFromFormat("Y-m-d H:i:s", $data['COL_TIME_ON'], new DateTimeZone("UTC"));
$this->de = $data['COL_STATION_CALLSIGN'];
$this->dx = $data['COL_CALL'];
$this->mode = $data['COL_MODE'] ?? '';
$this->submode = $data['COL_SUBMODE'] ?? '';
$this->band = $data['COL_BAND'];
$this->bandRX = $data['COL_BAND_RX'] ?? '';
$this->rstR = $data['COL_RST_RCVD'];
$this->rstS = $data['COL_RST_SENT'];
$this->propagationMode = $data['COL_PROP_MODE'] ?? '';
$this->satelliteMode = $data['COL_SAT_MODE'] ?? '';
$this->satelliteName = $data['COL_SAT_NAME'] ?? '';
$this->name = $data['COL_NAME'] ?? '';
$this->email = $data['COL_EMAIL'] ?? '';
$this->address = $data['COL_ADDRESS'] ?? '';
$this->deGridsquare = $data['COL_MY_GRIDSQUARE'] ?? '';
$this->deIOTA = $data['COL_MY_IOTA'] ?? '';
$this->deSig = $data['COL_MY_SIG'] ?? '';
$this->deSigInfo = $data['COL_MY_SIG_INFO'] ?? '';
$this->deIOTAIslandID = $data['COL_MY_IOTA_ISLAND_ID'] ?? '';
$this->deSOTAReference = $data['COL_MY_SOTA_REF'] ?? '';
$this->deVUCCGridsquares = ($data['COL_MY_VUCC_GRIDS'] === null) ? [] : explode(",", $data['COL_MY_VUCC_GRIDS'] ?? '');
$this->dxGridsquare = $data['COL_GRIDSQUARE'] ?? '';
$this->dxIOTA = $data['COL_IOTA'] ?? '';
$this->dxSig = $data['COL_SIG'] ?? '';
$this->dxSigInfo = $data['COL_SIG_INFO'] ?? '';
$this->dxDARCDOK = $data['COL_DARC_DOK'] ?? '';
$this->dxSOTAReference = $data['COL_SOTA_REF'] ?? '';
$this->dxVUCCGridsquares = ($data['COL_VUCC_GRIDS'] === null) ? [] : explode(",", $data['COL_VUCC_GRIDS'] ?? '');
$this->QSLMsg = $data['COL_QSLMSG'] ?? '';
$this->QSLReceivedDate = ($data['COL_QSLRDATE'] === null) ? null : DateTime::createFromFormat("Y-m-d H:i:s", $data['COL_QSLRDATE'], new DateTimeZone('UTC'));
$this->QSLReceived = ($data['COL_QSL_RCVD'] === null) ? '' : $data['COL_QSL_RCVD'];
$this->QSLReceivedVia = ($data['COL_QSL_RCVD_VIA'] === null) ? '' : $data['COL_QSL_RCVD_VIA'];
$this->QSLSentDate = ($data['COL_QSLSDATE'] === null) ? null : DateTime::createFromFormat("Y-m-d H:i:s", $data['COL_QSLSDATE'], new DateTimeZone('UTC'));
$this->QSLSent = ($data['COL_QSL_SENT'] === null) ? '' : $data['COL_QSL_SENT'];
$this->QSLSentVia = ($data['COL_QSL_SENT_VIA'] === null) ? '' : $data['COL_QSL_SENT_VIA'];
$this->QSLVia = ($data['COL_QSL_VIA'] === null) ? '' : $data['COL_QSL_VIA'];
}
/**
* @return string
*/
public function getQsoID(): string
{
return $this->qsoID;
}
/**
* @return DateTime
*/
public function getQsoDateTime(): DateTime
{
return $this->qsoDateTime;
}
/**
* @return string
*/
public function getDe(): string
{
return $this->de;
}
/**
* @return string
*/
public function getDx(): string
{
return $this->dx;
}
/**
* @return string
*/
public function getMode(): string
{
return $this->mode;
}
/**
* @return string
*/
public function getSubmode(): string
{
return $this->submode;
}
/**
* @return string
*/
public function getBand(): string
{
return $this->band;
}
/**
* @return string
*/
public function getBandRX(): string
{
return $this->bandRX;
}
/**
* @return string
*/
public function getRstR(): string
{
return $this->rstR;
}
/**
* @return string
*/
public function getRstS(): string
{
return $this->rstS;
}
/**
* @return string
*/
public function getPropagationMode(): string
{
return $this->propagationMode;
}
/**
* @return string
*/
public function getSatelliteMode(): string
{
return $this->satelliteMode;
}
/**
* @return string
*/
public function getSatelliteName(): string
{
return $this->satelliteName;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return string
*/
public function getEmail(): string
{
return $this->email;
}
/**
* @return string
*/
public function getAddress(): string
{
return $this->address;
}
/**
* @return string
*/
public function getDeGridsquare(): string
{
return $this->deGridsquare;
}
/**
* @return string
*/
public function getDeIOTA(): string
{
return $this->deIOTA;
}
/**
* @return string
*/
public function getDeSig(): string
{
return $this->deSig;
}
/**
* @return string
*/
public function getDeSigInfo(): string
{
return $this->deSigInfo;
}
/**
* @return string
*/
public function getDeIOTAIslandID(): string
{
return $this->deIOTAIslandID;
}
/**
* @return string
*/
public function getDeSOTAReference(): string
{
return $this->deSOTAReference;
}
/**
* @return string[]
*/
public function getDeVUCCGridsquares(): array
{
return $this->deVUCCGridsquares;
}
/**
* @return string
*/
public function getDxGridsquare(): string
{
return $this->dxGridsquare;
}
/**
* @return string
*/
public function getDxIOTA(): string
{
return $this->dxIOTA;
}
/**
* @return string
*/
public function getDxSig(): string
{
return $this->dxSig;
}
/**
* @return string
*/
public function getDxSigInfo(): string
{
return $this->dxSigInfo;
}
/**
* @return string
*/
public function getDxDARCDOK(): string
{
return $this->dxDARCDOK;
}
/**
* @return string
*/
public function getDxSOTAReference(): string
{
return $this->dxSOTAReference;
}
/**
* @return string[]
*/
public function getDxVUCCGridsquares(): array
{
return $this->dxVUCCGridsquares;
}
/**
* @return string
*/
public function getQSLMsg(): string
{
return $this->QSLMsg;
}
/**
* @return ?DateTime
*/
public function getQSLReceivedDate(): ?DateTime
{
return $this->QSLReceivedDate;
}
/**
* @return string
*/
public function getQSLReceived(): string
{
return $this->QSLReceived;
}
/**
* @return string
*/
public function getQSLReceivedVia(): string
{
return $this->QSLReceivedVia;
}
/**
* @return ?DateTime
*/
public function getQSLSentDate(): ?DateTime
{
return $this->QSLSentDate;
}
/**
* @return string
*/
public function getQSLSent(): string
{
return $this->QSLSent;
}
/**
* @return string
*/
public function getQSLSentVia(): string
{
return $this->QSLSentVia;
}
/**
* @return string
*/
public function getQSLVia(): string
{
return $this->QSLVia;
}
public function toArray(): array
{
return [
'qsoID' => $this->qsoID,
'qsoDateTime' => $this->qsoDateTime->format("Y-m-d H:i"),
'de' => $this->de,
'dx' => $this->dx,
'mode' => $this->getFormattedMode(),
'rstS' => $this->rstS,
'rstR' => $this->rstR,
'band' => $this->getFormattedBand(),
'deRefs' => $this->getFormattedDeRefs(),
'dxRefs' => $this->getFormattedDxRefs(),
'qslVia' => $this->QSLVia,
'qslSent' => $this->getFormattedQSLSent(),
'qslReceived' => $this->getFormattedQSLReceived(),
'qslMessage' => $this->getQSLMsg(),
'name' => $this->getName(),
];
}
private function getFormattedMode(): string
{
if ($this->submode !== '') {
return $this->submode;
} else {
return $this->mode;
}
}
private function getFormattedBand(): string
{
$label = "";
if ($this->propagationMode !== '') {
$label .= $this->propagationMode;
if ($this->satelliteName !== '') {
$label .= " " . $this->satelliteName;
if ($this->satelliteMode !== '') {
$label .= " " . $this->satelliteMode;
}
}
}
$label .= " " . $this->band;
if ($this->bandRX !== '' && $this->band !== '') {
$label .= "/" . $this->bandRX;
}
return trim($label);
}
private function getFormattedDeRefs(): string
{
$refs = [];
if ($this->deVUCCGridsquares !== []) {
$refs[] = implode(",", $this->deVUCCGridsquares);
} else {
if ($this->deGridsquare !== '') {
$refs[] = $this->deGridsquare;
}
}
if ($this->deIOTA !== '') {
if ($this->deIOTAIslandID !== '') {
$refs[] = "IOTA:" . $this->deIOTA . "(" . $this->deIOTAIslandID . ")";
} else {
$refs[] = "IOTA:" . $this->deIOTA;
}
}
if ($this->deSOTAReference !== '') {
$refs[] = "SOTA:" . $this->deSOTAReference;
}
if ($this->deSig !== '') {
$refs[] = $this->deSig . ":" . $this->deSigInfo;
}
return trim(implode(" ", $refs));
}
private function getFormattedDxRefs(): string
{
$refs = [];
if ($this->dxVUCCGridsquares !== []) {
$refs[] = implode(",", $this->dxVUCCGridsquares);
} else if ($this->dxGridsquare !== '') {
$refs[] = $this->dxGridsquare;
}
if ($this->dxIOTA !== '') {
$refs[] = "IOTA:" . $this->dxIOTA;
}
if ($this->dxSOTAReference !== '') {
$refs[] = "SOTA:" . $this->dxSOTAReference;
}
if ($this->dxSig !== '') {
$refs[] = $this->dxSig . ":" . $this->dxSigInfo;
}
if ($this->dxDARCDOK !== '') {
$refs[] = "DOK:" . $this->dxDARCDOK;
}
return implode(" ", $refs);
}
private function getFormattedQSLSent(): string
{
$showVia = false;
$label = [];
if ($this->QSLSent === "Y") {
if ($this->QSLSentDate !== null) {
$label[] = $this->QSLSentDate->format("Y-m-d");
} else {
$label[] = "Yes";
}
$showVia = true;
} else if ($this->QSLSent === "N") {
$label[] = "No";
} else if ($this->QSLSent === "Q") {
$label[] = "Queued";
if ($this->QSLSentDate !== null) {
$label[] = $this->QSLSentDate->format("Y-m-d");
}
$showVia = true;
} else if ($this->QSLSent === "R") {
$label[] = "Requested";
if ($this->QSLSentDate !== null) {
$label[] = $this->QSLSentDate->format("Y-m-d");
}
$showVia = true;
}
if ($showVia && $this->QSLSentVia !== '') {
switch ($this->QSLSentVia) {
case 'B':
$label[] = "Bureau";
break;
case 'D':
$label[] = "Direct";
break;
case 'E':
$label[] = "Electronic";
break;
case 'M':
$label[] = "Manager";
break;
}
}
return trim(implode(" ", $label));
}
private function getFormattedQSLReceived(): string
{
$showVia = false;
$label = [];
if ($this->QSLReceived === "Y") {
if ($this->QSLReceivedDate !== null) {
$label[] = $this->QSLReceivedDate->format("Y-m-d");
} else {
$label[] = "Yes";
}
$showVia = true;
} else if ($this->QSLReceived === "N") {
$label[] = "No";
} else if ($this->QSLReceived === "Q") {
$label[] = "Queued";
if ($this->QSLReceivedDate !== null) {
$label[] = $this->QSLReceivedDate->format("Y-m-d");
}
$showVia = true;
} else if ($this->QSLReceived === "R") {
$label[] = "Requested";
if ($this->QSLReceivedDate !== null) {
$label[] = $this->QSLReceivedDate->format("Y-m-d");
}
$showVia = true;
}
if ($showVia && $this->QSLReceivedVia !== '') {
switch ($this->QSLReceivedVia) {
case 'B':
$label[] = "Bureau";
break;
case 'D':
$label[] = "Direct";
break;
case 'E':
$label[] = "Electronic";
break;
case 'M':
$label[] = "Manager";
break;
}
}
return trim(implode(" ", $label));
}
}