From 26d2a46c08c73492e39fb5130122d0fae65ce331 Mon Sep 17 00:00:00 2001 From: HB9HIL Date: Mon, 2 Feb 2026 22:07:00 +0100 Subject: [PATCH 1/6] first fixes of dxclustercache since new cache driver handling --- application/controllers/Dxcluster.php | 27 +++-- application/libraries/DxclusterCache.php | 120 ++--------------------- application/models/Dxcluster_model.php | 7 -- 3 files changed, 28 insertions(+), 126 deletions(-) diff --git a/application/controllers/Dxcluster.php b/application/controllers/Dxcluster.php index 928d3a8d9..40f4ae9d2 100644 --- a/application/controllers/Dxcluster.php +++ b/application/controllers/Dxcluster.php @@ -9,9 +9,9 @@ class Dxcluster extends CI_Controller { function __construct() { parent::__construct(); - $this->load->model('user_model'); + $this->load->is_loaded('user_model') ?: $this->load->model('user_model'); if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('error', __("You're not allowed to do that!")); redirect('dashboard'); } - $this->load->model('dxcluster_model'); + $this->load->is_loaded('dxcluster_model') ?: $this->load->model('dxcluster_model'); } @@ -35,19 +35,21 @@ class Dxcluster extends CI_Controller { header('Content-Type: application/json'); if ($calls_found && !empty($calls_found)) { - echo json_encode($calls_found); + http_response_code(200); + echo json_encode($calls_found, JSON_PRETTY_PRINT); } else { - echo json_encode(['error' => 'not found']); + $this->_return_not_found(); } } function qrg_lookup($qrg) { - $call_found=$this->dxcluster_model->dxc_qrg_lookup($this->security->xss_clean($qrg)); - header('Content-Type: application/json'); + $call_found = $this->dxcluster_model->dxc_qrg_lookup($this->security->xss_clean($qrg)); + header('Content-Type: application/json'); if ($call_found) { + http_response_code(200); echo json_encode($call_found, JSON_PRETTY_PRINT); } else { - echo '{ "error": "not found" }'; + $this->_return_not_found(); } } @@ -57,11 +59,18 @@ class Dxcluster extends CI_Controller { $dxcc = $dxccobj->dxcc_lookup($call, $date); + header('Content-Type: application/json'); if ($dxcc) { - header('Content-Type: application/json'); + http_response_code(200); echo json_encode($dxcc, JSON_PRETTY_PRINT); } else { - echo '{ "error": "not found" }'; + $this->_return_not_found(); } } + + private function _return_not_found() { + header('Content-Type: application/json'); + http_response_code(404); + echo json_encode(['error' => 'not found'], JSON_PRETTY_PRINT); + } } diff --git a/application/libraries/DxclusterCache.php b/application/libraries/DxclusterCache.php index b8d2b6a08..013a9874b 100644 --- a/application/libraries/DxclusterCache.php +++ b/application/libraries/DxclusterCache.php @@ -77,35 +77,20 @@ class DxclusterCache { if (empty($logbook_key)) return; // Delete callsign cache - $this->deleteFile($this->getWorkedCallKey($logbook_key, $callsign)); + $this->_delete_from_cache($this->getWorkedCallKey($logbook_key, $callsign)); // Look up DXCC and continent from callsign $dxccobj = new Dxcc(null); $dxcc_info = $dxccobj->dxcc_lookup($callsign, date('Y-m-d')); if (!empty($dxcc_info['adif'])) { - $this->deleteFile($this->getWorkedDxccKey($logbook_key, $dxcc_info['adif'])); + $this->_delete_from_cache($this->getWorkedDxccKey($logbook_key, $dxcc_info['adif'])); } if (!empty($dxcc_info['cont'])) { - $this->deleteFile($this->getWorkedContKey($logbook_key, $dxcc_info['cont'])); + $this->_delete_from_cache($this->getWorkedContKey($logbook_key, $dxcc_info['cont'])); } } - /** - * Invalidate all worked cache for current user (bulk operations) - */ - public function invalidateAllWorkedForCurrentUser() { - // Skip if worked cache is disabled - if ($this->CI->config->item('enable_dxcluster_file_cache_worked') !== true) return; - - $logbook_key = $this->getCurrentUserLogbookKey(); - if (empty($logbook_key)) return; - - $this->invalidateByPrefix("dxcluster_worked_call_{$logbook_key}_"); - $this->invalidateByPrefix("dxcluster_worked_dxcc_{$logbook_key}_"); - $this->invalidateByPrefix("dxcluster_worked_cont_{$logbook_key}_"); - } - /** * Get current user's logbook key from session */ @@ -129,97 +114,12 @@ class DxclusterCache { // INTERNAL HELPERS // ========================================================================= - protected function deleteFile($cache_key) { - $cache_path = $this->getCachePath(); - if (!$cache_path) return; - @unlink($cache_path . $cache_key); - } - - protected function invalidateByPrefix($prefix) { - $cache_path = $this->getCachePath(); - if (!$cache_path) return; - - $handle = @opendir($cache_path); - if (!$handle) return; - - while (($filename = readdir($handle)) !== false) { - if (strpos($filename, $prefix) === 0) { - @unlink($cache_path . $filename); - } - } - closedir($handle); - } - - protected function getCachePath() { - $cache_path = $this->CI->config->item('cache_path'); - $cache_path = ($cache_path === '' || $cache_path === false) ? APPPATH . 'cache/' : $cache_path; - $cache_path = rtrim($cache_path, '/\\') . DIRECTORY_SEPARATOR; - return (is_dir($cache_path) && is_writable($cache_path)) ? $cache_path : false; - } - - // ========================================================================= - // GARBAGE COLLECTION - // ========================================================================= - - /** - * Run garbage collection with probability check (1% chance) - * Call this on each request when worked cache is enabled - */ - public function maybeRunGc() { - if (mt_rand(1, 100) === 1) { - $this->cleanExpiredCache(); - } - } - - /** - * Clean expired dxcluster cache files - * Uses file mtime for fast pre-filtering before reading file contents - */ - public function cleanExpiredCache() { - $cache_path = $this->getCachePath(); - if (!$cache_path || !is_readable($cache_path)) return; - - $handle = @opendir($cache_path); - if (!$handle) return; - - $now = time(); - $deleted = 0; - - // Max TTL for dxcluster files: raw=59s, worked=900s - use 900s + buffer - $max_ttl = 1000; - - while (($filename = readdir($handle)) !== false) { - // Only process dxcluster cache files - if (strpos($filename, 'dxcluster_') !== 0) continue; - - $file = $cache_path . $filename; - if (!is_file($file)) continue; - - // Fast pre-filter: skip files modified recently (can't be expired yet) - $mtime = @filemtime($file); - if ($mtime !== false && ($now - $mtime) < $max_ttl) { - continue; - } - - // File is old enough to potentially be expired - read and verify - $data = @unserialize(@file_get_contents($file)); - if (!is_array($data) || !isset($data['time'], $data['ttl'])) { - @unlink($file); - $deleted++; - continue; - } - - // Check if expired - if ($data['ttl'] > 0 && $now > $data['time'] + $data['ttl']) { - @unlink($file); - $deleted++; - } - } - - closedir($handle); - - if ($deleted > 0) { - log_message('debug', "DXCluster cache GC: deleted {$deleted} expired files"); - } + protected function _delete_from_cache($cache_key) { + $this->CI->load->driver('cache', [ + 'adapter' => $this->CI->config->item('cache_adapter') ?? 'file', + 'backup' => $this->CI->config->item('cache_backup') ?? 'file', + 'key_prefix' => $this->CI->config->item('cache_key_prefix') ?? '' + ]); + $this->CI->cache->delete($cache_key); } } diff --git a/application/models/Dxcluster_model.php b/application/models/Dxcluster_model.php index 70e6c8d63..24f4f38d1 100644 --- a/application/models/Dxcluster_model.php +++ b/application/models/Dxcluster_model.php @@ -58,13 +58,6 @@ class Dxcluster_model extends CI_Model { 'backup' => $this->config->item('cache_backup') ?? 'file', 'key_prefix' => $this->config->item('cache_key_prefix') ?? '' ]); - - // Garbage collection: 1% chance to clean expired cache files - // Only needed when worked cache is enabled (creates many per-callsign files) - if ($cache_worked_enabled) { - $this->load->library('DxclusterCache'); - $this->dxclustercache->maybeRunGc(); - } } if($this->session->userdata('user_date_format')) { From c2d5079061c81363c6266881ccb8958b01eba88a Mon Sep 17 00:00:00 2001 From: HB9HIL Date: Mon, 2 Feb 2026 22:33:31 +0100 Subject: [PATCH 2/6] avoid loading the cache driver multiple times --- application/controllers/Dxcluster.php | 10 +++++++++ application/models/Dxcluster_model.php | 31 +++++++++----------------- application/models/Logbook_model.php | 17 ++++++++------ 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/application/controllers/Dxcluster.php b/application/controllers/Dxcluster.php index 40f4ae9d2..8e3baffb7 100644 --- a/application/controllers/Dxcluster.php +++ b/application/controllers/Dxcluster.php @@ -20,6 +20,16 @@ class Dxcluster extends CI_Controller { $band = $this->security->xss_clean($band); $mode = $this->security->xss_clean($mode); + + // Only load cache driver if caching is enabled + if (($this->config->item('enable_dxcluster_file_cache_band') ?? false) || ($this->config->item('enable_dxcluster_file_cache_worked') ?? false)) { + $this->load->driver('cache', [ + 'adapter' => $this->config->item('cache_adapter') ?? 'file', + 'backup' => $this->config->item('cache_backup') ?? 'file', + 'key_prefix' => $this->config->item('cache_key_prefix') ?? '' + ]); + } + if ($age == '') { $age = $this->optionslib->get_option('dxcluster_maxage') ?? 60; } else { diff --git a/application/models/Dxcluster_model.php b/application/models/Dxcluster_model.php index 24f4f38d1..6b2439bd2 100644 --- a/application/models/Dxcluster_model.php +++ b/application/models/Dxcluster_model.php @@ -27,8 +27,11 @@ class Dxcluster_model extends CI_Model { 'ISCAT', 'JT6M', 'FST4', 'FST4W', 'FREEDV', 'VARA' ]; + protected $cache_band_enabled; + public function __construct() { - $this->load->Model('Modes'); + $this->load->is_loaded('Modes') ?: $this->load->model('Modes'); + $this->db->where('bandedges.userid', $this->session->userdata('user_id')); $query = $this->db->get('bandedges'); $result = $query->result_array(); @@ -41,24 +44,12 @@ class Dxcluster_model extends CI_Model { $query = $this->db->get('bandedges'); $this->bandedges = $query->result_array(); } + + $this->cache_band_enabled = ($this->config->item('enable_dxcluster_file_cache_band') ?? false); } // Main function to get spot list from DXCache and process it public function dxc_spotlist($band = '20m', $maxage = 60, $de = '', $mode = 'All') { - $this->load->helper(array('psr4_autoloader')); - - // Check if file caching is enabled in config - $cache_band_enabled = $this->config->item('enable_dxcluster_file_cache_band') === true; - $cache_worked_enabled = $this->config->item('enable_dxcluster_file_cache_worked') === true; - - // Only load cache driver if caching is enabled - if ($cache_band_enabled || $cache_worked_enabled) { - $this->load->driver('cache', [ - 'adapter' => $this->config->item('cache_adapter') ?? 'file', - 'backup' => $this->config->item('cache_backup') ?? 'file', - 'key_prefix' => $this->config->item('cache_key_prefix') ?? '' - ]); - } if($this->session->userdata('user_date_format')) { $custom_date_format = $this->session->userdata('user_date_format'); @@ -74,17 +65,17 @@ class Dxcluster_model extends CI_Model { $dxcache_url = $dxcache_url . '/spots/'.$band; } - $this->load->model('logbook_model'); + $this->load->is_loaded('logbook_model') ?: $this->load->model('logbook_model'); $logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook')); // Cache key for RAW cluster response (instance-wide, no worked status) // Use DxclusterCache library for centralized key generation - $this->load->library('DxclusterCache'); + $raw_cache_key = $this->dxclustercache->getRawCacheKey($maxage, $band); // Check cache for raw processed spots (without worked status) $spotsout = null; - if ($cache_band_enabled) { + if ($this->cache_band_enabled) { $spotsout = $this->cache->get($raw_cache_key); } @@ -248,7 +239,7 @@ class Dxcluster_model extends CI_Model { } // Cache the RAW processed spots (WITHOUT worked status) - instance-wide - if ($cache_band_enabled && !empty($spotsout)) { + if ($this->cache_band_enabled && !empty($spotsout)) { $this->cache->save($raw_cache_key, $spotsout, 59); } } @@ -536,7 +527,7 @@ class Dxcluster_model extends CI_Model { } public function dxc_qrg_lookup($qrg, $maxage = 120) { - $this->load->helper(array('psr4_autoloader')); + if (is_numeric($qrg)) { $dxcache_url = ($this->optionslib->get_option('dxcache_url') == '' ? 'https://dxc.wavelog.org/dxcache' : $this->optionslib->get_option('dxcache_url')); diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 42892992d..25fda55f3 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -2715,13 +2715,16 @@ class Logbook_model extends CI_Model { // Load cache driver for file caching $cache_enabled = $this->config->item('enable_dxcluster_file_cache_worked') === true; - if ($cache_enabled && !isset($this->cache)) { - $this->load->driver('cache', [ - 'adapter' => $this->config->item('cache_adapter') ?? 'file', - 'backup' => $this->config->item('cache_backup') ?? 'file', - 'key_prefix' => $this->config->item('cache_key_prefix') ?? '' - ]); - } + + // Gets already loaded in dxcluster controller + // + // if ($cache_enabled && !isset($this->cache)) { + // $this->load->driver('cache', [ + // 'adapter' => $this->config->item('cache_adapter') ?? 'file', + // 'backup' => $this->config->item('cache_backup') ?? 'file', + // 'key_prefix' => $this->config->item('cache_key_prefix') ?? '' + // ]); + // } // Cache TTL in seconds (15 minutes = 900 seconds) $cache_ttl = 900; From 4c4d5845e5f08022003fde10d9e834947517413d Mon Sep 17 00:00:00 2001 From: HB9HIL Date: Mon, 2 Feb 2026 22:44:44 +0100 Subject: [PATCH 3/6] some methods were camelCase and some snake_case. made all snake_case as this is usual for our codebase --- application/libraries/DxclusterCache.php | 18 +++++++++--------- application/models/Logbook_model.php | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/application/libraries/DxclusterCache.php b/application/libraries/DxclusterCache.php index 013a9874b..25526f801 100644 --- a/application/libraries/DxclusterCache.php +++ b/application/libraries/DxclusterCache.php @@ -40,21 +40,21 @@ class DxclusterCache { /** * Generate WORKED callsign cache key */ - public function getWorkedCallKey($logbook_key, $callsign) { + public function get_worked_call_key($logbook_key, $callsign) { return "dxcluster_worked_call_{$logbook_key}_{$callsign}"; } /** * Generate WORKED DXCC cache key */ - public function getWorkedDxccKey($logbook_key, $dxcc) { + public function get_worked_dxcc_key($logbook_key, $dxcc) { return "dxcluster_worked_dxcc_{$logbook_key}_{$dxcc}"; } /** * Generate WORKED continent cache key */ - public function getWorkedContKey($logbook_key, $cont) { + public function get_worked_cont_key($logbook_key, $cont) { return "dxcluster_worked_cont_{$logbook_key}_{$cont}"; } @@ -73,28 +73,28 @@ class DxclusterCache { if (empty($callsign)) return; // Get current user's logbook key - $logbook_key = $this->getCurrentUserLogbookKey(); + $logbook_key = $this->_get_current_user_logbook_key(); if (empty($logbook_key)) return; // Delete callsign cache - $this->_delete_from_cache($this->getWorkedCallKey($logbook_key, $callsign)); + $this->_delete_from_cache($this->get_worked_call_key($logbook_key, $callsign)); // Look up DXCC and continent from callsign $dxccobj = new Dxcc(null); $dxcc_info = $dxccobj->dxcc_lookup($callsign, date('Y-m-d')); if (!empty($dxcc_info['adif'])) { - $this->_delete_from_cache($this->getWorkedDxccKey($logbook_key, $dxcc_info['adif'])); + $this->_delete_from_cache($this->get_worked_dxcc_key($logbook_key, $dxcc_info['adif'])); } if (!empty($dxcc_info['cont'])) { - $this->_delete_from_cache($this->getWorkedContKey($logbook_key, $dxcc_info['cont'])); + $this->_delete_from_cache($this->get_worked_cont_key($logbook_key, $dxcc_info['cont'])); } } /** * Get current user's logbook key from session */ - protected function getCurrentUserLogbookKey() { + private function _get_current_user_logbook_key() { $user_id = $this->CI->session->userdata('user_id'); $active_logbook = $this->CI->session->userdata('active_station_logbook'); @@ -114,7 +114,7 @@ class DxclusterCache { // INTERNAL HELPERS // ========================================================================= - protected function _delete_from_cache($cache_key) { + private function _delete_from_cache($cache_key) { $this->CI->load->driver('cache', [ 'adapter' => $this->CI->config->item('cache_adapter') ?? 'file', 'backup' => $this->CI->config->item('cache_backup') ?? 'file', diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 25fda55f3..8c206f82e 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -2784,7 +2784,7 @@ class Logbook_model extends CI_Model { if (!isset($this->spot_status_cache[$cache_key])) { // Check file cache if ($cache_enabled) { - $file_cache_key = $this->dxclustercache->getWorkedCallKey($logbook_ids_key, $callsign); + $file_cache_key = $this->dxclustercache->get_worked_call_key($logbook_ids_key, $callsign); $cached_data = $this->cache->get($file_cache_key); if ($cached_data !== false) { // Load from file cache into in-memory cache @@ -2802,7 +2802,7 @@ class Logbook_model extends CI_Model { if (!isset($this->spot_status_cache[$cache_key])) { if ($cache_enabled) { - $file_cache_key = $this->dxclustercache->getWorkedDxccKey($logbook_ids_key, $dxcc); + $file_cache_key = $this->dxclustercache->get_worked_dxcc_key($logbook_ids_key, $dxcc); $cached_data = $this->cache->get($file_cache_key); if ($cached_data !== false) { $this->spot_status_cache[$cache_key] = $cached_data; @@ -2818,7 +2818,7 @@ class Logbook_model extends CI_Model { if (!isset($this->spot_status_cache[$cache_key])) { if ($cache_enabled) { - $file_cache_key = $this->dxclustercache->getWorkedContKey($logbook_ids_key, $cont); + $file_cache_key = $this->dxclustercache->get_worked_cont_key($logbook_ids_key, $cont); $cached_data = $this->cache->get($file_cache_key); if ($cached_data !== false) { $this->spot_status_cache[$cache_key] = $cached_data; @@ -3061,7 +3061,7 @@ class Logbook_model extends CI_Model { // Save to file cache for 15 minutes if ($cache_enabled) { - $file_cache_key = $this->dxclustercache->getWorkedCallKey($logbook_ids_key, $callsign); + $file_cache_key = $this->dxclustercache->get_worked_call_key($logbook_ids_key, $callsign); $this->cache->save($file_cache_key, $data, $cache_ttl); } } @@ -3070,7 +3070,7 @@ class Logbook_model extends CI_Model { $this->spot_status_cache[$cache_key] = $data; if ($cache_enabled) { - $file_cache_key = $this->dxclustercache->getWorkedDxccKey($logbook_ids_key, $dxcc); + $file_cache_key = $this->dxclustercache->get_worked_dxcc_key($logbook_ids_key, $dxcc); $this->cache->save($file_cache_key, $data, $cache_ttl); } } @@ -3079,7 +3079,7 @@ class Logbook_model extends CI_Model { $this->spot_status_cache[$cache_key] = $data; if ($cache_enabled) { - $file_cache_key = $this->dxclustercache->getWorkedContKey($logbook_ids_key, $cont); + $file_cache_key = $this->dxclustercache->get_worked_cont_key($logbook_ids_key, $cont); $this->cache->save($file_cache_key, $data, $cache_ttl); } } // Cache NOT WORKED items (negative results) - store empty arrays @@ -3090,7 +3090,7 @@ class Logbook_model extends CI_Model { $this->spot_status_cache[$cache_key] = []; // Empty = not worked if ($cache_enabled) { - $file_cache_key = $this->dxclustercache->getWorkedCallKey($logbook_ids_key, $callsign); + $file_cache_key = $this->dxclustercache->get_worked_call_key($logbook_ids_key, $callsign); $this->cache->save($file_cache_key, [], $cache_ttl); } } @@ -3101,7 +3101,7 @@ class Logbook_model extends CI_Model { $this->spot_status_cache[$cache_key] = []; if ($cache_enabled) { - $file_cache_key = $this->dxclustercache->getWorkedDxccKey($logbook_ids_key, $dxcc); + $file_cache_key = $this->dxclustercache->get_worked_dxcc_key($logbook_ids_key, $dxcc); $this->cache->save($file_cache_key, [], $cache_ttl); } } @@ -3112,7 +3112,7 @@ class Logbook_model extends CI_Model { $this->spot_status_cache[$cache_key] = []; if ($cache_enabled) { - $file_cache_key = $this->dxclustercache->getWorkedContKey($logbook_ids_key, $cont); + $file_cache_key = $this->dxclustercache->get_worked_cont_key($logbook_ids_key, $cont); $this->cache->save($file_cache_key, [], $cache_ttl); } } From c0c36a50d4a3d6b3beeb8c888cf2221e9ca9c971 Mon Sep 17 00:00:00 2001 From: HB9HIL Date: Mon, 2 Feb 2026 23:07:45 +0100 Subject: [PATCH 4/6] more cameCase to snake_case, defined function types and prefixed private functions with _, removed unused functions from old dxcluster --- application/controllers/Dxcluster.php | 6 +- application/libraries/DxclusterCache.php | 8 +- application/models/Dxcluster_model.php | 146 +++-------------------- application/models/Logbook_model.php | 8 +- 4 files changed, 30 insertions(+), 138 deletions(-) diff --git a/application/controllers/Dxcluster.php b/application/controllers/Dxcluster.php index 8e3baffb7..17df1a565 100644 --- a/application/controllers/Dxcluster.php +++ b/application/controllers/Dxcluster.php @@ -15,7 +15,7 @@ class Dxcluster extends CI_Controller { } - function spots($band, $age = '', $de = '', $mode = 'All') { + public function spots($band, $age = '', $de = '', $mode = 'All') { // Sanitize inputs $band = $this->security->xss_clean($band); $mode = $this->security->xss_clean($mode); @@ -52,7 +52,7 @@ class Dxcluster extends CI_Controller { } } - function qrg_lookup($qrg) { + public function qrg_lookup($qrg) { $call_found = $this->dxcluster_model->dxc_qrg_lookup($this->security->xss_clean($qrg)); header('Content-Type: application/json'); if ($call_found) { @@ -63,7 +63,7 @@ class Dxcluster extends CI_Controller { } } - function call($call) { + public function call($call) { $date = date('Y-m-d', time()); $dxccobj = new Dxcc($date); diff --git a/application/libraries/DxclusterCache.php b/application/libraries/DxclusterCache.php index 25526f801..c6d9d4816 100644 --- a/application/libraries/DxclusterCache.php +++ b/application/libraries/DxclusterCache.php @@ -24,14 +24,14 @@ class DxclusterCache { /** * Generate RAW spot cache key (instance-wide, shared by all users) */ - public function getRawCacheKey($maxage, $band) { + public function get_raw_cache_key($maxage, $band) { return "dxcluster_raw_{$maxage}_{$band}_Any_All"; } /** * Generate logbook IDs key component (user-specific) */ - public function getLogbookKey($user_id, $logbook_ids, $confirmation_prefs) { + public function get_logbook_key($user_id, $logbook_ids, $confirmation_prefs) { $logbook_ids_str = implode('_', $logbook_ids); $confirmation_hash = md5($confirmation_prefs); return "{$user_id}_{$logbook_ids_str}_{$confirmation_hash}"; @@ -66,7 +66,7 @@ class DxclusterCache { * Invalidate cache after QSO add/edit/delete for current user * @param string $callsign - The worked callsign */ - public function invalidateForCallsign($callsign) { + public function invalidate_for_callsign($callsign) { // Skip if worked cache is disabled if ($this->CI->config->item('enable_dxcluster_file_cache_worked') !== true) return; @@ -107,7 +107,7 @@ class DxclusterCache { if (empty($logbook_ids)) return null; - return $this->getLogbookKey($user_id, $logbook_ids, $confirmation_prefs); + return $this->get_logbook_key($user_id, $logbook_ids, $confirmation_prefs); } // ========================================================================= diff --git a/application/models/Dxcluster_model.php b/application/models/Dxcluster_model.php index 6b2439bd2..91338acad 100644 --- a/application/models/Dxcluster_model.php +++ b/application/models/Dxcluster_model.php @@ -71,7 +71,7 @@ class Dxcluster_model extends CI_Model { // Cache key for RAW cluster response (instance-wide, no worked status) // Use DxclusterCache library for centralized key generation - $raw_cache_key = $this->dxclustercache->getRawCacheKey($maxage, $band); + $raw_cache_key = $this->dxclustercache->get_raw_cache_key($maxage, $band); // Check cache for raw processed spots (without worked status) $spotsout = null; @@ -155,7 +155,7 @@ class Dxcluster_model extends CI_Model { $singlespot->frequency = floatval($singlespot->frequency); // Validate against amateur band allocations (skip non-amateur frequencies) - if (!$this->isFrequencyInAmateurBand($singlespot->frequency)) { + if (!$this->_is_frequency_in_amateurband($singlespot->frequency)) { continue; } @@ -168,7 +168,7 @@ class Dxcluster_model extends CI_Model { // Only determine mode if not provided by cluster if (!isset($singlespot->mode) || empty($singlespot->mode)) { - $singlespot->mode = $this->get_mode($singlespot); + $singlespot->mode = $this->_get_mode($singlespot); } else { // Normalize cluster-provided mode to lowercase $singlespot->mode = strtolower($singlespot->mode); @@ -176,7 +176,7 @@ class Dxcluster_model extends CI_Model { // Only determine submode if not provided by cluster if (!isset($singlespot->submode) || empty($singlespot->submode)) { - $singlespot->submode = $this->get_submode($singlespot); + $singlespot->submode = $this->_get_submode($singlespot); } else { // Normalize cluster-provided submode to uppercase $singlespot->submode = strtoupper($singlespot->submode); @@ -232,7 +232,7 @@ class Dxcluster_model extends CI_Model { } // Extract park references from message - $singlespot = $this->enrich_spot_metadata($singlespot); + $singlespot = $this->_enrich_spot_metadata($singlespot); // Collect spots for batch processing $spotsout[] = $singlespot; @@ -249,7 +249,7 @@ class Dxcluster_model extends CI_Model { $de_lower = strtolower($de); $filter_continent = ($de != '' && $de != 'Any'); $spotsout = array_filter($spotsout, function($spot) use ($mode, $de_lower, $filter_continent) { - if ($mode != 'All' && !$this->modefilter($spot, $mode)) return false; + if ($mode != 'All' && !$this->_modefilter($spot, $mode)) return false; if ($filter_continent && ($de_lower != strtolower($spot->dxcc_spotter->cont ?? ''))) return false; return true; }); @@ -341,13 +341,14 @@ class Dxcluster_model extends CI_Model { return $spotsout; } // Determine mode with priority: POTA/SOTA mode > message keywords > frequency-based - function get_mode($spot) { + + private function _get_mode($spot) { // Priority 0: If spot already has a valid mode from cluster, use it if (isset($spot->mode) && !empty($spot->mode)) { $existingMode = strtolower($spot->mode); // Validate it's a known mode category if (in_array($existingMode, ['cw', 'phone', 'digi', 'ssb'])) { - return $this->mapToModeCategory($existingMode); + return $this->_map_to_mode_category($existingMode); } } @@ -356,10 +357,10 @@ class Dxcluster_model extends CI_Model { $sotaMode = $spot->sota_mode ?? $spot->dxcc_spotted->sota_mode ?? null; if (!empty($potaMode)) { - return $this->mapToModeCategory($potaMode); + return $this->_map_to_mode_category($potaMode); } if (!empty($sotaMode)) { - return $this->mapToModeCategory($sotaMode); + return $this->_map_to_mode_category($sotaMode); } // Priority 2: Message keywords (explicit mode in message text) @@ -381,7 +382,7 @@ class Dxcluster_model extends CI_Model { // Priority 3: Frequency-based mode (from bandedges table) // If frequency falls within a defined band edge, use that mode - $frequencyMode = $this->Frequency2Mode($spot->frequency); + $frequencyMode = $this->_frequency_to_mode($spot->frequency); if ($frequencyMode != '') { return $frequencyMode; } @@ -391,7 +392,7 @@ class Dxcluster_model extends CI_Model { } // Map specific mode names to mode categories (phone/cw/digi) - function mapToModeCategory($mode) { + private function _map_to_mode_category($mode) { $modeUpper = strtoupper($mode); // CW modes @@ -416,7 +417,7 @@ class Dxcluster_model extends CI_Model { } // Determine submode for more specific mode classification - function get_submode($spot) { + private function _get_submode($spot) { // Priority 0: If spot already has a valid submode from cluster, use it if (isset($spot->submode) && !empty($spot->submode)) { return strtoupper($spot->submode); @@ -466,16 +467,16 @@ class Dxcluster_model extends CI_Model { return strtoupper($mode); } - function modefilter($spot, $mode) { + private function _modefilter($spot, $mode) { $mode = strtolower($mode); // Normalize case $spotMode = strtolower($spot->mode ?? ''); // Get already-determined mode - // Since get_mode() already determined the mode using priority logic + // Since _get_mode() already determined the mode using priority logic // (frequency > POTA/SOTA > message), we can directly compare return $spotMode === $mode; } - public function Frequency2Mode($frequency) { + private function _frequency_to_mode($frequency) { // Ensure frequency is in Hz if input is in kHz if ($frequency < 1_000_000) { $frequency *= 1000; @@ -489,29 +490,12 @@ class Dxcluster_model extends CI_Model { return ''; } - public function isFrequencyInMode($frequency, $mode) { - // Ensure frequency is in Hz if input is in kHz - if ($frequency < 1_000_000) { - $frequency *= 1000; - } - - foreach ($this->bandedges as $band) { - if (strtolower($band['mode']) === strtolower($mode)) { - if ($frequency >= $band['frequencyfrom'] && $frequency < $band['frequencyto']) { - return true; - } - } - } - - return false; - } - /** * Check if frequency falls within amateur band allocations * @param float $frequency Frequency in Hz * @return bool True if frequency is in amateur band */ - public function isFrequencyInAmateurBand($frequency) { + private function _is_frequency_in_amateurband($frequency) { // Ensure frequency is in Hz if input is in kHz if ($frequency < 1_000_000) { $frequency *= 1000; @@ -569,98 +553,6 @@ class Dxcluster_model extends CI_Model { } } - function check_if_continent_worked_in_logbook($cont, $StationLocationsArray = null, $band = null, $mode = 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; - } - - $this->db->select('COL_CONT'); - $this->db->where_in('station_id', $logbooks_locations_array); - $this->db->where('COL_CONT', $cont); - - if (isset($mode)) { - $this->db->where(" COL_MODE in ".$this->Modes->get_modes_from_qrgmode($mode,true)); - } - - $band = ($band == 'All') ? null : $band; - if ($band != null && $band != 'SAT') { - $this->db->where('COL_BAND', $band); - } else if ($band == 'SAT') { - // Where col_sat_name is not empty - $this->db->where('COL_SAT_NAME !=', ''); - } - $this->db->limit('2'); - - $query = $this->db->get($this->config->item('table_name')); - return $query->num_rows(); - } - - function check_if_continent_cnfmd_in_logbook($cont, $StationLocationsArray = null, $band = null, $mode = 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; - } - - $user_default_confirmation = $this->session->userdata('user_default_confirmation'); - $extrawhere = ''; - if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Q') !== false) { - $extrawhere = "COL_QSL_RCVD='Y'"; - } - if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'L') !== false) { - if ($extrawhere != '') { - $extrawhere .= " OR"; - } - $extrawhere .= " COL_LOTW_QSL_RCVD='Y'"; - } - if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'E') !== false) { - if ($extrawhere != '') { - $extrawhere .= " OR"; - } - $extrawhere .= " COL_EQSL_QSL_RCVD='Y'"; - } - - if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Z') !== false) { - if ($extrawhere != '') { - $extrawhere .= " OR"; - } - $extrawhere .= " COL_QRZCOM_QSO_DOWNLOAD_STATUS='Y'"; - } - - - $this->db->select('COL_CONT'); - $this->db->where_in('station_id', $logbooks_locations_array); - $this->db->where('COL_CONT', $cont); - - if (isset($mode)) { - $this->db->where(" COL_MODE in ".$this->Modes->get_modes_from_qrgmode($mode,true)); - } - - $band = ($band == 'All') ? null : $band; - if ($band != null && $band != 'SAT') { - $this->db->where('COL_BAND', $band); - } else if ($band == 'SAT') { - // Where col_sat_name is not empty - $this->db->where('COL_SAT_NAME !=', ''); - } - if ($extrawhere != '') { - $this->db->where('(' . $extrawhere . ')'); - } else { - $this->db->where("1=0"); - } - $this->db->limit('2'); - - $query = $this->db->get($this->config->item('table_name')); - - return $query->num_rows(); - } - /** * Enrich spot metadata with park references and contest detection * Extracts SOTA/POTA/IOTA/WWFF references and detects contest spots @@ -668,7 +560,7 @@ class Dxcluster_model extends CI_Model { * @param object $spot - Spot object with message and dxcc_spotted properties * @return object - Spot object with enriched dxcc_spotted containing references and isContest flag */ - function enrich_spot_metadata($spot) { + private function _enrich_spot_metadata($spot) { // Ensure dxcc_spotted object exists if (!property_exists($spot, 'dxcc_spotted') || !is_object($spot->dxcc_spotted)) { $spot->dxcc_spotted = (object)[]; diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 8c206f82e..80d9aa7ef 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -899,7 +899,7 @@ class Logbook_model extends CI_Model { } // Invalidate DXCluster cache for this callsign - $this->dxclustercache->invalidateForCallsign($data['COL_CALL']); + $this->dxclustercache->invalidate_for_callsign($data['COL_CALL']); // Return QSO ID for ADIF generation return $last_id; @@ -1674,7 +1674,7 @@ class Logbook_model extends CI_Model { $retvals['success']=true; // Invalidate DXCluster cache for this callsign - $this->dxclustercache->invalidateForCallsign($data['COL_CALL']); + $this->dxclustercache->invalidate_for_callsign($data['COL_CALL']); } catch (Exception $e) { $retvals['success']=false; $retvals['detail']=$e; @@ -2746,7 +2746,7 @@ class Logbook_model extends CI_Model { // Build cache key with user_id, logbook_ids, and confirmation preference $user_id = $this->session->userdata('user_id'); - $logbook_ids_key = $this->dxclustercache->getLogbookKey($user_id, $logbooks_locations_array, $user_default_confirmation); + $logbook_ids_key = $this->dxclustercache->get_logbook_key($user_id, $logbooks_locations_array, $user_default_confirmation); $spots_by_callsign = []; // Group spots by callsign for processing foreach ($spots as $spot) { @@ -4354,7 +4354,7 @@ class Logbook_model extends CI_Model { $this->db->delete("oqrs"); // Invalidate DXCluster cache for this callsign - $this->dxclustercache->invalidateForCallsign($callsign); + $this->dxclustercache->invalidate_for_callsign($callsign); } else { return; } From 12ecf803107e22f6dc4bffec21ed0ec036b8f3e7 Mon Sep 17 00:00:00 2001 From: HB9HIL Date: Mon, 2 Feb 2026 23:26:24 +0100 Subject: [PATCH 5/6] prevent cache driver loaded multiple times --- src/Dxcc/Dxcc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dxcc/Dxcc.php b/src/Dxcc/Dxcc.php index 71227c281..9bc494900 100644 --- a/src/Dxcc/Dxcc.php +++ b/src/Dxcc/Dxcc.php @@ -323,7 +323,7 @@ class Dxcc { */ function read_data($date = NULL) { $CI = &get_instance(); - $CI->load->driver('cache', [ + $CI->load->is_loaded('cache') ?: $CI->load->driver('cache', [ 'adapter' => $CI->config->item('cache_adapter') ?? 'file', 'backup' => $CI->config->item('cache_backup') ?? 'file', 'key_prefix' => $CI->config->item('cache_key_prefix') ?? '' From cf27d030b6d4a7b6e497bb62f7fc7ce034c65c0e Mon Sep 17 00:00:00 2001 From: HB9HIL Date: Tue, 3 Feb 2026 00:25:00 +0100 Subject: [PATCH 6/6] Use the same date format so we can benefit from the dxcc lookup cache --- application/models/Dxcluster_model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/models/Dxcluster_model.php b/application/models/Dxcluster_model.php index 91338acad..3fc73d4cd 100644 --- a/application/models/Dxcluster_model.php +++ b/application/models/Dxcluster_model.php @@ -133,7 +133,7 @@ class Dxcluster_model extends CI_Model { log_message('debug', 'DXCluster: Empty array received from ' . $dxcache_url . ' (no spots available)'); return []; } - $date = date('Ymd', time()); + $date = date('Y-m-d', time()); $dxccObj = new DXCC($date);