diff --git a/Dockerfile b/Dockerfile
index cfaf7523..c3c516f8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -24,5 +24,16 @@ RUN apt-get update && apt-get install -y \
&& docker-php-ext-install xml \
&& a2enmod rewrite
+# Copy script.sh and make it executable
+COPY script.sh /usr/local/bin/startup.sh
+RUN sed -i 's/\r$//' /usr/local/bin/startup.sh && chmod +x /usr/local/bin/startup.sh
+
+# Configure PHP for larger file uploads (30MB)
+RUN echo "upload_max_filesize = 30M" >> /usr/local/etc/php/conf.d/uploads.ini \
+ && echo "post_max_size = 35M" >> /usr/local/etc/php/conf.d/uploads.ini \
+ && echo "memory_limit = 64M" >> /usr/local/etc/php/conf.d/uploads.ini \
+ && echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/uploads.ini \
+ && echo "max_input_time = 300" >> /usr/local/etc/php/conf.d/uploads.ini
+
# Expose port 80
EXPOSE 80
\ No newline at end of file
diff --git a/Dockerfile-db b/Dockerfile-db
index ac550b76..e4ce0ce8 100644
--- a/Dockerfile-db
+++ b/Dockerfile-db
@@ -4,5 +4,9 @@ FROM mariadb:latest
# Add the install.sql file to the docker image
ADD install/assets/install.sql /docker-entrypoint-initdb.d
+# Create a healthcheck script that uses mariadb-admin
+RUN echo '#!/bin/bash\nmariadb-admin ping -h "localhost" --silent' > /usr/local/bin/healthcheck.sh \
+ && chmod +x /usr/local/bin/healthcheck.sh
+
# Expose port 3306
EXPOSE 3306
\ No newline at end of file
diff --git a/application/config/migration.php b/application/config/migration.php
index 60928918..1b160613 100644
--- a/application/config/migration.php
+++ b/application/config/migration.php
@@ -22,7 +22,7 @@ $config['migration_enabled'] = TRUE;
|
*/
-$config['migration_version'] = 204;
+$config['migration_version'] = 205;
/*
|--------------------------------------------------------------------------
diff --git a/application/controllers/Adif.php b/application/controllers/Adif.php
index 64750fc5..18cdb2ac 100644
--- a/application/controllers/Adif.php
+++ b/application/controllers/Adif.php
@@ -13,16 +13,6 @@ class adif extends CI_Controller {
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
}
- public function test() {
- if(validateADIFDate('20120228') == true){
- echo "valid date";
- } else {
- echo "date incorrect";
- }
-
-
- }
-
/* Shows Export Views */
public function export() {
diff --git a/application/controllers/Dashboard.php b/application/controllers/Dashboard.php
index 4cdc91d2..f133c081 100644
--- a/application/controllers/Dashboard.php
+++ b/application/controllers/Dashboard.php
@@ -3,6 +3,16 @@
class Dashboard extends CI_Controller
{
+ public function __construct()
+ {
+ parent::__construct();
+
+ // Load common models that are used across multiple methods
+ $this->load->model('user_model');
+ $this->load->model('logbook_model');
+ $this->load->model('logbooks_model');
+ }
+
public function index()
{
// If environment is set to development then show the debug toolbar
@@ -13,10 +23,6 @@ class Dashboard extends CI_Controller
// Load language files
$this->lang->load('lotw');
- // Database connections
- $this->load->model('logbook_model');
- $this->load->model('user_model');
-
// LoTW infos
$this->load->model('LotwCert');
@@ -31,7 +37,6 @@ class Dashboard extends CI_Controller
redirect('user/login');
}
- $this->load->model('logbooks_model');
$logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
/*
@@ -63,9 +68,11 @@ class Dashboard extends CI_Controller
$this->load->model('stations');
$this->load->model('setup_model');
- $data['countryCount'] = $this->setup_model->getCountryCount();
- $data['logbookCount'] = $this->setup_model->getLogbookCount();
- $data['locationCount'] = $this->setup_model->getLocationCount();
+ // Use consolidated setup counts instead of 3 separate queries
+ $setup_counts = $this->setup_model->getAllSetupCounts();
+ $data['countryCount'] = $setup_counts['country_count'];
+ $data['logbookCount'] = $setup_counts['logbook_count'];
+ $data['locationCount'] = $setup_counts['location_count'];
$data['current_active'] = $this->stations->find_active();
@@ -85,19 +92,21 @@ class Dashboard extends CI_Controller
$data['radio_status'] = $this->cat->recent_status();
- // Store info
- $data['todays_qsos'] = $this->logbook_model->todays_qsos($logbooks_locations_array);
- $data['total_qsos'] = $this->logbook_model->total_qsos($logbooks_locations_array);
- $data['month_qsos'] = $this->logbook_model->month_qsos($logbooks_locations_array);
- $data['year_qsos'] = $this->logbook_model->year_qsos($logbooks_locations_array);
+ // Store info - Use consolidated query for QSO statistics
+ $qso_stats = $this->logbook_model->get_qso_statistics_consolidated($logbooks_locations_array);
+ $data['todays_qsos'] = $qso_stats['todays_qsos'];
+ $data['total_qsos'] = $qso_stats['total_qsos'];
+ $data['month_qsos'] = $qso_stats['month_qsos'];
+ $data['year_qsos'] = $qso_stats['year_qsos'];
- // Load Countries Breakdown data into array
- $CountriesBreakdown = $this->logbook_model->total_countries_confirmed($logbooks_locations_array);
-
- $data['total_countries'] = $CountriesBreakdown['Countries_Worked'];
- $data['total_countries_confirmed_paper'] = $CountriesBreakdown['Countries_Worked_QSL'];
- $data['total_countries_confirmed_eqsl'] = $CountriesBreakdown['Countries_Worked_EQSL'];
- $data['total_countries_confirmed_lotw'] = $CountriesBreakdown['Countries_Worked_LOTW'];
+ // Use consolidated countries statistics instead of separate queries
+ $countries_stats = $this->logbook_model->get_countries_statistics_consolidated($logbooks_locations_array);
+
+ $data['total_countries'] = $countries_stats['Countries_Worked'];
+ $data['total_countries_confirmed_paper'] = $countries_stats['Countries_Worked_QSL'];
+ $data['total_countries_confirmed_eqsl'] = $countries_stats['Countries_Worked_EQSL'];
+ $data['total_countries_confirmed_lotw'] = $countries_stats['Countries_Worked_LOTW'];
+ $current_countries = $countries_stats['Countries_Current'];
$data['dashboard_upcoming_dx_card'] = false;
$data['dashboard_qslcard_card'] = false;
@@ -107,53 +116,23 @@ class Dashboard extends CI_Controller
$dashboard_options = $this->user_options_model->get_options('dashboard')->result();
+ // Optimize options processing - convert to associative array for O(1) lookup
+ $options_map = array();
foreach ($dashboard_options as $item) {
- $option_name = $item->option_name;
- $option_key = $item->option_key;
- $option_value = $item->option_value;
-
- if ($option_name == 'dashboard_upcoming_dx_card' && $option_key == 'enabled') {
- if($option_value == 'true') {
- $data['dashboard_upcoming_dx_card'] = true;
- } else {
- $data['dashboard_upcoming_dx_card'] = false;
- }
- }
+ $options_map[$item->option_name][$item->option_key] = $item->option_value;
+ }
- if ($option_name == 'dashboard_qslcards_card' && $option_key == 'enabled') {
- if($item->option_value == 'true') {
- $data['dashboard_qslcard_card'] = true;
- } else {
- $data['dashboard_qslcard_card'] = false;
- }
- }
+ // Quick lookups instead of nested loops
+ $data['dashboard_upcoming_dx_card'] = isset($options_map['dashboard_upcoming_dx_card']['enabled']) && $options_map['dashboard_upcoming_dx_card']['enabled'] == 'true';
+ $data['dashboard_qslcard_card'] = isset($options_map['dashboard_qslcards_card']['enabled']) && $options_map['dashboard_qslcards_card']['enabled'] == 'true';
+ $data['dashboard_eqslcard_card'] = isset($options_map['dashboard_eqslcards_card']['enabled']) && $options_map['dashboard_eqslcards_card']['enabled'] == 'true';
+ $data['dashboard_lotw_card'] = isset($options_map['dashboard_lotw_card']['enabled']) && $options_map['dashboard_lotw_card']['enabled'] == 'true';
+ $data['dashboard_vuccgrids_card'] = isset($options_map['dashboard_vuccgrids_card']['enabled']) && $options_map['dashboard_vuccgrids_card']['enabled'] == 'true';
- if ($option_name == 'dashboard_eqslcards_card' && $option_key == 'enabled') {
- if($item->option_value == 'true') {
- $data['dashboard_eqslcard_card'] = true;
- } else {
- $data['dashboard_eqslcard_card'] = false;
- }
- }
-
- if ($option_name == 'dashboard_lotw_card' && $option_key == 'enabled') {
- if($item->option_value == 'true') {
- $data['dashboard_lotw_card'] = true;
- } else {
- $data['dashboard_lotw_card'] = false;
- }
- }
-
- if ($option_name == 'dashboard_vuccgrids_card' && $option_key == 'enabled') {
- if($item->option_value == 'true') {
- $data['dashboard_vuccgrids_card'] = true;
-
- $data['vucc'] = $this->vucc->fetchVuccSummary();
- $data['vuccSAT'] = $this->vucc->fetchVuccSummary('SAT');
- } else {
- $data['dashboard_vuccgrids_card'] = false;
- }
- }
+ // Only load VUCC data if the card is actually enabled
+ if ($data['dashboard_vuccgrids_card']) {
+ $data['vucc'] = $this->vucc->fetchVuccSummary();
+ $data['vuccSAT'] = $this->vucc->fetchVuccSummary('SAT');
}
@@ -185,12 +164,10 @@ class Dashboard extends CI_Controller
$data['page_title'] = "Dashboard";
+ // Optimize DXCC calculation - get count directly instead of loading all records
$this->load->model('dxcc');
- $dxcc = $this->dxcc->list_current();
-
- $current = $this->logbook_model->total_countries_current($logbooks_locations_array);
-
- $data['total_countries_needed'] = count($dxcc->result()) - $current;
+ $total_dxcc_count = $this->dxcc->get_total_dxcc_count();
+ $data['total_countries_needed'] = $total_dxcc_count - $current_countries;
$this->load->view('interface_assets/header', $data);
$this->load->view('dashboard/index');
@@ -199,30 +176,23 @@ class Dashboard extends CI_Controller
}
public function todays_qso_component() {
- $this->load->model('user_model');
-
if ($this->user_model->validate_session() == 0) {
// User is not logged in
- } else {
- $this->load->model('logbook_model');
- $this->load->model('logbooks_model');
+ return;
}
$logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
- $data['todays_qsos'] = $this->logbook_model->todays_qsos($logbooks_locations_array);
+ // Use consolidated query instead of individual todays_qsos call
+ $qso_stats = $this->logbook_model->get_qso_statistics_consolidated($logbooks_locations_array);
+ $data['todays_qsos'] = $qso_stats['todays_qsos'];
$this->load->view('components/dashboard_todays_qsos', $data);
-
}
public function logbook_display_component() {
- $this->load->model('user_model');
-
if ($this->user_model->validate_session() == 0) {
// User is not logged in
- } else {
- $this->load->model('logbook_model');
- $this->load->model('logbooks_model');
+ return;
}
// Get Logbook Locations
diff --git a/application/controllers/Update.php b/application/controllers/Update.php
index deb74779..bd8ba7cc 100644
--- a/application/controllers/Update.php
+++ b/application/controllers/Update.php
@@ -219,6 +219,16 @@ class Update extends CI_Controller {
$this->update_status("DONE");
}
+ public function get_status() {
+ $status_file = $this->make_update_path("status.html");
+ if (file_exists($status_file)) {
+ $content = file_get_contents($status_file);
+ echo $content;
+ } else {
+ echo "No status available";
+ }
+ }
+
public function update_status($done=""){
if ($done != "Downloading file"){
@@ -234,7 +244,16 @@ class Update extends CI_Controller {
$html = $done."....
";
}
- file_put_contents($this->make_update_path("status.html"), $html);
+ $status_file = $this->make_update_path("status.html");
+ if (file_put_contents($status_file, $html) === FALSE) {
+ log_message('error', 'Failed to write status file: ' . $status_file);
+ // Try to create the directory if it doesn't exist
+ $dir = dirname($status_file);
+ if (!is_dir($dir)) {
+ mkdir($dir, 0755, true);
+ file_put_contents($status_file, $html);
+ }
+ }
}
diff --git a/application/controllers/Workabledxcc.php b/application/controllers/Workabledxcc.php
index e28ed67b..fda5adb8 100644
--- a/application/controllers/Workabledxcc.php
+++ b/application/controllers/Workabledxcc.php
@@ -29,70 +29,81 @@ class Workabledxcc extends CI_Controller
public function dxcclist()
{
-
$json = file_get_contents($this->optionslib->get_option('dxped_url'));
-
- // Decode the JSON data into a PHP array
$dataResult = json_decode($json, true);
- // Initialize an empty array to store the required data
- $requiredData = array();
+ if (empty($dataResult)) {
+ $data['dxcclist'] = array();
+ $this->load->view('/workabledxcc/components/dxcclist', $data);
+ return;
+ }
// Get Date format
if ($this->session->userdata('user_date_format')) {
- // If Logged in and session exists
$custom_date_format = $this->session->userdata('user_date_format');
} else {
- // Get Default date format from /config/cloudlog.php
$custom_date_format = $this->config->item('qso_date_format');
}
- // Iterate through the decoded JSON data
- foreach ($dataResult as $item) {
- // Create a new array with the required fields and add it to the main array
- $oldStartDate = DateTime::createFromFormat('Y-m-d', $item['0']);
+ // Load models once
+ $this->load->model('logbook_model');
+ $this->load->model('Workabledxcc_model');
+ // Get all DXCC entities for all callsigns in one batch
+ $callsigns = array_column($dataResult, 'callsign');
+ $dates = array_column($dataResult, '0');
+ $dxccEntities = $this->Workabledxcc_model->batchDxccLookup($callsigns, $dates);
+
+ // Get worked/confirmed status for all entities in batch
+ $uniqueEntities = array_unique(array_filter($dxccEntities));
+ $dxccStatus = $this->Workabledxcc_model->batchDxccWorkedStatus($uniqueEntities);
+
+ // If JSON contains iota fields, batch process IOTA status
+ $iotas = [];
+ foreach ($dataResult as $item) {
+ if (!empty($item['iota'])) {
+ $iotas[] = $item['iota'];
+ }
+ }
+ $uniqueIotas = array_unique($iotas);
+ $iotaStatus = [];
+ if (!empty($uniqueIotas)) {
+ $iotaStatus = $this->Workabledxcc_model->batchIotaWorkedStatus($uniqueIotas);
+ }
+
+ // Process results
+ $requiredData = array();
+ foreach ($dataResult as $index => $item) {
+ $oldStartDate = DateTime::createFromFormat('Y-m-d', $item['0']);
$StartDate = $oldStartDate->format($custom_date_format);
$oldEndDate = DateTime::createFromFormat('Y-m-d', $item['1']);
-
$EndDate = $oldEndDate->format($custom_date_format);
- $oldStartDate1 = DateTime::createFromFormat('Y-m-d', $item['0']);
-
- $StartDate1 = $oldStartDate1->format('Y-m-d');
-
-
- $this->load->model('logbook_model');
- $dxccInfo = $this->logbook_model->dxcc_lookup($item['callsign'], $StartDate1);
-
- // Call DXCC Worked function to check if the DXCC has been worked before
- if (isset($dxccInfo['entity'])) {
- $dxccWorked = $this->dxccWorked($dxccInfo['entity']);
- } else {
- // Handle the case where 'entity' is not set in $dxccInfo
- $dxccWorked = array(
- 'workedBefore' => false,
- 'confirmed' => false,
- );
- }
+ // Get DXCC status for this callsign
+ $entity = $dxccEntities[$index] ?? null;
+ $worked = $entity && isset($dxccStatus[$entity]) ? $dxccStatus[$entity] : [
+ 'workedBefore' => false,
+ 'confirmed' => false,
+ 'workedViaSatellite' => false
+ ];
$requiredData[] = array(
'clean_date' => $item['0'],
'start_date' => $StartDate,
'end_date' => $EndDate,
'country' => $item['2'],
+ 'iota' => isset($item['iota']) ? $item['iota'] : null,
+ 'iota_status' => (isset($item['iota']) && isset($iotaStatus[$item['iota']])) ? $iotaStatus[$item['iota']] : null,
'notes' => $item['6'],
'callsign' => $item['callsign'],
- 'workedBefore' => $dxccWorked['workedBefore'],
- 'confirmed' => $dxccWorked['confirmed'],
+ 'workedBefore' => $worked['workedBefore'],
+ 'confirmed' => $worked['confirmed'],
+ 'workedViaSatellite' => $worked['workedViaSatellite'],
);
}
$data['dxcclist'] = $requiredData;
-
- // Return the array with the required data
-
$this->load->view('/workabledxcc/components/dxcclist', $data);
}
@@ -102,6 +113,7 @@ class Workabledxcc extends CI_Controller
$return = [
"workedBefore" => false,
"confirmed" => false,
+ "workedViaSatellite" => false,
];
$user_default_confirmation = $this->session->userdata('user_default_confirmation');
@@ -110,16 +122,28 @@ class Workabledxcc extends CI_Controller
$this->load->model('logbook_model');
if (!empty($logbooks_locations_array)) {
+ // Check terrestrial contacts
$this->db->where('COL_PROP_MODE !=', 'SAT');
$this->db->where_in('station_id', $logbooks_locations_array);
- $this->db->where('COL_COUNTRY', urldecode($country));
+ // Fix case sensitivity issue for DXCC country matching
+ $this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
$query = $this->db->get($this->config->item('table_name'), 1, 0);
foreach ($query->result() as $workedBeforeRow) {
$return['workedBefore'] = true;
}
+ // Check satellite contacts
+ $this->db->where('COL_PROP_MODE', 'SAT');
+ $this->db->where_in('station_id', $logbooks_locations_array);
+ $this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
+
+ $query = $this->db->get($this->config->item('table_name'), 1, 0);
+ foreach ($query->result() as $satelliteRow) {
+ $return['workedViaSatellite'] = true;
+ }
+
$extrawhere = '';
if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Q') !== false) {
$extrawhere = "COL_QSL_RCVD='Y'";
@@ -155,7 +179,8 @@ class Workabledxcc extends CI_Controller
$this->db->where_in('station_id', $logbooks_locations_array);
- $this->db->where('COL_COUNTRY', urldecode($country));
+ // Fix case sensitivity issue for DXCC country matching
+ $this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
$query = $this->db->get($this->config->item('table_name'), 1, 0);
foreach ($query->result() as $workedBeforeRow) {
diff --git a/application/migrations/139_modify_eQSL_url.php b/application/migrations/139_modify_eQSL_url.php
index 09e4fc37..e3f04afc 100644
--- a/application/migrations/139_modify_eQSL_url.php
+++ b/application/migrations/139_modify_eQSL_url.php
@@ -1,4 +1,4 @@
-db->db_debug = false;
+
+ // Check if index already exists
+ $index_exists = $this->db->query("SHOW INDEX FROM ".$this->config->item('table_name')." WHERE Key_name = 'idx_workable_dxcc'")->num_rows();
+
+ if ($index_exists == 0) {
+ $sql = "ALTER TABLE ".$this->config->item('table_name')." ADD INDEX `idx_workable_dxcc` (`COL_COUNTRY`, `station_id`, `COL_PROP_MODE`)";
+ $this->db->query($sql);
+ }
+
+ // Add index for confirmation status columns
+ $conf_index_exists = $this->db->query("SHOW INDEX FROM ".$this->config->item('table_name')." WHERE Key_name = 'idx_qsl_confirmations'")->num_rows();
+
+ if ($conf_index_exists == 0) {
+ $sql = "ALTER TABLE ".$this->config->item('table_name')." ADD INDEX `idx_qsl_confirmations` (`COL_QSL_RCVD`, `COL_LOTW_QSL_RCVD`, `COL_EQSL_QSL_RCVD`, `COL_QRZCOM_QSO_DOWNLOAD_STATUS`)";
+ $this->db->query($sql);
+ }
+
+ $this->db->db_debug = true;
+ }
+
+ public function down()
+ {
+ $this->db->db_debug = false;
+
+ // Drop the indexes if they exist
+ $index_exists = $this->db->query("SHOW INDEX FROM ".$this->config->item('table_name')." WHERE Key_name = 'idx_workable_dxcc'")->num_rows();
+ if ($index_exists > 0) {
+ $this->db->query("ALTER TABLE ".$this->config->item('table_name')." DROP INDEX `idx_workable_dxcc`");
+ }
+
+ $conf_index_exists = $this->db->query("SHOW INDEX FROM ".$this->config->item('table_name')." WHERE Key_name = 'idx_qsl_confirmations'")->num_rows();
+ if ($conf_index_exists > 0) {
+ $this->db->query("ALTER TABLE ".$this->config->item('table_name')." DROP INDEX `idx_qsl_confirmations`");
+ }
+
+ $this->db->db_debug = true;
+ }
+}
diff --git a/application/models/Dxcc.php b/application/models/Dxcc.php
index b02e940d..ef02fea5 100644
--- a/application/models/Dxcc.php
+++ b/application/models/Dxcc.php
@@ -58,6 +58,12 @@ class DXCC extends CI_Model {
return $this->db->get('dxcc_entities');
}
+ /* Optimized method to get count of current DXCC entities without loading all records */
+ function get_total_dxcc_count() {
+ $this->db->where('end', null);
+ return $this->db->count_all_results('dxcc_entities');
+ }
+
function get_dxcc_array($dxccArray, $bands, $postdata) {
$CI =& get_instance();
$CI->load->model('logbooks_model');
diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php
index 0b7e394d..c4d5bdb4 100755
--- a/application/models/Logbook_model.php
+++ b/application/models/Logbook_model.php
@@ -2079,7 +2079,7 @@ class Logbook_model extends CI_Model
$this->db->where_in('station_id', $logbooks_locations_array);
$this->db->group_start();
$this->db->like('SUBSTRING(COL_GRIDSQUARE, 1, 4)', substr($grid, 0, 4));
- $this->db->or_like('SUBSTRING(COL_VUCC_GRIDS, 1, 4)', substr($grid, 0, 4));
+ $this->db->or_like('COL_VUCC_GRIDS', substr($grid, 0, 4));
$this->db->group_end();
if ($band != null && $band != 'SAT') {
@@ -2621,95 +2621,99 @@ class Logbook_model extends CI_Model
}
if (!empty($logbooks_locations_array)) {
- $this->db->select('
- COUNT(IF(COL_QSL_SENT="Y",COL_QSL_SENT,null)) as QSL_Sent,
- COUNT(IF(COL_QSL_RCVD="Y",COL_QSL_RCVD,null)) as QSL_Received,
- COUNT(IF(COL_QSL_SENT IN("Q", "R") ,COL_QSL_SENT,null)) as QSL_Requested,
- COUNT(IF(COL_EQSL_QSL_SENT="Y",COL_EQSL_QSL_SENT,null)) as eQSL_Sent,
- COUNT(IF(COL_EQSL_QSL_RCVD="Y",COL_EQSL_QSL_RCVD,null)) as eQSL_Received,
- COUNT(IF(COL_LOTW_QSL_SENT="Y",COL_LOTW_QSL_SENT,null)) as LoTW_Sent,
- COUNT(IF(COL_LOTW_QSL_RCVD="Y",COL_LOTW_QSL_RCVD,null)) as LoTW_Received,
- COUNT(IF(COL_QRZCOM_QSO_UPLOAD_STATUS="Y",COL_QRZCOM_QSO_UPLOAD_STATUS,null)) as QRZ_Sent,
- COUNT(IF(COL_QRZCOM_QSO_DOWNLOAD_STATUS="Y",COL_QRZCOM_QSO_DOWNLOAD_STATUS,null)) as QRZ_Received,
- COUNT(IF(COL_QSL_SENT="Y" and DATE(COL_QSLSDATE)=DATE(SYSDATE()),COL_QSL_SENT,null)) as QSL_Sent_today,
- COUNT(IF(COL_QSL_RCVD="Y" and DATE(COL_QSLRDATE)=DATE(SYSDATE()),COL_QSL_RCVD,null)) as QSL_Received_today,
- COUNT(IF(COL_QSL_SENT IN("Q", "R") and DATE(COL_QSLSDATE)=DATE(SYSDATE()) ,COL_QSL_SENT,null)) as QSL_Requested_today,
- COUNT(IF(COL_EQSL_QSL_SENT="Y" and DATE(COL_EQSL_QSLSDATE)=DATE(SYSDATE()),COL_EQSL_QSL_SENT,null)) as eQSL_Sent_today,
- COUNT(IF(COL_EQSL_QSL_RCVD="Y" and DATE(COL_EQSL_QSLRDATE)=DATE(SYSDATE()),COL_EQSL_QSL_RCVD,null)) as eQSL_Received_today,
- COUNT(IF(COL_LOTW_QSL_SENT="Y" and DATE(COL_LOTW_QSLSDATE)=DATE(SYSDATE()),COL_LOTW_QSL_SENT,null)) as LoTW_Sent_today,
- COUNT(IF(COL_LOTW_QSL_RCVD="Y" and DATE(COL_LOTW_QSLRDATE)=DATE(SYSDATE()),COL_LOTW_QSL_RCVD,null)) as LoTW_Received_today,
- COUNT(IF(COL_QRZCOM_QSO_UPLOAD_STATUS="Y" and DATE(COL_QRZCOM_QSO_UPLOAD_DATE)=DATE(SYSDATE()),COL_QRZCOM_QSO_UPLOAD_STATUS,null)) as QRZ_Sent_today,
- COUNT(IF(COL_QRZCOM_QSO_DOWNLOAD_STATUS="Y" and DATE(COL_QRZCOM_QSO_DOWNLOAD_DATE)=DATE(SYSDATE()),COL_QRZCOM_QSO_DOWNLOAD_STATUS,null)) as QRZ_Received_today
- ');
+ // Pre-calculate today's date for better performance
+ $today_date = date('Y-m-d');
+
+ $this->db->select("
+ COUNT(IF(COL_QSL_SENT='Y',COL_QSL_SENT,null)) as QSL_Sent,
+ COUNT(IF(COL_QSL_RCVD='Y',COL_QSL_RCVD,null)) as QSL_Received,
+ COUNT(IF(COL_QSL_SENT IN('Q', 'R') ,COL_QSL_SENT,null)) as QSL_Requested,
+ COUNT(IF(COL_EQSL_QSL_SENT='Y',COL_EQSL_QSL_SENT,null)) as eQSL_Sent,
+ COUNT(IF(COL_EQSL_QSL_RCVD='Y',COL_EQSL_QSL_RCVD,null)) as eQSL_Received,
+ COUNT(IF(COL_LOTW_QSL_SENT='Y',COL_LOTW_QSL_SENT,null)) as LoTW_Sent,
+ COUNT(IF(COL_LOTW_QSL_RCVD='Y',COL_LOTW_QSL_RCVD,null)) as LoTW_Received,
+ COUNT(IF(COL_QRZCOM_QSO_UPLOAD_STATUS='Y',COL_QRZCOM_QSO_UPLOAD_STATUS,null)) as QRZ_Sent,
+ COUNT(IF(COL_QRZCOM_QSO_DOWNLOAD_STATUS='Y',COL_QRZCOM_QSO_DOWNLOAD_STATUS,null)) as QRZ_Received,
+ COUNT(IF(COL_QSL_SENT='Y' and DATE(COL_QSLSDATE)='$today_date',COL_QSL_SENT,null)) as QSL_Sent_today,
+ COUNT(IF(COL_QSL_RCVD='Y' and DATE(COL_QSLRDATE)='$today_date',COL_QSL_RCVD,null)) as QSL_Received_today,
+ COUNT(IF(COL_QSL_SENT IN('Q', 'R') and DATE(COL_QSLSDATE)='$today_date' ,COL_QSL_SENT,null)) as QSL_Requested_today,
+ COUNT(IF(COL_EQSL_QSL_SENT='Y' and DATE(COL_EQSL_QSLSDATE)='$today_date',COL_EQSL_QSL_SENT,null)) as eQSL_Sent_today,
+ COUNT(IF(COL_EQSL_QSL_RCVD='Y' and DATE(COL_EQSL_QSLRDATE)='$today_date',COL_EQSL_QSL_RCVD,null)) as eQSL_Received_today,
+ COUNT(IF(COL_LOTW_QSL_SENT='Y' and DATE(COL_LOTW_QSLSDATE)='$today_date',COL_LOTW_QSL_SENT,null)) as LoTW_Sent_today,
+ COUNT(IF(COL_LOTW_QSL_RCVD='Y' and DATE(COL_LOTW_QSLRDATE)='$today_date',COL_LOTW_QSL_RCVD,null)) as LoTW_Received_today,
+ COUNT(IF(COL_QRZCOM_QSO_UPLOAD_STATUS='Y' and DATE(COL_QRZCOM_QSO_UPLOAD_DATE)='$today_date',COL_QRZCOM_QSO_UPLOAD_STATUS,null)) as QRZ_Sent_today,
+ COUNT(IF(COL_QRZCOM_QSO_DOWNLOAD_STATUS='Y' and DATE(COL_QRZCOM_QSO_DOWNLOAD_DATE)='$today_date',COL_QRZCOM_QSO_DOWNLOAD_STATUS,null)) as QRZ_Received_today
+ ", FALSE);
$this->db->where_in('station_id', $logbooks_locations_array);
if ($query = $this->db->get($this->config->item('table_name'))) {
- $this->db->last_query();
- foreach ($query->result() as $row) {
- $QSLBreakdown['QSL_Sent'] = $row->QSL_Sent;
- $QSLBreakdown['QSL_Received'] = $row->QSL_Received;
- $QSLBreakdown['QSL_Requested'] = $row->QSL_Requested;
- $QSLBreakdown['eQSL_Sent'] = $row->eQSL_Sent;
- $QSLBreakdown['eQSL_Received'] = $row->eQSL_Received;
- $QSLBreakdown['LoTW_Sent'] = $row->LoTW_Sent;
- $QSLBreakdown['LoTW_Received'] = $row->LoTW_Received;
- $QSLBreakdown['QRZ_Sent'] = $row->QRZ_Sent;
- $QSLBreakdown['QRZ_Received'] = $row->QRZ_Received;
- $QSLBreakdown['QSL_Sent_today'] = $row->QSL_Sent_today;
- $QSLBreakdown['QSL_Received_today'] = $row->QSL_Received_today;
- $QSLBreakdown['QSL_Requested_today'] = $row->QSL_Requested_today;
- $QSLBreakdown['eQSL_Sent_today'] = $row->eQSL_Sent_today;
- $QSLBreakdown['eQSL_Received_today'] = $row->eQSL_Received_today;
- $QSLBreakdown['LoTW_Sent_today'] = $row->LoTW_Sent_today;
- $QSLBreakdown['LoTW_Received_today'] = $row->LoTW_Received_today;
- $QSLBreakdown['QRZ_Sent_today'] = $row->QRZ_Sent_today;
- $QSLBreakdown['QRZ_Received_today'] = $row->QRZ_Received_today;
+ if ($query->num_rows() > 0) {
+ $row = $query->row();
+ return array(
+ 'QSL_Sent' => (int)$row->QSL_Sent,
+ 'QSL_Received' => (int)$row->QSL_Received,
+ 'QSL_Requested' => (int)$row->QSL_Requested,
+ 'eQSL_Sent' => (int)$row->eQSL_Sent,
+ 'eQSL_Received' => (int)$row->eQSL_Received,
+ 'LoTW_Sent' => (int)$row->LoTW_Sent,
+ 'LoTW_Received' => (int)$row->LoTW_Received,
+ 'QRZ_Sent' => (int)$row->QRZ_Sent,
+ 'QRZ_Received' => (int)$row->QRZ_Received,
+ 'QSL_Sent_today' => (int)$row->QSL_Sent_today,
+ 'QSL_Received_today' => (int)$row->QSL_Received_today,
+ 'QSL_Requested_today' => (int)$row->QSL_Requested_today,
+ 'eQSL_Sent_today' => (int)$row->eQSL_Sent_today,
+ 'eQSL_Received_today' => (int)$row->eQSL_Received_today,
+ 'LoTW_Sent_today' => (int)$row->LoTW_Sent_today,
+ 'LoTW_Received_today' => (int)$row->LoTW_Received_today,
+ 'QRZ_Sent_today' => (int)$row->QRZ_Sent_today,
+ 'QRZ_Received_today' => (int)$row->QRZ_Received_today
+ );
}
-
- return $QSLBreakdown;
- } else {
- $QSLBreakdown['QSL_Sent'] = 0;
- $QSLBreakdown['QSL_Received'] = 0;
- $QSLBreakdown['QSL_Requested'] = 0;
- $QSLBreakdown['eQSL_Sent'] = 0;
- $QSLBreakdown['eQSL_Received'] = 0;
- $QSLBreakdown['LoTW_Sent'] = 0;
- $QSLBreakdown['LoTW_Received'] = 0;
- $QSLBreakdown['QRZ_Sent'] = 0;
- $QSLBreakdown['QRZ_Received'] = 0;
- $QSLBreakdown['QSL_Sent_today'] = 0;
- $QSLBreakdown['QSL_Received_today'] = 0;
- $QSLBreakdown['QSL_Requested_today'] = 0;
- $QSLBreakdown['eQSL_Sent_today'] = 0;
- $QSLBreakdown['eQSL_Received_today'] = 0;
- $QSLBreakdown['LoTW_Sent_today'] = 0;
- $QSLBreakdown['LoTW_Received_today'] = 0;
- $QSLBreakdown['QRZ_Sent_today'] = 0;
- $QSLBreakdown['QRZ_Received_today'] = 0;
-
- return $QSLBreakdown;
}
- } else {
- $QSLBreakdown['QSL_Sent'] = 0;
- $QSLBreakdown['QSL_Received'] = 0;
- $QSLBreakdown['QSL_Requested'] = 0;
- $QSLBreakdown['eQSL_Sent'] = 0;
- $QSLBreakdown['eQSL_Received'] = 0;
- $QSLBreakdown['LoTW_Sent'] = 0;
- $QSLBreakdown['LoTW_Received'] = 0;
- $QSLBreakdown['QRZ_Sent'] = 0;
- $QSLBreakdown['QRZ_Received'] = 0;
- $QSLBreakdown['QSL_Sent_today'] = 0;
- $QSLBreakdown['QSL_Received_today'] = 0;
- $QSLBreakdown['QSL_Requested_today'] = 0;
- $QSLBreakdown['eQSL_Sent_today'] = 0;
- $QSLBreakdown['eQSL_Received_today'] = 0;
- $QSLBreakdown['LoTW_Sent_today'] = 0;
- $QSLBreakdown['LoTW_Received_today'] = 0;
- $QSLBreakdown['QRZ_Sent_today'] = 0;
- $QSLBreakdown['QRZ_Received_today'] = 0;
- return $QSLBreakdown;
+ // Return default values if no results
+ return array(
+ 'QSL_Sent' => 0,
+ 'QSL_Received' => 0,
+ 'QSL_Requested' => 0,
+ 'eQSL_Sent' => 0,
+ 'eQSL_Received' => 0,
+ 'LoTW_Sent' => 0,
+ 'LoTW_Received' => 0,
+ 'QRZ_Sent' => 0,
+ 'QRZ_Received' => 0,
+ 'QSL_Sent_today' => 0,
+ 'QSL_Received_today' => 0,
+ 'QSL_Requested_today' => 0,
+ 'eQSL_Sent_today' => 0,
+ 'eQSL_Received_today' => 0,
+ 'LoTW_Sent_today' => 0,
+ 'LoTW_Received_today' => 0,
+ 'QRZ_Sent_today' => 0,
+ 'QRZ_Received_today' => 0
+ );
+ } else {
+ return array(
+ 'QSL_Sent' => 0,
+ 'QSL_Received' => 0,
+ 'QSL_Requested' => 0,
+ 'eQSL_Sent' => 0,
+ 'eQSL_Received' => 0,
+ 'LoTW_Sent' => 0,
+ 'LoTW_Received' => 0,
+ 'QRZ_Sent' => 0,
+ 'QRZ_Received' => 0,
+ 'QSL_Sent_today' => 0,
+ 'QSL_Received_today' => 0,
+ 'QSL_Requested_today' => 0,
+ 'eQSL_Sent_today' => 0,
+ 'eQSL_Received_today' => 0,
+ 'LoTW_Sent_today' => 0,
+ 'LoTW_Received_today' => 0,
+ 'QRZ_Sent_today' => 0,
+ 'QRZ_Received_today' => 0
+ );
}
}
@@ -2986,6 +2990,53 @@ class Logbook_model extends CI_Model
}
}
+ // Consolidated method to get all country statistics in one query
+ function get_countries_statistics_consolidated($StationLocationsArray = null) {
+ if ($StationLocationsArray == null) {
+ $CI = &get_instance();
+ $CI->load->model('logbooks_model');
+ $logbooks_locations_array = $CI->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
+ } else {
+ $logbooks_locations_array = $StationLocationsArray;
+ }
+
+ if (!empty($logbooks_locations_array)) {
+ // Get both confirmed countries and current countries in one query
+ $this->db->select('
+ COUNT(DISTINCT COL_COUNTRY) as Countries_Worked,
+ COUNT(DISTINCT IF(COL_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_QSL,
+ COUNT(DISTINCT IF(COL_EQSL_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_EQSL,
+ COUNT(DISTINCT IF(COL_LOTW_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_LOTW,
+ COUNT(DISTINCT IF(dxcc_entities.end IS NULL, COL_COUNTRY, NULL)) as Countries_Current
+ ');
+ $this->db->join('dxcc_entities', 'dxcc_entities.adif = ' . $this->config->item('table_name') . '.col_dxcc', 'left');
+ $this->db->where_in('station_id', $logbooks_locations_array);
+ $this->db->where('COL_COUNTRY !=', 'Invalid');
+ $this->db->where('COL_DXCC >', '0');
+
+ if ($query = $this->db->get($this->config->item('table_name'))) {
+ if ($query->num_rows() > 0) {
+ $row = $query->row();
+ return array(
+ 'Countries_Worked' => $row->Countries_Worked,
+ 'Countries_Worked_QSL' => $row->Countries_Worked_QSL,
+ 'Countries_Worked_EQSL' => $row->Countries_Worked_EQSL,
+ 'Countries_Worked_LOTW' => $row->Countries_Worked_LOTW,
+ 'Countries_Current' => $row->Countries_Current
+ );
+ }
+ }
+ }
+
+ return array(
+ 'Countries_Worked' => 0,
+ 'Countries_Worked_QSL' => 0,
+ 'Countries_Worked_EQSL' => 0,
+ 'Countries_Worked_LOTW' => 0,
+ 'Countries_Current' => 0
+ );
+ }
+
/* Return total number of countries confirmed with paper QSL */
function total_countries_confirmed_paper()
{
@@ -4806,6 +4857,7 @@ class Logbook_model extends CI_Model
$plot = array('lat' => 0, 'lng' => 0, 'html' => '', 'label' => '', 'flag' => '', 'confirmed' => 'N');
$plot['label'] = $row->COL_CALL;
+ $plot['callsign'] = $row->COL_CALL;
$flag = strtolower($CI->dxccflag->getISO($row->COL_DXCC));
$plot['flag'] = 'name))) . '"> ';
$plot['html'] = ($row->COL_GRIDSQUARE != null ? "Grid: " . $row->COL_GRIDSQUARE . "
" : "");
@@ -4980,6 +5032,63 @@ class Logbook_model extends CI_Model
// If all parsing fails, return the original input and let the database handle it
return $date_input;
}
+
+ /* Consolidated QSO Statistics - Get all basic counts in a single query */
+ function get_qso_statistics_consolidated($StationLocationsArray = null)
+ {
+ if ($StationLocationsArray == null) {
+ $this->load->model('logbooks_model');
+ $logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
+ } else {
+ $logbooks_locations_array = $StationLocationsArray;
+ }
+
+ if (!$logbooks_locations_array) {
+ return array(
+ 'total_qsos' => 0,
+ 'todays_qsos' => 0,
+ 'month_qsos' => 0,
+ 'year_qsos' => 0
+ );
+ }
+
+ // Calculate date ranges
+ $today_morning = date('Y-m-d 00:00:00');
+ $today_night = date('Y-m-d 23:59:59');
+ $month_morning = date('Y-m-01 00:00:00');
+ $date = new DateTime('now');
+ $date->modify('last day of this month');
+ $month_night = $date->format('Y-m-d') . " 23:59:59";
+ $year_morning = date('Y-01-01 00:00:00');
+ $year_night = date('Y-12-31 23:59:59');
+
+ // Build the consolidated query
+ $this->db->select("
+ COUNT(*) as total_qsos,
+ COUNT(CASE WHEN COL_TIME_ON >= '$today_morning' AND COL_TIME_ON <= '$today_night' THEN 1 END) as todays_qsos,
+ COUNT(CASE WHEN COL_TIME_ON >= '$month_morning' AND COL_TIME_ON <= '$month_night' THEN 1 END) as month_qsos,
+ COUNT(CASE WHEN COL_TIME_ON >= '$year_morning' AND COL_TIME_ON <= '$year_night' THEN 1 END) as year_qsos
+ ", FALSE);
+ $this->db->where_in('station_id', $logbooks_locations_array);
+ $query = $this->db->get($this->config->item('table_name'));
+
+ if ($query->num_rows() > 0) {
+ $row = $query->row();
+ return array(
+ 'total_qsos' => (int)$row->total_qsos,
+ 'todays_qsos' => (int)$row->todays_qsos,
+ 'month_qsos' => (int)$row->month_qsos,
+ 'year_qsos' => (int)$row->year_qsos
+ );
+ }
+
+ return array(
+ 'total_qsos' => 0,
+ 'todays_qsos' => 0,
+ 'month_qsos' => 0,
+ 'year_qsos' => 0
+ );
+ }
}
// Function to validate ADIF date format
diff --git a/application/models/Setup_model.php b/application/models/Setup_model.php
index 66692014..eae2abd9 100644
--- a/application/models/Setup_model.php
+++ b/application/models/Setup_model.php
@@ -24,6 +24,25 @@ class Setup_model extends CI_Model {
return $query->row()->count;
}
+
+ // Consolidated method to get all setup counts in one query
+ function getAllSetupCounts() {
+ $userid = xss_clean($this->session->userdata('user_id'));
+
+ $sql = "SELECT
+ (SELECT COUNT(*) FROM dxcc_entities) as country_count,
+ (SELECT COUNT(*) FROM station_logbooks WHERE user_id = {$userid}) as logbook_count,
+ (SELECT COUNT(*) FROM station_profile WHERE user_id = {$userid}) as location_count";
+
+ $query = $this->db->query($sql);
+ $row = $query->row();
+
+ return array(
+ 'country_count' => $row->country_count,
+ 'logbook_count' => $row->logbook_count,
+ 'location_count' => $row->location_count
+ );
+ }
}
?>
diff --git a/application/models/Stats.php b/application/models/Stats.php
index 8fb9bf20..2cdbf1c7 100644
--- a/application/models/Stats.php
+++ b/application/models/Stats.php
@@ -88,10 +88,20 @@
}
}
+ // Initialize bandcalls array with all bands set to 0
+ foreach ($bands as $band) {
+ $bandcalls[$band] = 0;
+ }
+
foreach ($bandunique as $band) {
$bandcalls[$band->band] = $band->calls;
}
+ // Initialize modecalls array with all modes set to 0
+ foreach ($modes as $mode) {
+ $modecalls[$mode] = 0;
+ }
+
foreach ($modeunique as $mode) {
//if ($mode->col_submode == null) {
if ($mode->col_submode == null || $mode->col_submode == "") {
diff --git a/application/models/Workabledxcc_model.php b/application/models/Workabledxcc_model.php
index d7a1860d..a09e54c3 100644
--- a/application/models/Workabledxcc_model.php
+++ b/application/models/Workabledxcc_model.php
@@ -2,87 +2,421 @@
class Workabledxcc_model extends CI_Model
{
+ // Cache for DXCC lookups to avoid repeated queries
+ private $dxccCache = array();
+ private $workedCache = array();
+
+ /**
+ * Batch DXCC lookup for multiple callsigns
+ * @param array $callsigns Array of callsigns
+ * @param array $dates Array of dates corresponding to callsigns
+ * @return array Array of DXCC entities indexed by callsign index
+ */
+ public function batchDxccLookup($callsigns, $dates)
+ {
+ $this->load->model('logbook_model');
+ $entities = array();
+
+ foreach ($callsigns as $index => $callsign) {
+ $cacheKey = $callsign . '_' . $dates[$index];
+
+ if (!isset($this->dxccCache[$cacheKey])) {
+ $dxccInfo = $this->logbook_model->dxcc_lookup($callsign, $dates[$index]);
+ $this->dxccCache[$cacheKey] = isset($dxccInfo['entity']) ? $dxccInfo['entity'] : null;
+ }
+
+ $entities[$index] = $this->dxccCache[$cacheKey];
+ }
+
+ return $entities;
+ }
+
+ /**
+ * Batch check if DXCC entities have been worked/confirmed
+ * @param array $entities Array of unique DXCC entities
+ * @return array Array of worked/confirmed status indexed by entity
+ */
+ public function batchDxccWorkedStatus($entities)
+ {
+ if (empty($entities)) {
+ return array();
+ }
+
+ $user_default_confirmation = $this->session->userdata('user_default_confirmation');
+ $this->load->model('logbooks_model');
+ $logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
+
+ if (empty($logbooks_locations_array)) {
+ return array_fill_keys($entities, [
+ 'workedBefore' => false,
+ 'confirmed' => false,
+ 'workedViaSatellite' => false
+ ]);
+ }
+
+ $results = array();
+
+ // Build confirmation criteria once
+ $confirmationCriteria = $this->buildConfirmationCriteria($user_default_confirmation);
+
+ // Debug: Log entities being checked
+ log_message('debug', 'Workable DXCC: Checking entities: ' . implode(', ', $entities));
+
+ // Batch query for worked status (terrestrial)
+ $workedResults = $this->batchWorkedQuery($entities, $logbooks_locations_array);
+
+ // Batch query for confirmed status (terrestrial)
+ $confirmedResults = $this->batchConfirmedQuery($entities, $logbooks_locations_array, $confirmationCriteria);
+
+ // Batch query for satellite contacts
+ $satelliteResults = $this->batchSatelliteQuery($entities, $logbooks_locations_array);
+
+ // Debug: Log results
+ log_message('debug', 'Workable DXCC: Worked results: ' . json_encode($workedResults));
+ log_message('debug', 'Workable DXCC: Confirmed results: ' . json_encode($confirmedResults));
+ log_message('debug', 'Workable DXCC: Satellite results: ' . json_encode($satelliteResults));
+
+ // Combine results
+ foreach ($entities as $entity) {
+ $results[$entity] = [
+ 'workedBefore' => isset($workedResults[$entity]),
+ 'confirmed' => isset($confirmedResults[$entity]),
+ 'workedViaSatellite' => isset($satelliteResults[$entity])
+ ];
+ }
+
+ return $results;
+ }
+
+ /**
+ * Batch check IOTA worked/confirmed status
+ * @param array $iotas Array of unique IOTA tags
+ * @return array Array indexed by IOTA tag with ['worked'=>bool,'confirmed'=>bool]
+ */
+ public function batchIotaWorkedStatus($iotas)
+ {
+ if (empty($iotas)) {
+ return array();
+ }
+
+ $user_default_confirmation = $this->session->userdata('user_default_confirmation');
+ $this->load->model('logbooks_model');
+ $logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
+
+ if (empty($logbooks_locations_array)) {
+ $out = [];
+ foreach ($iotas as $i) {
+ $out[$i] = ['worked' => false, 'confirmed' => false];
+ }
+ return $out;
+ }
+
+ // Build confirmation criteria once
+ $confirmationCriteria = $this->buildConfirmationCriteria($user_default_confirmation);
+
+ // Build case-insensitive WHERE conditions for COL_IOTA
+ $whereConditions = array();
+ foreach ($iotas as $iota) {
+ $whereConditions[] = "UPPER(COL_IOTA) = UPPER('" . $this->db->escape_str($iota) . "')";
+ }
+
+ if (empty($whereConditions)) {
+ return array();
+ }
+
+ $whereClause = '(' . implode(' OR ', $whereConditions) . ')';
+
+ // Worked query (any mode)
+ $this->db->select('COL_IOTA')
+ ->distinct()
+ ->from($this->config->item('table_name'))
+ ->where_in('station_id', $logbooks_locations_array)
+ ->where($whereClause);
+
+ $workedQuery = $this->db->get();
+ log_message('debug', 'Workable DXCC IOTA worked query: ' . $this->db->last_query());
+
+ $workedResults = array();
+ foreach ($workedQuery->result() as $row) {
+ foreach ($iotas as $iota) {
+ if (strtoupper($row->COL_IOTA) === strtoupper($iota)) {
+ $workedResults[$iota] = true;
+ break;
+ }
+ }
+ }
+
+ // Confirmed query (apply confirmation criteria, exclude satellite confirmations if desired)
+ $confirmedResults = array();
+ if ($confirmationCriteria !== '1=0') {
+ $this->db->select('COL_IOTA')
+ ->distinct()
+ ->from($this->config->item('table_name'))
+ ->where($confirmationCriteria)
+ ->where_in('station_id', $logbooks_locations_array)
+ ->where($whereClause);
+
+ $confirmedQuery = $this->db->get();
+ foreach ($confirmedQuery->result() as $row) {
+ foreach ($iotas as $iota) {
+ if (strtoupper($row->COL_IOTA) === strtoupper($iota)) {
+ $confirmedResults[$iota] = true;
+ break;
+ }
+ }
+ }
+ }
+
+ $out = array();
+ foreach ($iotas as $iota) {
+ $out[$iota] = [
+ 'worked' => isset($workedResults[$iota]),
+ 'confirmed' => isset($confirmedResults[$iota])
+ ];
+ }
+
+ // Debug
+ log_message('debug', 'Workable DXCC: IOTA worked results: ' . json_encode($workedResults));
+ log_message('debug', 'Workable DXCC: IOTA confirmed results: ' . json_encode($confirmedResults));
+
+ return $out;
+ }
+
+ /**
+ * Batch query to check which entities have been worked via satellite
+ */
+ private function batchSatelliteQuery($entities, $logbooks_locations_array)
+ {
+ // Create case-insensitive matching for DXCC entities
+ $whereConditions = array();
+ foreach ($entities as $entity) {
+ $whereConditions[] = "UPPER(COL_COUNTRY) = UPPER('" . $this->db->escape_str($entity) . "')";
+ }
+
+ if (empty($whereConditions)) {
+ return array();
+ }
+
+ $whereClause = '(' . implode(' OR ', $whereConditions) . ')';
+
+ $this->db->select('COL_COUNTRY')
+ ->distinct()
+ ->from($this->config->item('table_name'))
+ ->where('COL_PROP_MODE', 'SAT') // Only satellite contacts
+ ->where_in('station_id', $logbooks_locations_array)
+ ->where($whereClause);
+
+ $query = $this->db->get();
+
+ // Debug: Log the SQL query
+ log_message('debug', 'Workable DXCC satellite query: ' . $this->db->last_query());
+
+ $results = array();
+
+ foreach ($query->result() as $row) {
+ // Store with the original entity case for lookup
+ foreach ($entities as $entity) {
+ if (strtoupper($row->COL_COUNTRY) === strtoupper($entity)) {
+ $results[$entity] = true;
+ log_message('debug', 'Workable DXCC: Found satellite match: ' . $entity . ' matches ' . $row->COL_COUNTRY);
+ break;
+ }
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Build confirmation criteria SQL based on user preferences
+ */
+ private function buildConfirmationCriteria($user_default_confirmation)
+ {
+ $criteria = array();
+
+ if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Q') !== false) {
+ $criteria[] = "COL_QSL_RCVD='Y'";
+ }
+ if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'L') !== false) {
+ $criteria[] = "COL_LOTW_QSL_RCVD='Y'";
+ }
+ if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'E') !== false) {
+ $criteria[] = "COL_EQSL_QSL_RCVD='Y'";
+ }
+ if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Z') !== false) {
+ $criteria[] = "COL_QRZCOM_QSO_DOWNLOAD_STATUS='Y'";
+ }
+
+ return empty($criteria) ? '1=0' : '(' . implode(' OR ', $criteria) . ')';
+ }
+
+ /**
+ * Batch query to check which entities have been worked
+ */
+ private function batchWorkedQuery($entities, $logbooks_locations_array)
+ {
+ // Create case-insensitive matching for DXCC entities
+ $whereConditions = array();
+ foreach ($entities as $entity) {
+ $whereConditions[] = "UPPER(COL_COUNTRY) = UPPER('" . $this->db->escape_str($entity) . "')";
+ }
+
+ if (empty($whereConditions)) {
+ return array();
+ }
+
+ $whereClause = '(' . implode(' OR ', $whereConditions) . ')';
+
+ $this->db->select('COL_COUNTRY')
+ ->distinct()
+ ->from($this->config->item('table_name'))
+ ->where('COL_PROP_MODE !=', 'SAT')
+ ->where_in('station_id', $logbooks_locations_array)
+ ->where($whereClause);
+
+ $query = $this->db->get();
+
+ // Debug: Log the SQL query
+ log_message('debug', 'Workable DXCC worked query: ' . $this->db->last_query());
+
+ $results = array();
+
+ foreach ($query->result() as $row) {
+ // Store with the original entity case for lookup
+ foreach ($entities as $entity) {
+ if (strtoupper($row->COL_COUNTRY) === strtoupper($entity)) {
+ $results[$entity] = true;
+ log_message('debug', 'Workable DXCC: Found worked match: ' . $entity . ' matches ' . $row->COL_COUNTRY);
+ break;
+ }
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Batch query to check which entities have been confirmed
+ */
+ private function batchConfirmedQuery($entities, $logbooks_locations_array, $confirmationCriteria)
+ {
+ if ($confirmationCriteria === '1=0') {
+ return array();
+ }
+
+ // Create case-insensitive matching for DXCC entities
+ $whereConditions = array();
+ foreach ($entities as $entity) {
+ $whereConditions[] = "UPPER(COL_COUNTRY) = UPPER('" . $this->db->escape_str($entity) . "')";
+ }
+
+ if (empty($whereConditions)) {
+ return array();
+ }
+
+ $whereClause = '(' . implode(' OR ', $whereConditions) . ')';
+
+ $this->db->select('COL_COUNTRY')
+ ->distinct()
+ ->from($this->config->item('table_name'))
+ ->where('COL_PROP_MODE !=', 'SAT')
+ ->where($confirmationCriteria)
+ ->where_in('station_id', $logbooks_locations_array)
+ ->where($whereClause);
+
+ $query = $this->db->get();
+ $results = array();
+
+ foreach ($query->result() as $row) {
+ // Store with the original entity case for lookup
+ foreach ($entities as $entity) {
+ if (strtoupper($row->COL_COUNTRY) === strtoupper($entity)) {
+ $results[$entity] = true;
+ break;
+ }
+ }
+ }
+
+ return $results;
+ }
public function GetThisWeek()
{
- $json = file_get_contents($this->optionslib->get_option('dxped_url'));
-
- // Step 2: Convert the JSON data to an array.
+ $json = file_get_contents($this->optionslib->get_option('dxped_url'));
$data = json_decode($json, true);
- // Step 3: Create a new array to hold the records for this week.
- $thisWeekRecords = [];
+ if (empty($data)) {
+ return array();
+ }
- // Get the start and end of this week.
+ $thisWeekRecords = [];
$startOfWeek = (new DateTime())->setISODate((new DateTime())->format('o'), (new DateTime())->format('W'), 1);
$endOfWeek = (clone $startOfWeek)->modify('+6 days');
- // Step 4: Iterate over the array.
- foreach ($data as $record) {
+ // Get Date format
+ if ($this->session->userdata('user_date_format')) {
+ $custom_date_format = $this->session->userdata('user_date_format');
+ } else {
+ $custom_date_format = $this->config->item('qso_date_format');
+ }
- // Convert "0" and "1" to DateTime objects.
+ // First pass: filter records for this week
+ $weekRecords = array();
+ foreach ($data as $record) {
$startDate = new DateTime($record['0']);
$endDate = new DateTime($record['1']);
- // Step 5: Check if the start date or end date is within this week.
- if (($startDate >= $startOfWeek && $startDate <= $endOfWeek) || ($endDate >= $startOfWeek && $endDate <= $endOfWeek)) {
- $endDate = new DateTime($record['1']);
- $now = new DateTime();
- $interval = $now->diff($endDate);
- $daysLeft = $interval->days;
-
- // If daysLeft is 0, set it to "Last day"
- if ($daysLeft == 0) {
- $daysLeft = "Last day";
- } else {
- $daysLeft = $daysLeft . " days left";
- }
-
- // Add daysLeft to record
- $record['daysLeft'] = $daysLeft;
- // Get Date format
- if ($this->session->userdata('user_date_format')) {
- // If Logged in and session exists
- $custom_date_format = $this->session->userdata('user_date_format');
- } else {
- // Get Default date format from /config/cloudlog.php
- $custom_date_format = $this->config->item('qso_date_format');
- }
-
- // Create a new array with the required fields and add it to the main array
- $oldStartDate = DateTime::createFromFormat('Y-m-d', $record['0']);
-
- $StartDate = $oldStartDate->format($custom_date_format);
- $record['startDate'] = $StartDate;
-
- $oldEndDate = DateTime::createFromFormat('Y-m-d', $record['1']);
- $EndDate = $oldEndDate->format($custom_date_format);
- $record['endDate'] = $EndDate;
-
- $record['confirmed'] = true; // or false, depending on your logic
-
- $CI = &get_instance();
- $CI->load->model('logbook_model');
- $dxccInfo = $CI->logbook_model->dxcc_lookup($record['callsign'], $startDate->format('Y-m-d'));
-
- // Call DXCC Worked function to check if the DXCC has been worked before
- if (isset($dxccInfo['entity'])) {
- $dxccWorkedData = $this->dxccWorked($dxccInfo['entity']);
- $record = array_merge($record, $dxccWorkedData);
- } else {
- // Handle the case where 'entity' is not set in $dxccInfo
- $itemsToAdd = array(
- 'workedBefore' => false,
- 'confirmed' => false,
- );
- $record = array_merge($record, $itemsToAdd);
- }
-
- $thisWeekRecords[] = $record;
+ if (($startDate >= $startOfWeek && $startDate <= $endOfWeek) ||
+ ($endDate >= $startOfWeek && $endDate <= $endOfWeek)) {
+ $weekRecords[] = $record;
}
}
- return $thisWeekRecords;
+ if (empty($weekRecords)) {
+ return array();
+ }
+
+ // Batch process DXCC lookups
+ $callsigns = array_column($weekRecords, 'callsign');
+ $dates = array_column($weekRecords, '0');
+ $dxccEntities = $this->batchDxccLookup($callsigns, $dates);
+
+ // Get worked/confirmed status for all entities in batch
+ $uniqueEntities = array_unique(array_filter($dxccEntities));
+ $dxccStatus = $this->batchDxccWorkedStatus($uniqueEntities);
+
+ // Process results
+ foreach ($weekRecords as $index => $record) {
+ $endDate = new DateTime($record['1']);
+ $now = new DateTime();
+ $interval = $now->diff($endDate);
+ $daysLeft = $interval->days;
+
+ $daysLeft = ($daysLeft == 0) ? "Last day" : $daysLeft . " days left";
+ $record['daysLeft'] = $daysLeft;
+
+ $oldStartDate = DateTime::createFromFormat('Y-m-d', $record['0']);
+ $record['startDate'] = $oldStartDate->format($custom_date_format);
+
+ $oldEndDate = DateTime::createFromFormat('Y-m-d', $record['1']);
+ $record['endDate'] = $oldEndDate->format($custom_date_format);
+
+ // Get DXCC status for this callsign
+ $entity = $dxccEntities[$index] ?? null;
+ $worked = $entity && isset($dxccStatus[$entity]) ? $dxccStatus[$entity] : [
+ 'workedBefore' => false,
+ 'confirmed' => false,
+ 'workedViaSatellite' => false
+ ];
+
+ $record['workedBefore'] = $worked['workedBefore'];
+ $record['confirmed'] = $worked['confirmed'];
+ $record['workedViaSatellite'] = $worked['workedViaSatellite'];
+
+ $thisWeekRecords[] = $record;
+ }
+
+ return $thisWeekRecords;
}
function dxccWorked($country)
@@ -91,6 +425,7 @@ class Workabledxcc_model extends CI_Model
$return = [
"workedBefore" => false,
"confirmed" => false,
+ "workedViaSatellite" => false,
];
$user_default_confirmation = $this->session->userdata('user_default_confirmation');
@@ -99,16 +434,28 @@ class Workabledxcc_model extends CI_Model
$this->load->model('logbook_model');
if (!empty($logbooks_locations_array)) {
+ // Check for terrestrial contacts
$this->db->where('COL_PROP_MODE !=', 'SAT');
$this->db->where_in('station_id', $logbooks_locations_array);
- $this->db->where('COL_COUNTRY', urldecode($country));
+ // Fix case sensitivity issue for DXCC country matching
+ $this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
$query = $this->db->get($this->config->item('table_name'), 1, 0);
foreach ($query->result() as $workedBeforeRow) {
$return['workedBefore'] = true;
}
+ // Check for satellite contacts
+ $this->db->where('COL_PROP_MODE', 'SAT');
+ $this->db->where_in('station_id', $logbooks_locations_array);
+ $this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
+
+ $query = $this->db->get($this->config->item('table_name'), 1, 0);
+ foreach ($query->result() as $satelliteRow) {
+ $return['workedViaSatellite'] = true;
+ }
+
$extrawhere = '';
if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Q') !== false) {
$extrawhere = "COL_QSL_RCVD='Y'";
@@ -144,7 +491,8 @@ class Workabledxcc_model extends CI_Model
$this->db->where_in('station_id', $logbooks_locations_array);
- $this->db->where('COL_COUNTRY', urldecode($country));
+ // Fix case sensitivity issue for DXCC country matching
+ $this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
$query = $this->db->get($this->config->item('table_name'), 1, 0);
foreach ($query->result() as $workedBeforeRow) {
diff --git a/application/views/adif/import.php b/application/views/adif/import.php
index 2de27165..973de685 100644
--- a/application/views/adif/import.php
+++ b/application/views/adif/import.php
@@ -1,6 +1,12 @@
Import and export your amateur radio logs in ADIF format
+-
B.
+ +