From 4016b53bae7fe3605b000f35142263c6c653f6d9 Mon Sep 17 00:00:00 2001
From: parallels
Date: Tue, 20 Aug 2024 13:38:51 +1000
Subject: [PATCH 1/7] Add & migrate DB keywords->count field
---
application/config/migration.php | 2 +-
.../003_add_keywords_count_field.php | 22 +++++++++++++++++++
application/models/Project_keyword_model.php | 1 -
3 files changed, 23 insertions(+), 2 deletions(-)
create mode 100644 application/migrations/003_add_keywords_count_field.php
diff --git a/application/config/migration.php b/application/config/migration.php
index 397a47a7..1f12a8f6 100644
--- a/application/config/migration.php
+++ b/application/config/migration.php
@@ -2,5 +2,5 @@
$config['migration_enabled'] = TRUE;
$config['migration_type'] = 'sequential';
-$config['migration_version'] = 2;
+$config['migration_version'] = 3;
$config['migration_path'] = APPPATH . 'migrations/';
diff --git a/application/migrations/003_add_keywords_count_field.php b/application/migrations/003_add_keywords_count_field.php
new file mode 100644
index 00000000..c6315485
--- /dev/null
+++ b/application/migrations/003_add_keywords_count_field.php
@@ -0,0 +1,22 @@
+ array('type' => 'INT',
+ 'default' => '0',
+ 'after' => 'value')
+ );
+ $this->dbforge->add_column('keywords', $fields);
+ }
+
+ public function down()
+ {
+ $this->dbforge->drop_column('keywords', 'count');
+ }
+}
+
diff --git a/application/models/Project_keyword_model.php b/application/models/Project_keyword_model.php
index 742afc7f..67fafc94 100644
--- a/application/models/Project_keyword_model.php
+++ b/application/models/Project_keyword_model.php
@@ -3,7 +3,6 @@
class Project_keyword_model extends MY_Model {
-
}
/* End of file project_keyword.php */
From 385c71e1e2c5cd5bf8123907d9fd5cf8749a07ed Mon Sep 17 00:00:00 2001
From: parallels
Date: Thu, 5 Dec 2024 12:17:39 +1100
Subject: [PATCH 2/7] Drop redundant keywords from projects
application/migrations/004_drop_redundant_keywords_from_projects.php
---
application/config/migration.php | 2 +-
..._drop_redundant_keywords_from_projects.php | 52 +++++++++++++++++++
2 files changed, 53 insertions(+), 1 deletion(-)
create mode 100644 application/migrations/004_drop_redundant_keywords_from_projects.php
diff --git a/application/config/migration.php b/application/config/migration.php
index 1f12a8f6..185232aa 100644
--- a/application/config/migration.php
+++ b/application/config/migration.php
@@ -2,5 +2,5 @@
$config['migration_enabled'] = TRUE;
$config['migration_type'] = 'sequential';
-$config['migration_version'] = 3;
+$config['migration_version'] = 4;
$config['migration_path'] = APPPATH . 'migrations/';
diff --git a/application/migrations/004_drop_redundant_keywords_from_projects.php b/application/migrations/004_drop_redundant_keywords_from_projects.php
new file mode 100644
index 00000000..b3ab102d
--- /dev/null
+++ b/application/migrations/004_drop_redundant_keywords_from_projects.php
@@ -0,0 +1,52 @@
+db->query($sql);
+ }
+
+ public function down()
+ {
+ ;
+ }
+}
From d78ebd0084902733023f228d5fe0eed76c44e9d7 Mon Sep 17 00:00:00 2001
From: parallels
Date: Fri, 24 May 2024 09:40:13 +1000
Subject: [PATCH 3/7] New cron job to update DB keywords->count field
---
.../controllers/cron/Keywords_stats.php | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 application/controllers/cron/Keywords_stats.php
diff --git a/application/controllers/cron/Keywords_stats.php b/application/controllers/cron/Keywords_stats.php
new file mode 100644
index 00000000..e25a7789
--- /dev/null
+++ b/application/controllers/cron/Keywords_stats.php
@@ -0,0 +1,32 @@
+db->query($sql);
+
+ $sql = '
+ UPDATE keywords
+ JOIN
+ (SELECT pk.keyword_id as keyword_id, COUNT(pk.project_id) AS count
+ FROM project_keywords pk
+ GROUP BY 1
+ ) as sub
+ ON keywords.id = sub.keyword_id
+ SET keywords.count = sub.count ';
+ $this->db->query($sql);
+
+ }
+}
+
+
+/* End of file Keyword_stats.php */
+/* Location: ./application/controllers/cron/Keyword_stats.php */
+
From 4119a6532c7e8afbea81c0b77c633061b31a8428 Mon Sep 17 00:00:00 2001
From: parallels
Date: Thu, 23 May 2024 16:49:55 +1000
Subject: [PATCH 4/7] New functions in Project model to support keywords
display
---
application/models/Project_model.php | 79 ++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/application/models/Project_model.php b/application/models/Project_model.php
index 20c2e197..99ea6d56 100644
--- a/application/models/Project_model.php
+++ b/application/models/Project_model.php
@@ -363,6 +363,85 @@ function get_frozen_projects()
return $query->result();
}
+
+
+ public function get_projects_by_keywords_id($params)
+ {
+ // If calls to this function do not pass a project_type_description parameter,
+ // provide a sensible default
+
+ $keyword_id = $params['keywords_id'];
+ $offset = 0 + $params['offset'];
+ $limit = 0 + $params['limit'];
+
+ $sql = 'select p.id, p.title_prefix, p.title, COALESCE(p.date_catalog, "2001-01-01" ) as safe_release_date,
+ p.url_librivox, p.status, p.coverart_thumbnail, p.zip_url, p.zip_size, l.two_letter_code, l.language, p.url_forum, p.project_type
+ FROM keywords k
+ JOIN project_keywords pk ON (k.id = pk.keyword_id)
+ JOIN projects p on (pk.project_id = p.id)
+ JOIN languages l ON (l.id = p.language_id) ';
+ $sql .= 'WHERE k.id = ? ';
+
+
+ // It is likely that either $params['project_type_description'] will not exist as a key at all,
+ // or that if it does exist, it will contain the values of 'either','solo' or 'group', matching values passed
+ // from Javascript embedded in the search page footer. Of these values, only 'solo' matches a value actually to be
+ // found in the projects.project_type field.
+
+ if (array_key_exists('project_type_description', $params))
+ {
+ if ($params['project_type_description'] == 'group')
+ {
+ $sql .= 'AND p.project_type != "solo" ';
+ }
+ elseif ($params['project_type_description'] == 'solo')
+ {
+ $sql .= 'AND p.project_type = "solo" ';
+ }
+ }
+
+ if ($params['search_order'] == 'catalog_date')
+ {
+ $sql .= ' ORDER BY safe_release_date DESC ';
+ } else
+ {
+ $sql .= ' ORDER BY p.title ASC ';
+ }
+ $sql .= ' LIMIT ? , ? ';
+ $query = $this->db->query($sql, array($keyword_id, $offset, $limit));
+
+ return $query->result_array();
+
+ }
+
+
+ public function get_keywords_and_statistics_by_project($project_id)
+ {
+ $sql = '
+ SELECT keywords.id, keywords.value, keywords.count as keyword_count
+ FROM keywords
+ JOIN project_keywords
+ ON keywords.id = project_keywords.keyword_id
+ WHERE project_keywords.project_id = ?
+ ORDER BY keywords.count DESC' ;
+
+ $query = $this->db->query($sql, array($project_id));
+ if ($query->num_rows() > 0) return $query->result_array();
+ return '';
+ }
+
+ public function get_keywords_count_by_project($project_id)
+ {
+ $sql = '
+ SELECT COUNT(keywords.id) as keywords_number
+ FROM keywords
+ JOIN project_keywords
+ ON keywords.id = project_keywords.keyword_id
+ WHERE project_keywords.project_id = ?';
+
+ $query = $this->db->query($sql, array($project_id));
+ return $query->row();
+ }
From cc60a1d1db0912b14d859d92f48e03d7276f0205 Mon Sep 17 00:00:00 2001
From: parallels
Date: Thu, 16 May 2024 13:42:15 +1000
Subject: [PATCH 5/7] Display keywords details on catalog page
---
application/controllers/catalog/Page.php | 132 ++++++++++++++++++++++-
application/views/catalog/page.php | 11 +-
2 files changed, 140 insertions(+), 3 deletions(-)
diff --git a/application/controllers/catalog/Page.php b/application/controllers/catalog/Page.php
index 1fac57e3..e74ba541 100644
--- a/application/controllers/catalog/Page.php
+++ b/application/controllers/catalog/Page.php
@@ -61,6 +61,20 @@ public function index($slug)
$this->data['volunteers']->bc = $this->user_model->get($this->data['project']->person_bc_id);
$this->data['volunteers']->mc = $this->user_model->get($this->data['project']->person_mc_id);
$this->data['volunteers']->pl = $this->user_model->get($this->data['project']->person_pl_id);
+
+
+ // **** KEYWORDS ****//
+ $MAX_KEYWORDS_TO_DISPLAY_INITIALLY = 10;
+ $this->data['keywords_array'] = $this->project_model->get_keywords_and_statistics_by_project($this->data['project']->id);
+ $this->data['project']->has_long_keywords_list = $this->_has_long_keywords_list($this->data['project']->id, $MAX_KEYWORDS_TO_DISPLAY_INITIALLY);
+ $this->data['project']->formatted_keywords_string_primary = $this->_formatted_keywords_string($this->data['project']->id,
+ $this->data['project']->has_long_keywords_list,
+ $MAX_KEYWORDS_TO_DISPLAY_INITIALLY,
+ "primary_list");
+ $this->data['project']->formatted_keywords_string_secondary = $this->_formatted_keywords_string($this->data['project']->id,
+ $this->data['project']->has_long_keywords_list,
+ $MAX_KEYWORDS_TO_DISPLAY_INITIALLY,
+ "secondary_list");
// **** MISC ****//
$this->data['project']->project_type = (trim($this->data['project']->project_type) == 'solo') ? 'solo' : 'group'; //keep on top - other values dependent on solo/group
@@ -170,7 +184,123 @@ function _genre_list($project_id)
return implode(', ', $genres);
}
-
+
+ function _formatted_keywords_string($project_id, $project_has_long_keywords_list,
+ $max_keywords_to_display_initially, $list_variant)
+ {
+ /** The catalog page will contain one, or possibly two lists of keywords.
+ It will always contain a primary list.
+ If the project has more keywords than can conveniently be fitted in the primary list:
+ (a) We truncate this primary list
+ (b) We also output to the page a secondary list, iniitially hidden, that includes all the keywords for the project
+ (c) We add to the primary list a "Show full list" link which, if clicked, hides the primary list
+ and shows the secondary list
+ */
+
+ $return_value = "";
+
+ switch ($list_variant) {
+ case "primary_list":
+ if ($project_has_long_keywords_list)
+ {
+ $return_value = $this->_formatted_truncated_keywords_string($project_id, $max_keywords_to_display_initially);
+ }
+ else
+ {
+ $return_value = $this->_formatted_full_keywords_string($project_id);
+ }
+ break;
+ case "secondary_list";
+ if ($project_has_long_keywords_list)
+ {
+ $return_value = $this->_formatted_full_keywords_string($project_id);
+ }
+ break;
+ default:
+ $return_value = "";
+ }
+ return $return_value;
+ }
+
+
+ function _formatted_full_keywords_string($project_id)
+ {
+ $return_value = '';
+ $keywords_array = $this->data['keywords_array'];
+ if (!empty($keywords_array))
+ {
+ foreach ($keywords_array as $key => $row)
+ {
+ // Add hyperlink only if at least two projects use this keyword
+ if ((int)$row['keyword_count'] > 1)
+ {
+ $return_value .= '';
+ }
+ $return_value .= $row['value'];
+ if ((int)$row['keyword_count'] > 1)
+ {
+ $return_value .= '';
+ }
+ $return_value .= ' (' . $row['keyword_count'] . '), ';
+ }
+ }
+ else
+ {
+ return $return_value;
+ }
+ // trim off final comma and trailing space
+ $return_value = substr($return_value, 0, -2);
+ return $return_value;
+ }
+
+ function _formatted_truncated_keywords_string($project_id, $max_keywords_to_display_initially)
+ {
+ $return_value = '';
+ $keywords_array = $this->data['keywords_array'];
+ if (!empty($keywords_array))
+ {
+ $i = 0;
+ foreach ($keywords_array as $key => $row)
+ {
+
+ // Add hyperlink only if at least two projects use this keyword
+ if ((int)$row['keyword_count'] > 1)
+ {
+ $return_value .= '';
+ }
+ $return_value .= $row['value'];
+ if ((int)$row['keyword_count'] > 1)
+ {
+ $return_value .= '';
+ }
+ $return_value .= ' (' . $row['keyword_count'] . '), ';
+ $i++;
+ if ($i == $max_keywords_to_display_initially)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ return $return_value;
+ }
+ // trim off final comma and trailing space
+ $return_value = substr($return_value, 0, -2);
+
+ // Add "Show full list" link
+ $return_value .= " ... [Show full list]";
+
+ return $return_value;
+ }
+
+ function _has_long_keywords_list($project_id, $max_keywords_to_display_initially)
+ {
+ $keywords_number = $this->project_model->get_keywords_count_by_project($project_id)->keywords_number;
+ return $keywords_number > $max_keywords_to_display_initially;
+ }
+
+
function _language($language_id)
{
$this->load->model('language_model');
diff --git a/application/views/catalog/page.php b/application/views/catalog/page.php
index c1c07a49..d385c0da 100644
--- a/application/views/catalog/page.php
+++ b/application/views/catalog/page.php
@@ -32,11 +32,18 @@
Genre(s): = $project->genre_list; ?>
Language: = $project->language; ?>
+
+ formatted_keywords_string_primary)): ?>
+ Keyword(s): = $project->formatted_keywords_string_primary; ?>
+
+ formatted_keywords_string_secondary)): ?>
+ Keyword(s): = $project->formatted_keywords_string_secondary; ?>
+
group)):?>
Group: = $project->group->group_name; ?>
-
+
@@ -86,4 +93,4 @@
-= $footer; ?>
\ No newline at end of file
+= $footer; ?>
From be75d374d2ca3a01577ea92333441de5a9fc3f0d Mon Sep 17 00:00:00 2001
From: parallels
Date: Thu, 23 May 2024 17:03:55 +1000
Subject: [PATCH 6/7] Display filterable, sortable list of projects that use
specific keywords
---
application/config/routes.php | 4 +-
application/controllers/catalog/Keywords.php | 108 ++++++++++++++++++
application/views/catalog/keywords.php | 39 +++++++
application/views/catalog/partials/footer.php | 12 +-
4 files changed, 161 insertions(+), 2 deletions(-)
create mode 100644 application/controllers/catalog/Keywords.php
create mode 100644 application/views/catalog/keywords.php
diff --git a/application/config/routes.php b/application/config/routes.php
index a5ba9d03..b9f322e7 100644
--- a/application/config/routes.php
+++ b/application/config/routes.php
@@ -121,6 +121,8 @@
$route['group/(:num)'] = "catalog/group/index/$1";
$route['group/get_results'] = "catalog/group/get_results";
+$route['keywords/(:num)'] = "catalog/keywords/index/$1";
+$route['keywords/get_results'] = "catalog/keywords/get_results";
$route['sections/readers/(:num)'] = 'catalog/sections/readers/$1';
@@ -166,4 +168,4 @@
$route['(:any)'] = "catalog/page/index/$1";
/* End of file routes.php */
-/* Location: ./application/config/routes.php */
\ No newline at end of file
+/* Location: ./application/config/routes.php */
diff --git a/application/controllers/catalog/Keywords.php b/application/controllers/catalog/Keywords.php
new file mode 100644
index 00000000..a454ed1a
--- /dev/null
+++ b/application/controllers/catalog/Keywords.php
@@ -0,0 +1,108 @@
+load->helper('general_functions_helper');
+ }
+
+ public function index($keywords_id)
+ {
+ if (empty($keywords_id)) {
+ show_404();
+ }
+ $this->load->model('keyword_model');
+ $this->data['keywords'] = $this->keyword_model->get($keywords_id);
+ $this->data['search_category'] = 'keywords';
+ $this->data['primary_key'] = $keywords_id;
+ $this->data['search_order'] = $this->input->get('search_order');
+ $params['keywords_id'] = $keywords_id;
+ $params['offset'] = 0;
+ $params['limit'] = 1000000;
+ $params['search_order'] = $this->input->get('search_order');
+ $matches = $this->_get_projects_by_keywords_id($params);
+ $this->data['matches'] = count($matches);
+ $this->_render('catalog/keywords');
+ return;
+ }
+
+ function get_results()
+ {
+
+ // collect - search_category, sub_category, page_number, sort_order
+ // and (sometimes) information about contents of project_type
+ $input = $this->input->get(null, true);
+
+ $params['keywords_id'] = $input['primary_key'];
+ $params['search_order'] = $input['search_order'];
+
+ if (empty($params['keywords_id'])) {
+ show_error('A primary_key (keywords ID) must be supplied', 400);
+ }
+
+ //format offset
+ $params['offset'] = ($input['search_page'] - 1) * CATALOG_RESULT_COUNT;
+
+ // format limit
+ $params['limit'] = CATALOG_RESULT_COUNT;
+
+
+
+ // format project_type_description. Note that values appearing here are not
+ // necessarily a match to values that appear in the projects.project_type column
+ // in the database - so we'll need to massage them later (in Project model->get_projects_by_keywords_id()).
+ if (array_key_exists('project_type', $input))
+ {
+ $params['project_type_description'] = $input['project_type'];
+ } else
+ {
+ $params['project_type_description'] = 'either';
+ }
+
+ // go get results
+ $results = $this->_get_projects_by_keywords_id($params);
+
+ // get full set by calling same function with different parameters
+ $params['offset'] = 0;
+ $params['limit'] = 1000000;
+
+ $full_set = $this->_get_projects_by_keywords_id($params);
+
+ // go format results
+ $retval['results'] = $this->_format_results($results, 'title');
+
+ //pagination
+ $page_count = (count($full_set) > CATALOG_RESULT_COUNT) ? ceil(count($full_set) / CATALOG_RESULT_COUNT) : 0;
+ $retval['pagination'] = (empty($page_count)) ? '' : $this->_format_pagination($input['search_page'], $page_count);
+
+ $retval['status'] = 'SUCCESS';
+
+ //return - results, pagination
+ if ($this->input->is_ajax_request())
+ {
+ header('Content-Type: application/json;charset=utf-8');
+ echo json_encode($retval);
+ return;
+ }
+ }
+
+ function _get_projects_by_keywords_id($params)
+ {
+ $this->load->model('project_model');
+ $projects = $this->project_model->get_projects_by_keywords_id($params);
+
+ foreach ($projects as $key => $project)
+ {
+ $projects[$key]['author_list'] = $this->_author_list($project['id']);
+ }
+
+ return $projects;
+ }
+}
diff --git a/application/views/catalog/keywords.php b/application/views/catalog/keywords.php
new file mode 100644
index 00000000..3eb1a9f1
--- /dev/null
+++ b/application/views/catalog/keywords.php
@@ -0,0 +1,39 @@
+= $header; ?>
+
+
+
+= $footer; ?>
+
+
+
+