From 71f4d38e47f42449884a8b57dc430b242704d8b3 Mon Sep 17 00:00:00 2001 From: HB9HIL Date: Tue, 3 Feb 2026 23:03:44 +0100 Subject: [PATCH 1/6] optimizing qsl breakdown.. not much but a bit better --- application/models/Logbook_model.php | 52 ++++++++++++++++------------ 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 784a3f8d3..6717ca7b7 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -3930,30 +3930,36 @@ class Logbook_model extends CI_Model { $logbooks_locations_array = $StationLocationsArray; } - 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 - '); - $this->db->where_in('station_id', $logbooks_locations_array); + $location_list = "'" . implode("','", $logbooks_locations_array) . "'"; - if ($query = $this->db->get($this->config->item('table_name'))) { + if (!empty($logbooks_locations_array)) { + $todayStart = date('Y-m-d 00:00:00'); + $tomorrowStart = date('Y-m-d 00:00:00', strtotime('+1 day')); + $todayStartSql = $this->db->escape($todayStart); + $tomorrowStartSql = $this->db->escape($tomorrowStart); + $sql = 'SELECT + SUM(CASE WHEN COL_QSL_SENT="Y" THEN 1 ELSE 0 END) as QSL_Sent, + SUM(CASE WHEN COL_QSL_RCVD="Y" THEN 1 ELSE 0 END) as QSL_Received, + SUM(CASE WHEN COL_QSL_SENT IN("Q", "R") THEN 1 ELSE 0 END) as QSL_Requested, + SUM(CASE WHEN COL_EQSL_QSL_SENT="Y" THEN 1 ELSE 0 END) as eQSL_Sent, + SUM(CASE WHEN COL_EQSL_QSL_RCVD="Y" THEN 1 ELSE 0 END) as eQSL_Received, + SUM(CASE WHEN COL_LOTW_QSL_SENT="Y" THEN 1 ELSE 0 END) as LoTW_Sent, + SUM(CASE WHEN COL_LOTW_QSL_RCVD="Y" THEN 1 ELSE 0 END) as LoTW_Received, + SUM(CASE WHEN COL_QRZCOM_QSO_UPLOAD_STATUS="Y" THEN 1 ELSE 0 END) as QRZ_Sent, + SUM(CASE WHEN COL_QRZCOM_QSO_DOWNLOAD_STATUS="Y" THEN 1 ELSE 0 END) as QRZ_Received, + SUM(CASE WHEN COL_QSL_SENT="Y" AND COL_QSLSDATE >= ' . $todayStartSql . ' AND COL_QSLSDATE < ' . $tomorrowStartSql . ' THEN 1 ELSE 0 END) as QSL_Sent_today, + SUM(CASE WHEN COL_QSL_RCVD="Y" AND COL_QSLRDATE >= ' . $todayStartSql . ' AND COL_QSLRDATE < ' . $tomorrowStartSql . ' THEN 1 ELSE 0 END) as QSL_Received_today, + SUM(CASE WHEN COL_QSL_SENT IN("Q", "R") AND COL_QSLSDATE >= ' . $todayStartSql . ' AND COL_QSLSDATE < ' . $tomorrowStartSql . ' THEN 1 ELSE 0 END) as QSL_Requested_today, + SUM(CASE WHEN COL_EQSL_QSL_SENT="Y" AND COL_EQSL_QSLSDATE >= ' . $todayStartSql . ' AND COL_EQSL_QSLSDATE < ' . $tomorrowStartSql . ' THEN 1 ELSE 0 END) as eQSL_Sent_today, + SUM(CASE WHEN COL_EQSL_QSL_RCVD="Y" AND COL_EQSL_QSLRDATE >= ' . $todayStartSql . ' AND COL_EQSL_QSLRDATE < ' . $tomorrowStartSql . ' THEN 1 ELSE 0 END) as eQSL_Received_today, + SUM(CASE WHEN COL_LOTW_QSL_SENT="Y" AND COL_LOTW_QSLSDATE >= ' . $todayStartSql . ' AND COL_LOTW_QSLSDATE < ' . $tomorrowStartSql . ' THEN 1 ELSE 0 END) as LoTW_Sent_today, + SUM(CASE WHEN COL_LOTW_QSL_RCVD="Y" AND COL_LOTW_QSLRDATE >= ' . $todayStartSql . ' AND COL_LOTW_QSLRDATE < ' . $tomorrowStartSql . ' THEN 1 ELSE 0 END) as LoTW_Received_today, + SUM(CASE WHEN COL_QRZCOM_QSO_UPLOAD_STATUS="Y" AND COL_QRZCOM_QSO_UPLOAD_DATE >= ' . $todayStartSql . ' AND COL_QRZCOM_QSO_UPLOAD_DATE < ' . $tomorrowStartSql . ' THEN 1 ELSE 0 END) as QRZ_Sent_today, + SUM(CASE WHEN COL_QRZCOM_QSO_DOWNLOAD_STATUS="Y" AND COL_QRZCOM_QSO_DOWNLOAD_DATE >= ' . $todayStartSql . ' AND COL_QRZCOM_QSO_DOWNLOAD_DATE < ' . $tomorrowStartSql . ' THEN 1 ELSE 0 END) as QRZ_Received_today + FROM ' . $this->config->item('table_name') . ' + WHERE station_id IN (' . $location_list . ')'; + + if ($query = $this->db->query($sql)) { $this->db->last_query(); foreach ($query->result() as $row) { $QSLBreakdown['QSL_Sent'] = $row->QSL_Sent; From 2a2c6d7abea5b7a74104a088069d7c28eef7979f Mon Sep 17 00:00:00 2001 From: int2001 Date: Wed, 4 Feb 2026 05:34:33 +0000 Subject: [PATCH 2/6] Optimize Yr/Mon/Day-Stat-SQL --- application/controllers/Api.php | 9 ++-- application/controllers/Dashboard.php | 11 ++-- application/controllers/Visitor.php | 10 ++-- application/models/Logbook_model.php | 74 +++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 14 deletions(-) diff --git a/application/controllers/Api.php b/application/controllers/Api.php index 358fc9022..2fa8af2eb 100644 --- a/application/controllers/Api.php +++ b/application/controllers/Api.php @@ -837,10 +837,11 @@ class API extends CI_Controller { $this->load->model('api_model'); if ((($key ?? '') != '') && ($this->api_model->authorize($key) != 0)) { $this->load->model('logbook_model'); - $data['todays_qsos'] = $this->logbook_model->todays_qsos(null, $key); - $data['total_qsos'] = $this->logbook_model->total_qsos(null, $key); - $data['month_qsos'] = $this->logbook_model->month_qsos(null, $key); - $data['year_qsos'] = $this->logbook_model->year_qsos(null, $key); + $qso_counts = $this->logbook_model->get_qso_counts(null, $key); + $data['todays_qsos'] = $qso_counts['today']; + $data['total_qsos'] = $qso_counts['total']; + $data['month_qsos'] = $qso_counts['month']; + $data['year_qsos'] = $qso_counts['year']; } else { # for Downcompat $data['todays_qsos'] = 0; $data['total_qsos'] = 0; diff --git a/application/controllers/Dashboard.php b/application/controllers/Dashboard.php index ba49a97bf..e234cd7c4 100644 --- a/application/controllers/Dashboard.php +++ b/application/controllers/Dashboard.php @@ -4,6 +4,7 @@ class Dashboard extends CI_Controller { public function index() { + $this->output->enable_profiler(TRUE); // Check if users logged in $this->load->model('user_model'); if ($this->user_model->validate_session() == 0) { @@ -97,11 +98,11 @@ 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); + $qso_counts = $this->logbook_model->get_qso_counts($logbooks_locations_array); + $data['todays_qsos'] = $qso_counts['today']; + $data['total_qsos'] = $qso_counts['total']; + $data['month_qsos'] = $qso_counts['month']; + $data['year_qsos'] = $qso_counts['year']; $rawstreak=$this->dayswithqso_model->getAlmostCurrentStreak(); if (is_array($rawstreak)) { diff --git a/application/controllers/Visitor.php b/application/controllers/Visitor.php index a783b692c..9f3779ab3 100644 --- a/application/controllers/Visitor.php +++ b/application/controllers/Visitor.php @@ -97,11 +97,11 @@ class Visitor extends CI_Controller { $this->pagination->initialize($config); - // 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); + $qso_counts = $this->logbook_model->get_qso_counts($logbooks_locations_array); + $data['todays_qsos'] = $qso_counts['today']; + $data['total_qsos'] = $qso_counts['total']; + $data['month_qsos'] = $qso_counts['month']; + $data['year_qsos'] = $qso_counts['year']; $data['user_map_custom'] = $this->optionslib->get_map_custom(true,$public_slug); diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 784a3f8d3..68b06a108 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -3684,6 +3684,80 @@ class Logbook_model extends CI_Model { } } + /* + * Combined function to get all QSO counts (today, month, year, total) in a single query + * This reduces 4 separate queries to 1, improving performance + */ + function get_qso_counts($StationLocationsArray = null, $api_key = null) { + if ($StationLocationsArray == null) { + $this->load->model('logbooks_model'); + if ($api_key != null) { + $this->load->model('api_model'); + if (strpos($this->api_model->access($api_key), 'r') !== false) { + $this->api_model->update_last_used($api_key); + $user_id = $this->api_model->key_userid($api_key); + $active_station_logbook = $this->logbooks_model->find_active_station_logbook_from_userid($user_id); + $logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($active_station_logbook); + } else { + $logbooks_locations_array = []; + } + } else { + $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 [ + 'total' => 0, + 'today' => 0, + 'month' => 0, + 'year' => 0 + ]; + } + + // Calculate date boundaries once + $todayStart = date('Y-m-d 00:00:00'); + $todayEnd = date('Y-m-d 23:59:59'); + $monthStart = date('Y-m-01 00:00:00'); + + $date = new DateTime('now'); + $date->modify('last day of this month'); + $monthEnd = $date->format('Y-m-d') . ' 23:59:59'; + + $yearStart = date('Y-01-01 00:00:00'); + $yearEnd = date('Y-12-31 23:59:59'); + + // Single query with conditional aggregation + $sql = "SELECT + COUNT(*) as total, + SUM(CASE WHEN COL_TIME_ON >= ? AND COL_TIME_ON <= ? THEN 1 ELSE 0 END) as today, + SUM(CASE WHEN COL_TIME_ON >= ? AND COL_TIME_ON <= ? THEN 1 ELSE 0 END) as month, + SUM(CASE WHEN COL_TIME_ON >= ? AND COL_TIME_ON <= ? THEN 1 ELSE 0 END) as year + FROM " . $this->config->item('table_name') . " + WHERE station_id IN ('" . implode("','", $logbooks_locations_array) . "')"; + + $query = $this->db->query($sql, [$todayStart, $todayEnd, $monthStart, $monthEnd, $yearStart, $yearEnd]); + + if ($query->num_rows() > 0) { + $row = $query->row(); + return [ + 'total' => (int)$row->total, + 'today' => (int)$row->today, + 'month' => (int)$row->month, + 'year' => (int)$row->year + ]; + } + + return [ + 'total' => 0, + 'today' => 0, + 'month' => 0, + 'year' => 0 + ]; + } + private function where_date_range($dateFrom, $dateTo) { if (!empty($dateFrom)) { $this->db->where('COL_TIME_ON >=', $dateFrom . ' 00:00:00'); From e2ad5d63da114f9f2102aea1d7c440ecaefb197f Mon Sep 17 00:00:00 2001 From: int2001 Date: Wed, 4 Feb 2026 05:53:20 +0000 Subject: [PATCH 3/6] Optimize VUCC-Queries --- application/models/Vucc.php | 168 +++++++++++++++++++++++------------- 1 file changed, 107 insertions(+), 61 deletions(-) diff --git a/application/models/Vucc.php b/application/models/Vucc.php index 7aad267fe..4163de860 100644 --- a/application/models/Vucc.php +++ b/application/models/Vucc.php @@ -353,76 +353,122 @@ class VUCC extends CI_Model return $workedGridArray; } + private function get_vucc_combined_data($band = 'All') { + if (!$this->logbooks_locations_array) { + return ['gridsquare' => [], 'vucc_grids' => []]; + } + + $results = ['gridsquare' => [], 'vucc_grids' => []]; + + $inPlaceholders = str_repeat('?,', count($this->logbooks_locations_array) - 1) . '?'; + + // Query 1: Get col_gridsquare data with worked/confirmed status + $bindings1 = array_merge($this->logbooks_locations_array); + $bandCondition1 = ''; + + if ($band != 'All') { + if ($band == 'SAT') { + $bandCondition1 = " and log.col_prop_mode = ?"; + $bindings1[] = $band; + } else { + $bandCondition1 = " and log.col_prop_mode != ? and log.col_band = ?"; + $bindings1[] = 'SAT'; + $bindings1[] = $band; + } + } else { + $bandCondition1 = " and log.col_prop_mode != ?"; + $bindings1[] = 'SAT'; + } + + $sql1 = "SELECT + DISTINCT UPPER(SUBSTRING(col_gridsquare, 1, 4)) as gridsquare, + MAX(CASE WHEN (col_qsl_rcvd='Y' OR col_lotw_qsl_rcvd='Y') THEN 1 ELSE 0 END) as confirmed + FROM " . $this->config->item('table_name') . " log + INNER JOIN bands b ON (b.band = log.col_band) + WHERE log.station_id IN (" . $inPlaceholders . ") + AND log.col_gridsquare <> '' + AND b.bandgroup IN ('vhf','uhf','shf','sat')" + . $bandCondition1 . " + GROUP BY UPPER(SUBSTRING(col_gridsquare, 1, 4))"; + + $query1 = $this->db->query($sql1, $bindings1); + if ($query1->num_rows() > 0) { + $results['gridsquare'] = $query1->result_array(); + } + + // Query 2: Get col_vucc_grids data with worked/confirmed status + // Note: col_vucc_grids has NO band filter when band='All' (includes SAT) + $bindings2 = array_merge($this->logbooks_locations_array); + $bandCondition2 = ''; + + if ($band != 'All') { + if ($band == 'SAT') { + $bandCondition2 = " and col_prop_mode = ?"; + $bindings2[] = $band; + } else { + $bandCondition2 = " and col_prop_mode != ? and col_band = ?"; + $bindings2[] = 'SAT'; + $bindings2[] = $band; + } + } + // When band='All', NO band filter is added (includes all prop_mode including SAT) + + $sql2 = "SELECT + DISTINCT col_vucc_grids, + MAX(CASE WHEN (col_qsl_rcvd='Y' OR col_lotw_qsl_rcvd='Y') THEN 1 ELSE 0 END) as confirmed + FROM " . $this->config->item('table_name') . " + WHERE station_id IN (" . $inPlaceholders . ") + AND col_vucc_grids <> ''" + . $bandCondition2 . " + GROUP BY col_vucc_grids"; + + $query2 = $this->db->query($sql2, $bindings2); + if ($query2->num_rows() > 0) { + $results['vucc_grids'] = $query2->result_array(); + } + + return $results; + } + /* * Builds the array to display worked/confirmed vucc on dashboard page */ function fetchVuccSummary($band = 'All') { - $totalGridConfirmed = array(); - $totalGridWorked = array(); + // Use associative arrays for O(1) lookups instead of O(n) in_array() + $totalGridWorked = []; + $totalGridConfirmed = []; - // Getting all the worked grids - $col_gridsquare_worked = $this->get_vucc_summary($band, 'none'); + // Get combined data (2 queries instead of 4) + $data = $this->get_vucc_combined_data($band); - $workedGridArray = array(); - if ($col_gridsquare_worked != null) { - foreach ($col_gridsquare_worked as $workedgrid) { - array_push($workedGridArray, $workedgrid['gridsquare']); - if(!in_array($workedgrid['gridsquare'], $totalGridWorked)){ - array_push($totalGridWorked, $workedgrid['gridsquare']); - } - } - } - - $col_vucc_grids_worked = $this->get_vucc_summary_col_vucc($band, 'none'); - - if ($col_vucc_grids_worked != null) { - foreach ($col_vucc_grids_worked as $gridSplit) { - $grids = explode(",", $gridSplit['col_vucc_grids']); - foreach($grids as $key) { - $grid_four = strtoupper(substr(trim($key),0,4)); - - if(!in_array($grid_four, $workedGridArray)){ - array_push($workedGridArray, $grid_four); - } - - if(!in_array($grid_four, $totalGridWorked)){ - array_push($totalGridWorked, $grid_four); - } - } - } - } - - // Getting all the confirmed grids - $col_gridsquare_confirmed = $this->get_vucc_summary($band, 'both'); - $confirmedGridArray = array(); - - if ($col_gridsquare_confirmed != null) { - foreach ($col_gridsquare_confirmed as $confirmedgrid) { - array_push($confirmedGridArray, $confirmedgrid['gridsquare']); - if(!in_array($confirmedgrid['gridsquare'], $totalGridConfirmed)){ - array_push($totalGridConfirmed, $confirmedgrid['gridsquare']); - } - } - } - - $col_vucc_grids_confirmed = $this->get_vucc_summary_col_vucc($band, 'both'); - - if ($col_vucc_grids_confirmed != null) { - foreach ($col_vucc_grids_confirmed as $gridSplit) { - $grids = explode(",", $gridSplit['col_vucc_grids']); - foreach($grids as $key) { - $grid_four = strtoupper(substr(trim($key),0,4)); - - if(!in_array($grid_four, $confirmedGridArray)){ - array_push($confirmedGridArray, $grid_four); - } - - if(!in_array($grid_four, $totalGridConfirmed)){ - array_push($totalGridConfirmed, $grid_four); - } + // Process col_gridsquare data + if (!empty($data['gridsquare'])) { + foreach ($data['gridsquare'] as $row) { + $grid = $row['gridsquare']; + // Always add to worked + $totalGridWorked[$grid] = true; + // Add to confirmed if flagged + if ($row['confirmed']) { + $totalGridConfirmed[$grid] = true; + } + } + } + + // Process col_vucc_grids data + if (!empty($data['vucc_grids'])) { + foreach ($data['vucc_grids'] as $row) { + $grids = explode(",", $row['col_vucc_grids']); + foreach ($grids as $key) { + $grid_four = strtoupper(substr(trim($key), 0, 4)); + // Always add to worked + $totalGridWorked[$grid_four] = true; + // Add to confirmed if flagged + if ($row['confirmed']) { + $totalGridConfirmed[$grid_four] = true; } } } + } $vuccArray[$band]['worked'] = count($totalGridWorked); $vuccArray[$band]['confirmed'] = count($totalGridConfirmed); From 7df15b054e6fc3aa5691df951436926cfe1c179c Mon Sep 17 00:00:00 2001 From: int2001 Date: Wed, 4 Feb 2026 07:09:26 +0000 Subject: [PATCH 4/6] Combine Country-Breakdown --- application/controllers/Dashboard.php | 7 ++--- application/controllers/Visitor.php | 6 ++-- application/models/Logbook_model.php | 45 +++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/application/controllers/Dashboard.php b/application/controllers/Dashboard.php index e234cd7c4..1dd2c3092 100644 --- a/application/controllers/Dashboard.php +++ b/application/controllers/Dashboard.php @@ -111,13 +111,14 @@ class Dashboard extends CI_Controller { $data['current_streak']=0; } - // Load Countries Breakdown data into array - $CountriesBreakdown = $this->logbook_model->total_countries_confirmed($logbooks_locations_array); + // Load Countries Breakdown data into array (combined query) + $CountriesBreakdown = $this->logbook_model->total_countries_breakdown_batch($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']; + $current = $CountriesBreakdown['Countries_Current']; $QSLStatsBreakdownArray = $this->logbook_model->get_QSLStats($logbooks_locations_array); @@ -157,8 +158,6 @@ class Dashboard extends CI_Controller { $this->load->model('dxcc'); $dxcc = $this->dxcc->list_current(); - $current = $this->logbook_model->total_countries_current($logbooks_locations_array); - $footerData['scripts'] = [ 'assets/js/sections/dashboard.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/sections/dashboard.js")), ]; diff --git a/application/controllers/Visitor.php b/application/controllers/Visitor.php index 9f3779ab3..9ce5eacea 100644 --- a/application/controllers/Visitor.php +++ b/application/controllers/Visitor.php @@ -105,16 +105,16 @@ class Visitor extends CI_Controller { $data['user_map_custom'] = $this->optionslib->get_map_custom(true,$public_slug); - // Load Countries Breakdown data into array - $CountriesBreakdown = $this->logbook_model->total_countries_confirmed($logbooks_locations_array); + // Load Countries Breakdown data into array (combined query) + $CountriesBreakdown = $this->logbook_model->total_countries_breakdown_batch($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']; + $current = $CountriesBreakdown['Countries_Current']; $dxcc = $this->dxcc->list_current(); - $current = $this->logbook_model->total_countries_current($logbooks_locations_array); $data['total_countries_needed'] = count($dxcc->result()) - $current; $QSLStatsBreakdownArray =$this->logbook_model->get_QSLStats($logbooks_locations_array); diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 2a53b74aa..0c4f2ded4 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -4357,6 +4357,51 @@ class Logbook_model extends CI_Model { } } + /* Return combined countries breakdown: worked, confirmed (QSL/eQSL/LOTW), and current */ + function total_countries_breakdown_batch($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 (!empty($logbooks_locations_array)) { + $sql = "SELECT + COUNT(DISTINCT t.COL_DXCC) as Countries_Worked, + COUNT(DISTINCT IF(t.COL_QSL_RCVD = 'Y', t.COL_DXCC, NULL)) as Countries_Worked_QSL, + COUNT(DISTINCT IF(t.COL_EQSL_QSL_RCVD = 'Y', t.COL_DXCC, NULL)) as Countries_Worked_EQSL, + COUNT(DISTINCT IF(t.COL_LOTW_QSL_RCVD = 'Y', t.COL_DXCC, NULL)) as Countries_Worked_LOTW, + COUNT(DISTINCT IF(d.end IS NULL AND d.adif != 0, t.COL_DXCC, NULL)) as Countries_Current + FROM " . $this->config->item('table_name') . " t + LEFT JOIN dxcc_entities d ON d.adif = t.col_dxcc + WHERE t.station_id IN (" . str_repeat('?,', count($logbooks_locations_array) - 1) . "?) + AND t.COL_COUNTRY != 'Invalid' + AND t.COL_DXCC > 0"; + + $query = $this->db->query($sql, $logbooks_locations_array); + + if ($query->num_rows() > 0) { + $row = $query->row(); + return [ + '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 [ + '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() { $this->load->model('logbooks_model'); From d00c1876e5437ed79c946afe5ea30454591fbd62 Mon Sep 17 00:00:00 2001 From: int2001 Date: Wed, 4 Feb 2026 07:32:07 +0000 Subject: [PATCH 5/6] Combine dailystats with QSLs --- application/controllers/Dashboard.php | 54 ++++++++-------- application/controllers/Visitor.php | 32 +++++----- application/models/Logbook_model.php | 92 +++++++++++++++++++++++---- 3 files changed, 122 insertions(+), 56 deletions(-) diff --git a/application/controllers/Dashboard.php b/application/controllers/Dashboard.php index 1dd2c3092..6a0dca6de 100644 --- a/application/controllers/Dashboard.php +++ b/application/controllers/Dashboard.php @@ -111,38 +111,38 @@ class Dashboard extends CI_Controller { $data['current_streak']=0; } - // Load Countries Breakdown data into array (combined query) - $CountriesBreakdown = $this->logbook_model->total_countries_breakdown_batch($logbooks_locations_array); + // Load Dashboard stats (countries + QSL stats in one query) + $stats = $this->logbook_model->dashboard_stats_batch($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']; - $current = $CountriesBreakdown['Countries_Current']; + // Country stats + $data['total_countries'] = $stats['Countries_Worked']; + $data['total_countries_confirmed_paper'] = $stats['Countries_Worked_QSL']; + $data['total_countries_confirmed_eqsl'] = $stats['Countries_Worked_EQSL']; + $data['total_countries_confirmed_lotw'] = $stats['Countries_Worked_LOTW']; + $current = $stats['Countries_Current']; - $QSLStatsBreakdownArray = $this->logbook_model->get_QSLStats($logbooks_locations_array); + // QSL stats + $data['total_qsl_sent'] = $stats['QSL_Sent']; + $data['total_qsl_rcvd'] = $stats['QSL_Received']; + $data['total_qsl_requested'] = $stats['QSL_Requested']; + $data['qsl_sent_today'] = $stats['QSL_Sent_today']; + $data['qsl_rcvd_today'] = $stats['QSL_Received_today']; + $data['qsl_requested_today'] = $stats['QSL_Requested_today']; - $data['total_qsl_sent'] = $QSLStatsBreakdownArray['QSL_Sent']; - $data['total_qsl_rcvd'] = $QSLStatsBreakdownArray['QSL_Received']; - $data['total_qsl_requested'] = $QSLStatsBreakdownArray['QSL_Requested']; - $data['qsl_sent_today'] = $QSLStatsBreakdownArray['QSL_Sent_today']; - $data['qsl_rcvd_today'] = $QSLStatsBreakdownArray['QSL_Received_today']; - $data['qsl_requested_today'] = $QSLStatsBreakdownArray['QSL_Requested_today']; + $data['total_eqsl_sent'] = $stats['eQSL_Sent']; + $data['total_eqsl_rcvd'] = $stats['eQSL_Received']; + $data['eqsl_sent_today'] = $stats['eQSL_Sent_today']; + $data['eqsl_rcvd_today'] = $stats['eQSL_Received_today']; - $data['total_eqsl_sent'] = $QSLStatsBreakdownArray['eQSL_Sent']; - $data['total_eqsl_rcvd'] = $QSLStatsBreakdownArray['eQSL_Received']; - $data['eqsl_sent_today'] = $QSLStatsBreakdownArray['eQSL_Sent_today']; - $data['eqsl_rcvd_today'] = $QSLStatsBreakdownArray['eQSL_Received_today']; + $data['total_lotw_sent'] = $stats['LoTW_Sent']; + $data['total_lotw_rcvd'] = $stats['LoTW_Received']; + $data['lotw_sent_today'] = $stats['LoTW_Sent_today']; + $data['lotw_rcvd_today'] = $stats['LoTW_Received_today']; - $data['total_lotw_sent'] = $QSLStatsBreakdownArray['LoTW_Sent']; - $data['total_lotw_rcvd'] = $QSLStatsBreakdownArray['LoTW_Received']; - $data['lotw_sent_today'] = $QSLStatsBreakdownArray['LoTW_Sent_today']; - $data['lotw_rcvd_today'] = $QSLStatsBreakdownArray['LoTW_Received_today']; - - $data['total_qrz_sent'] = $QSLStatsBreakdownArray['QRZ_Sent']; - $data['total_qrz_rcvd'] = $QSLStatsBreakdownArray['QRZ_Received']; - $data['qrz_sent_today'] = $QSLStatsBreakdownArray['QRZ_Sent_today']; - $data['qrz_rcvd_today'] = $QSLStatsBreakdownArray['QRZ_Received_today']; + $data['total_qrz_sent'] = $stats['QRZ_Sent']; + $data['total_qrz_rcvd'] = $stats['QRZ_Received']; + $data['qrz_sent_today'] = $stats['QRZ_Sent_today']; + $data['qrz_rcvd_today'] = $stats['QRZ_Received_today']; $data['last_qso_count'] = empty($this->session->userdata('dashboard_last_qso_count')) ? DASHBOARD_DEFAULT_QSOS_COUNT : $this->session->userdata('dashboard_last_qso_count'); $data['last_qsos_list'] = $this->logbook_model->get_last_qsos( diff --git a/application/controllers/Visitor.php b/application/controllers/Visitor.php index 9ce5eacea..bde637c96 100644 --- a/application/controllers/Visitor.php +++ b/application/controllers/Visitor.php @@ -105,29 +105,29 @@ class Visitor extends CI_Controller { $data['user_map_custom'] = $this->optionslib->get_map_custom(true,$public_slug); - // Load Countries Breakdown data into array (combined query) - $CountriesBreakdown = $this->logbook_model->total_countries_breakdown_batch($logbooks_locations_array); + // Load Dashboard stats (countries + QSL stats in one query) + $stats = $this->logbook_model->dashboard_stats_batch($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']; - $current = $CountriesBreakdown['Countries_Current']; + // Country stats + $data['total_countries'] = $stats['Countries_Worked']; + $data['total_countries_confirmed_paper'] = $stats['Countries_Worked_QSL']; + $data['total_countries_confirmed_eqsl'] = $stats['Countries_Worked_EQSL']; + $data['total_countries_confirmed_lotw'] = $stats['Countries_Worked_LOTW']; + $current = $stats['Countries_Current']; $dxcc = $this->dxcc->list_current(); $data['total_countries_needed'] = count($dxcc->result()) - $current; - $QSLStatsBreakdownArray =$this->logbook_model->get_QSLStats($logbooks_locations_array); + // QSL stats + $data['total_qsl_sent'] = $stats['QSL_Sent']; + $data['total_qsl_rcvd'] = $stats['QSL_Received']; + $data['total_qsl_requested'] = $stats['QSL_Requested']; - $data['total_qsl_sent'] = $QSLStatsBreakdownArray['QSL_Sent']; - $data['total_qsl_rcvd'] = $QSLStatsBreakdownArray['QSL_Received']; - $data['total_qsl_requested'] = $QSLStatsBreakdownArray['QSL_Requested']; + $data['total_eqsl_sent'] = $stats['eQSL_Sent']; + $data['total_eqsl_rcvd'] = $stats['eQSL_Received']; - $data['total_eqsl_sent'] = $QSLStatsBreakdownArray['eQSL_Sent']; - $data['total_eqsl_rcvd'] = $QSLStatsBreakdownArray['eQSL_Received']; - - $data['total_lotw_sent'] = $QSLStatsBreakdownArray['LoTW_Sent']; - $data['total_lotw_rcvd'] = $QSLStatsBreakdownArray['LoTW_Received']; + $data['total_lotw_sent'] = $stats['LoTW_Sent']; + $data['total_lotw_rcvd'] = $stats['LoTW_Received']; $data['results'] = $this->logbook_model->get_qsos($this->qso_per_page,$this->uri->segment(3),$logbooks_locations_array); diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index 0c4f2ded4..c65267ef1 100644 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -4357,8 +4357,8 @@ class Logbook_model extends CI_Model { } } - /* Return combined countries breakdown: worked, confirmed (QSL/eQSL/LOTW), and current */ - function total_countries_breakdown_batch($StationLocationsArray = null) { + /* Return combined countries breakdown + QSL stats in one query */ + function dashboard_stats_batch($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')); @@ -4367,38 +4367,104 @@ class Logbook_model extends CI_Model { } if (!empty($logbooks_locations_array)) { + $todayStart = date('Y-m-d 00:00:00'); + $tomorrowStart = date('Y-m-d 00:00:00', strtotime('+1 day')); + $todayStartSql = $this->db->escape($todayStart); + $tomorrowStartSql = $this->db->escape($tomorrowStart); + + $location_list = "'" . implode("','", $logbooks_locations_array) . "'"; + $sql = "SELECT - COUNT(DISTINCT t.COL_DXCC) as Countries_Worked, - COUNT(DISTINCT IF(t.COL_QSL_RCVD = 'Y', t.COL_DXCC, NULL)) as Countries_Worked_QSL, - COUNT(DISTINCT IF(t.COL_EQSL_QSL_RCVD = 'Y', t.COL_DXCC, NULL)) as Countries_Worked_EQSL, - COUNT(DISTINCT IF(t.COL_LOTW_QSL_RCVD = 'Y', t.COL_DXCC, NULL)) as Countries_Worked_LOTW, - COUNT(DISTINCT IF(d.end IS NULL AND d.adif != 0, t.COL_DXCC, NULL)) as Countries_Current + -- Country stats (COUNT DISTINCT - filtered to valid DXCC only) + COUNT(DISTINCT CASE WHEN t.COL_COUNTRY != 'Invalid' AND t.COL_DXCC > 0 THEN t.COL_DXCC END) as Countries_Worked, + COUNT(DISTINCT CASE WHEN t.COL_QSL_RCVD = 'Y' AND t.COL_COUNTRY != 'Invalid' AND t.COL_DXCC > 0 THEN t.COL_DXCC END) as Countries_Worked_QSL, + COUNT(DISTINCT CASE WHEN t.COL_EQSL_QSL_RCVD = 'Y' AND t.COL_COUNTRY != 'Invalid' AND t.COL_DXCC > 0 THEN t.COL_DXCC END) as Countries_Worked_EQSL, + COUNT(DISTINCT CASE WHEN t.COL_LOTW_QSL_RCVD = 'Y' AND t.COL_COUNTRY != 'Invalid' AND t.COL_DXCC > 0 THEN t.COL_DXCC END) as Countries_Worked_LOTW, + COUNT(DISTINCT CASE WHEN d.end IS NULL AND d.adif != 0 AND t.COL_COUNTRY != 'Invalid' AND t.COL_DXCC > 0 THEN t.COL_DXCC END) as Countries_Current, + -- QSL stats (SUM - no filtering, all QSOs) + SUM(CASE WHEN t.COL_QSL_SENT = 'Y' THEN 1 ELSE 0 END) as QSL_Sent, + SUM(CASE WHEN t.COL_QSL_RCVD = 'Y' THEN 1 ELSE 0 END) as QSL_Received, + SUM(CASE WHEN t.COL_QSL_SENT IN ('Q', 'R') THEN 1 ELSE 0 END) as QSL_Requested, + SUM(CASE WHEN t.COL_EQSL_QSL_SENT = 'Y' THEN 1 ELSE 0 END) as eQSL_Sent, + SUM(CASE WHEN t.COL_EQSL_QSL_RCVD = 'Y' THEN 1 ELSE 0 END) as eQSL_Received, + SUM(CASE WHEN t.COL_LOTW_QSL_SENT = 'Y' THEN 1 ELSE 0 END) as LoTW_Sent, + SUM(CASE WHEN t.COL_LOTW_QSL_RCVD = 'Y' THEN 1 ELSE 0 END) as LoTW_Received, + SUM(CASE WHEN t.COL_QRZCOM_QSO_UPLOAD_STATUS = 'Y' THEN 1 ELSE 0 END) as QRZ_Sent, + SUM(CASE WHEN t.COL_QRZCOM_QSO_DOWNLOAD_STATUS = 'Y' THEN 1 ELSE 0 END) as QRZ_Received, + -- Today's stats (SUM - no filtering, all QSOs) + SUM(CASE WHEN t.COL_QSL_SENT = 'Y' AND t.COL_QSLSDATE >= " . $todayStartSql . " AND t.COL_QSLSDATE < " . $tomorrowStartSql . " THEN 1 ELSE 0 END) as QSL_Sent_today, + SUM(CASE WHEN t.COL_QSL_RCVD = 'Y' AND t.COL_QSLRDATE >= " . $todayStartSql . " AND t.COL_QSLRDATE < " . $tomorrowStartSql . " THEN 1 ELSE 0 END) as QSL_Received_today, + SUM(CASE WHEN t.COL_QSL_SENT IN ('Q', 'R') AND t.COL_QSLSDATE >= " . $todayStartSql . " AND t.COL_QSLSDATE < " . $tomorrowStartSql . " THEN 1 ELSE 0 END) as QSL_Requested_today, + SUM(CASE WHEN t.COL_EQSL_QSL_SENT = 'Y' AND t.COL_EQSL_QSLSDATE >= " . $todayStartSql . " AND t.COL_EQSL_QSLSDATE < " . $tomorrowStartSql . " THEN 1 ELSE 0 END) as eQSL_Sent_today, + SUM(CASE WHEN t.COL_EQSL_QSL_RCVD = 'Y' AND t.COL_EQSL_QSLRDATE >= " . $todayStartSql . " AND t.COL_EQSL_QSLRDATE < " . $tomorrowStartSql . " THEN 1 ELSE 0 END) as eQSL_Received_today, + SUM(CASE WHEN t.COL_LOTW_QSL_SENT = 'Y' AND t.COL_LOTW_QSLSDATE >= " . $todayStartSql . " AND t.COL_LOTW_QSLSDATE < " . $tomorrowStartSql . " THEN 1 ELSE 0 END) as LoTW_Sent_today, + SUM(CASE WHEN t.COL_LOTW_QSL_RCVD = 'Y' AND t.COL_LOTW_QSLRDATE >= " . $todayStartSql . " AND t.COL_LOTW_QSLRDATE < " . $tomorrowStartSql . " THEN 1 ELSE 0 END) as LoTW_Received_today, + SUM(CASE WHEN t.COL_QRZCOM_QSO_UPLOAD_STATUS = 'Y' AND t.COL_QRZCOM_QSO_UPLOAD_DATE >= " . $todayStartSql . " AND t.COL_QRZCOM_QSO_UPLOAD_DATE < " . $tomorrowStartSql . " THEN 1 ELSE 0 END) as QRZ_Sent_today, + SUM(CASE WHEN t.COL_QRZCOM_QSO_DOWNLOAD_STATUS = 'Y' AND t.COL_QRZCOM_QSO_DOWNLOAD_DATE >= " . $todayStartSql . " AND t.COL_QRZCOM_QSO_DOWNLOAD_DATE < " . $tomorrowStartSql . " THEN 1 ELSE 0 END) as QRZ_Received_today FROM " . $this->config->item('table_name') . " t LEFT JOIN dxcc_entities d ON d.adif = t.col_dxcc - WHERE t.station_id IN (" . str_repeat('?,', count($logbooks_locations_array) - 1) . "?) - AND t.COL_COUNTRY != 'Invalid' - AND t.COL_DXCC > 0"; + WHERE t.station_id IN (" . $location_list . ")"; - $query = $this->db->query($sql, $logbooks_locations_array); + $query = $this->db->query($sql); if ($query->num_rows() > 0) { $row = $query->row(); return [ + // Country stats '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 + 'Countries_Current' => $row->Countries_Current, + // QSL stats + 'QSL_Sent' => $row->QSL_Sent, + 'QSL_Received' => $row->QSL_Received, + 'QSL_Requested' => $row->QSL_Requested, + 'eQSL_Sent' => $row->eQSL_Sent, + 'eQSL_Received' => $row->eQSL_Received, + 'LoTW_Sent' => $row->LoTW_Sent, + 'LoTW_Received' => $row->LoTW_Received, + 'QRZ_Sent' => $row->QRZ_Sent, + 'QRZ_Received' => $row->QRZ_Received, + // Today's stats + 'QSL_Sent_today' => $row->QSL_Sent_today, + 'QSL_Received_today' => $row->QSL_Received_today, + 'QSL_Requested_today' => $row->QSL_Requested_today, + 'eQSL_Sent_today' => $row->eQSL_Sent_today, + 'eQSL_Received_today' => $row->eQSL_Received_today, + 'LoTW_Sent_today' => $row->LoTW_Sent_today, + 'LoTW_Received_today' => $row->LoTW_Received_today, + 'QRZ_Sent_today' => $row->QRZ_Sent_today, + 'QRZ_Received_today' => $row->QRZ_Received_today ]; } } + // Return zero values if no data return [ 'Countries_Worked' => 0, 'Countries_Worked_QSL' => 0, 'Countries_Worked_EQSL' => 0, 'Countries_Worked_LOTW' => 0, - 'Countries_Current' => 0 + 'Countries_Current' => 0, + '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 ]; } From 6a51ab5a398ffde9cceddc5b624770d5cb1e5469 Mon Sep 17 00:00:00 2001 From: int2001 Date: Wed, 4 Feb 2026 08:33:31 +0000 Subject: [PATCH 6/6] Remove profiler. Ready to review --- application/controllers/Dashboard.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/controllers/Dashboard.php b/application/controllers/Dashboard.php index 6a0dca6de..45b44c178 100644 --- a/application/controllers/Dashboard.php +++ b/application/controllers/Dashboard.php @@ -4,7 +4,6 @@ class Dashboard extends CI_Controller { public function index() { - $this->output->enable_profiler(TRUE); // Check if users logged in $this->load->model('user_model'); if ($this->user_model->validate_session() == 0) {