simplesitesearch 0.0.2.dev1__tar.gz → 0.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {simplesitesearch-0.0.2.dev1/simplesitesearch.egg-info → simplesitesearch-0.0.4}/PKG-INFO +68 -20
  2. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/README.md +62 -14
  3. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/pyproject.toml +6 -6
  4. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/setup.cfg +6 -6
  5. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/setup.py +2 -2
  6. simplesitesearch-0.0.4/simplesitesearch/__init__.py +1 -0
  7. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/simplesitesearch/templates/simplesitesearch/search_results.html +1 -1
  8. simplesitesearch-0.0.4/simplesitesearch/utils.py +153 -0
  9. simplesitesearch-0.0.4/simplesitesearch/views.py +111 -0
  10. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4/simplesitesearch.egg-info}/PKG-INFO +68 -20
  11. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/simplesitesearch.egg-info/SOURCES.txt +1 -0
  12. simplesitesearch-0.0.2.dev1/simplesitesearch/__init__.py +0 -3
  13. simplesitesearch-0.0.2.dev1/simplesitesearch/views.py +0 -151
  14. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/LICENSE +0 -0
  15. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/MANIFEST.in +0 -0
  16. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/simplesitesearch/cms_apps.py +0 -0
  17. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/simplesitesearch/templates/simplesitesearch/pagination.html +0 -0
  18. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/simplesitesearch/urls.py +0 -0
  19. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/simplesitesearch.egg-info/dependency_links.txt +0 -0
  20. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/simplesitesearch.egg-info/not-zip-safe +0 -0
  21. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/simplesitesearch.egg-info/requires.txt +0 -0
  22. {simplesitesearch-0.0.2.dev1 → simplesitesearch-0.0.4}/simplesitesearch.egg-info/top_level.txt +0 -0
@@ -1,16 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simplesitesearch
3
- Version: 0.0.2.dev1
3
+ Version: 0.0.4
4
4
  Summary: Reptile Simple Site Search django app
5
- Home-page: https://github.com/reptiletech/simplesitesearch
5
+ Home-page: https://github.com/FlavienLouis/simplesitesearch
6
6
  Author: Reptile Tech
7
7
  Author-email: Reptile Tech <flouis@reptile.tech>
8
8
  Maintainer-email: Reptile Tech <flouis@reptile.tech>
9
9
  License: MIT
10
- Project-URL: Homepage, https://github.com/reptiletech/simplesitesearch
11
- Project-URL: Documentation, https://github.com/reptiletech/simplesitesearch#readme
12
- Project-URL: Repository, https://github.com/reptiletech/simplesitesearch.git
13
- Project-URL: Bug Tracker, https://github.com/reptiletech/simplesitesearch/issues
10
+ Project-URL: Homepage, https://github.com/FlavienLouis/simplesitesearch
11
+ Project-URL: Documentation, https://github.com/FlavienLouis/simplesitesearch#readme
12
+ Project-URL: Repository, https://github.com/FlavienLouis/simplesitesearch.git
13
+ Project-URL: Bug Tracker, https://github.com/FlavienLouis/simplesitesearch/issues
14
14
  Keywords: django,search,cms,django-cms
15
15
  Classifier: Development Status :: 5 - Production/Stable
16
16
  Classifier: Intended Audience :: Developers
@@ -48,7 +48,6 @@ Dynamic: requires-python
48
48
 
49
49
  A simple Django app for site search functionality with Django CMS integration. This package provides a clean, easy-to-use search interface that can be integrated into Django CMS projects.
50
50
 
51
- > **Note**: This is a development version (0.0.2.dev1). The package is still under active development and may have breaking changes in future releases.
52
51
 
53
52
  ## Features
54
53
 
@@ -147,10 +146,53 @@ The search results support pagination with a `page` parameter:
147
146
  /search/?q=your+search+term&page=2
148
147
  ```
149
148
 
149
+ ### Tags filter
150
+
151
+ You can send a comma-separated list of tags to filter results. The `tag` (or `tags`) query parameter is forwarded to the API as `tags`:
152
+
153
+ ```
154
+ /search/?q=your+search+term&tag=news,blog,tutorial
155
+ ```
156
+
157
+ Pagination links preserve the tag filter.
158
+
150
159
  ### Honeypot Protection
151
160
 
152
161
  The search includes basic honeypot protection. If a `message` parameter is present, the search will not execute.
153
162
 
163
+ ## Utility functions (QOL)
164
+
165
+ The app provides helpers in `simplesitesearch.utils` for use in views, management commands, or other code.
166
+
167
+ | Function | Description |
168
+ |----------|-------------|
169
+ | **`get_search_results(term, current_page, tags=None)`** | Fetches search results from the API. Returns a dict with `total_hits` and `hits`. On error returns `{"total_hits": 0, "hits": []}`. |
170
+ | **`get_search_api_url(term, current_page, tags=None)`** | Builds the full search API URL (uses Django settings and current language). |
171
+ | **`build_search_query_string(term, page=1, tags=None)`** | Builds the query string for search URLs (e.g. pagination links), e.g. `?q=foo&page=2&tag=a,b`. |
172
+ | **`parse_comma_separated_tags(value)`** | Parses a comma-separated string into a list of stripped tags; returns `[]` if value is falsy. |
173
+ | **`normalize_search_term(term, max_words=10)`** | Trims and limits the search term to a maximum number of words. |
174
+ | **`safe_int(value, default=None)`** | Safely converts a value to `int`; returns `default` on failure. |
175
+
176
+ **Example — fetch results in code:**
177
+
178
+ ```python
179
+ from simplesitesearch.utils import get_search_results
180
+
181
+ data = get_search_results("django", current_page=1, tags=["cms", "tutorial"])
182
+ total = data["total_hits"]
183
+ results = data["hits"]
184
+ ```
185
+
186
+ **Example — build search/pagination URLs:**
187
+
188
+ ```python
189
+ from simplesitesearch.utils import build_search_query_string
190
+
191
+ # Pagination link for page 2 with tag filter
192
+ qs = build_search_query_string("my query", page=2, tags=["blog"])
193
+ # => "?q=my+query&page=2&tag=blog"
194
+ ```
195
+
154
196
  ## Customization
155
197
 
156
198
  ### Templates
@@ -239,23 +281,29 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
239
281
 
240
282
  ## Support
241
283
 
242
- For support and questions, please open an issue on the [GitHub repository](https://github.com/yourusername/simplesitesearch/issues).
284
+ For support and questions, please open an issue on the [GitHub repository](https://github.com/FlavienLouis/simplesitesearch/issues).
243
285
 
244
286
  ## Changelog
245
287
 
246
- ### 0.0.2.dev1
247
- - Updated Django CMS configuration instructions for clarity
248
- - Clarified Application ID setting (should be 'site_search')
249
- - Improved documentation flow and user experience
250
-
251
- ### 0.0.1.dev1
252
- - Initial development release
253
- - Django CMS integration
254
- - Pagination support
255
- - Multi-language support
256
- - Basic search functionality
288
+ ### 0.0.3
289
+ - Added `simplesitesearch.utils` QOL helpers: `get_search_results`, `get_search_api_url`, `build_search_query_string`, `parse_comma_separated_tags`, `normalize_search_term`, `safe_int`
290
+ - Tag filter: `tag` (or `tags`) query parameter forwarded to API as comma-separated `tags`; pagination links preserve tags
291
+ - README: Utility functions section and tags filter documentation
292
+
293
+ ### 0.0.2
294
+ - Fixed template include path in search_results.html
295
+ - Updated pagination template include to use full namespace: `simplesitesearch/pagination.html`
296
+ - Improved template isolation and namespace consistency
297
+
298
+ ### 0.0.1
299
+ - **First stable release**
300
+ - Django CMS integration with apphook support
301
+ - Pagination support for search results
302
+ - Multi-language support with Django i18n
303
+ - Basic search functionality with API integration
257
304
  - Template customization support
258
305
  - Support for Python 3.6+ and Django 2.2+
259
- - Updated API endpoint to use Reptile Search API
306
+ - Reptile Search API integration
307
+ - Comprehensive documentation and setup instructions
260
308
 
261
309
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  A simple Django app for site search functionality with Django CMS integration. This package provides a clean, easy-to-use search interface that can be integrated into Django CMS projects.
4
4
 
5
- > **Note**: This is a development version (0.0.2.dev1). The package is still under active development and may have breaking changes in future releases.
6
5
 
7
6
  ## Features
8
7
 
@@ -101,10 +100,53 @@ The search results support pagination with a `page` parameter:
101
100
  /search/?q=your+search+term&page=2
102
101
  ```
103
102
 
103
+ ### Tags filter
104
+
105
+ You can send a comma-separated list of tags to filter results. The `tag` (or `tags`) query parameter is forwarded to the API as `tags`:
106
+
107
+ ```
108
+ /search/?q=your+search+term&tag=news,blog,tutorial
109
+ ```
110
+
111
+ Pagination links preserve the tag filter.
112
+
104
113
  ### Honeypot Protection
105
114
 
106
115
  The search includes basic honeypot protection. If a `message` parameter is present, the search will not execute.
107
116
 
117
+ ## Utility functions (QOL)
118
+
119
+ The app provides helpers in `simplesitesearch.utils` for use in views, management commands, or other code.
120
+
121
+ | Function | Description |
122
+ |----------|-------------|
123
+ | **`get_search_results(term, current_page, tags=None)`** | Fetches search results from the API. Returns a dict with `total_hits` and `hits`. On error returns `{"total_hits": 0, "hits": []}`. |
124
+ | **`get_search_api_url(term, current_page, tags=None)`** | Builds the full search API URL (uses Django settings and current language). |
125
+ | **`build_search_query_string(term, page=1, tags=None)`** | Builds the query string for search URLs (e.g. pagination links), e.g. `?q=foo&page=2&tag=a,b`. |
126
+ | **`parse_comma_separated_tags(value)`** | Parses a comma-separated string into a list of stripped tags; returns `[]` if value is falsy. |
127
+ | **`normalize_search_term(term, max_words=10)`** | Trims and limits the search term to a maximum number of words. |
128
+ | **`safe_int(value, default=None)`** | Safely converts a value to `int`; returns `default` on failure. |
129
+
130
+ **Example — fetch results in code:**
131
+
132
+ ```python
133
+ from simplesitesearch.utils import get_search_results
134
+
135
+ data = get_search_results("django", current_page=1, tags=["cms", "tutorial"])
136
+ total = data["total_hits"]
137
+ results = data["hits"]
138
+ ```
139
+
140
+ **Example — build search/pagination URLs:**
141
+
142
+ ```python
143
+ from simplesitesearch.utils import build_search_query_string
144
+
145
+ # Pagination link for page 2 with tag filter
146
+ qs = build_search_query_string("my query", page=2, tags=["blog"])
147
+ # => "?q=my+query&page=2&tag=blog"
148
+ ```
149
+
108
150
  ## Customization
109
151
 
110
152
  ### Templates
@@ -193,23 +235,29 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
193
235
 
194
236
  ## Support
195
237
 
196
- For support and questions, please open an issue on the [GitHub repository](https://github.com/yourusername/simplesitesearch/issues).
238
+ For support and questions, please open an issue on the [GitHub repository](https://github.com/FlavienLouis/simplesitesearch/issues).
197
239
 
198
240
  ## Changelog
199
241
 
200
- ### 0.0.2.dev1
201
- - Updated Django CMS configuration instructions for clarity
202
- - Clarified Application ID setting (should be 'site_search')
203
- - Improved documentation flow and user experience
204
-
205
- ### 0.0.1.dev1
206
- - Initial development release
207
- - Django CMS integration
208
- - Pagination support
209
- - Multi-language support
210
- - Basic search functionality
242
+ ### 0.0.3
243
+ - Added `simplesitesearch.utils` QOL helpers: `get_search_results`, `get_search_api_url`, `build_search_query_string`, `parse_comma_separated_tags`, `normalize_search_term`, `safe_int`
244
+ - Tag filter: `tag` (or `tags`) query parameter forwarded to API as comma-separated `tags`; pagination links preserve tags
245
+ - README: Utility functions section and tags filter documentation
246
+
247
+ ### 0.0.2
248
+ - Fixed template include path in search_results.html
249
+ - Updated pagination template include to use full namespace: `simplesitesearch/pagination.html`
250
+ - Improved template isolation and namespace consistency
251
+
252
+ ### 0.0.1
253
+ - **First stable release**
254
+ - Django CMS integration with apphook support
255
+ - Pagination support for search results
256
+ - Multi-language support with Django i18n
257
+ - Basic search functionality with API integration
211
258
  - Template customization support
212
259
  - Support for Python 3.6+ and Django 2.2+
213
- - Updated API endpoint to use Reptile Search API
260
+ - Reptile Search API integration
261
+ - Comprehensive documentation and setup instructions
214
262
 
215
263
 
@@ -1,10 +1,10 @@
1
1
  [build-system]
2
- requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"]
2
+ requires = ["setuptools>=45", "wheel"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "simplesitesearch"
7
- dynamic = ["version"]
7
+ version = "0.0.4"
8
8
  description = "Reptile Simple Site Search django app"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -46,10 +46,10 @@ dependencies = [
46
46
  ]
47
47
 
48
48
  [project.urls]
49
- Homepage = "https://github.com/reptiletech/simplesitesearch"
50
- Documentation = "https://github.com/reptiletech/simplesitesearch#readme"
51
- Repository = "https://github.com/reptiletech/simplesitesearch.git"
52
- "Bug Tracker" = "https://github.com/reptiletech/simplesitesearch/issues"
49
+ Homepage = "https://github.com/FlavienLouis/simplesitesearch"
50
+ Documentation = "https://github.com/FlavienLouis/simplesitesearch#readme"
51
+ Repository = "https://github.com/FlavienLouis/simplesitesearch.git"
52
+ "Bug Tracker" = "https://github.com/FlavienLouis/simplesitesearch/issues"
53
53
 
54
54
  [tool.setuptools]
55
55
  packages = ["simplesitesearch"]
@@ -1,12 +1,12 @@
1
1
  [metadata]
2
2
  name = simplesitesearch
3
- version = 0.0.2.dev1
3
+ version = 0.0.4
4
4
  author = Reptile Tech
5
5
  author_email = flouis@reptile.tech
6
6
  description = Reptile Simple Site Search django app
7
7
  long_description = file: README.md
8
8
  long_description_content_type = text/markdown
9
- url = https://github.com/reptiletech/simplesitesearch
9
+ url = https://github.com/FlavienLouis/simplesitesearch
10
10
  license = MIT
11
11
  license_files = LICENSE
12
12
  classifiers =
@@ -33,10 +33,10 @@ classifiers =
33
33
  Topic :: Software Development :: Libraries :: Python Modules
34
34
  keywords = django search cms django-cms
35
35
  project_urls =
36
- Homepage = https://github.com/yourusername/simplesitesearch
37
- Documentation = https://github.com/yourusername/simplesitesearch#readme
38
- Repository = https://github.com/yourusername/simplesitesearch.git
39
- Bug Tracker = https://github.com/yourusername/simplesitesearch/issues
36
+ Homepage = https://github.com/FlavienLouis/simplesitesearch
37
+ Documentation = https://github.com/FlavienLouis/simplesitesearch#readme
38
+ Repository = https://github.com/FlavienLouis/simplesitesearch.git
39
+ Bug Tracker = https://github.com/FlavienLouis/simplesitesearch/issues
40
40
 
41
41
  [options]
42
42
  packages = find:
@@ -8,13 +8,13 @@ with open("README.md", "r", encoding="utf-8") as fh:
8
8
 
9
9
  setup(
10
10
  name="simplesitesearch",
11
- version="0.0.2.dev1",
11
+ version="0.0.4",
12
12
  author="Reptile Tech",
13
13
  author_email="flouis@reptile.tech",
14
14
  description="Reptile Simple Site Search django app",
15
15
  long_description=long_description,
16
16
  long_description_content_type="text/markdown",
17
- url="https://github.com/reptiletech/simplesitesearch",
17
+ url="https://github.com/FlavienLouis/simplesitesearch",
18
18
  packages=find_packages(),
19
19
  classifiers=[
20
20
  "Development Status :: 5 - Production/Stable",
@@ -0,0 +1 @@
1
+ __version__ = "0.0.4"
@@ -48,7 +48,7 @@
48
48
  </section>
49
49
  {% if page_links|length > 1 %}
50
50
  <section class="section section--nopadding section--nomargin">
51
- {% include "pagination.html" %}
51
+ {% include "simplesitesearch/pagination.html" %}
52
52
  </section>
53
53
  {% endif %}
54
54
 
@@ -0,0 +1,153 @@
1
+ """
2
+ Quality-of-life utilities for simplesitesearch.
3
+ """
4
+ from urllib.parse import quote_plus, urlencode
5
+
6
+ import requests
7
+
8
+ from django.conf import settings
9
+ from django.utils.translation import get_language
10
+
11
+
12
+ def parse_comma_separated_tags(value):
13
+ """
14
+ Parse a comma-separated string of tags into a list of stripped tags.
15
+
16
+ Args:
17
+ value: String (e.g. from request.GET) or None.
18
+
19
+ Returns:
20
+ List of non-empty tag strings. Empty list if value is falsy.
21
+
22
+ Example:
23
+ >>> parse_comma_separated_tags("news, blog, tutorial")
24
+ ['news', 'blog', 'tutorial']
25
+ >>> parse_comma_separated_tags(None)
26
+ []
27
+ """
28
+ if not value or not value.strip():
29
+ return []
30
+ return [t.strip() for t in value.split(",") if t.strip()]
31
+
32
+
33
+ def tags_to_query_value(tags):
34
+ """
35
+ Convert a list of tags to a comma-separated string for query params or API.
36
+
37
+ Args:
38
+ tags: Iterable of tag strings.
39
+
40
+ Returns:
41
+ Comma-separated string, or None if tags is empty.
42
+ """
43
+ if not tags:
44
+ return None
45
+ return ",".join(str(t).strip() for t in tags if str(t).strip())
46
+
47
+
48
+ def build_search_query_string(term, page=1, tags=None):
49
+ """
50
+ Build the query string for search URLs (pagination, prev/next).
51
+
52
+ Args:
53
+ term: Search term.
54
+ page: Page number (optional).
55
+ tags: Optional list of tags or comma-separated string; as `tag` param.
56
+
57
+ Returns:
58
+ Query string with leading "?", e.g. "?q=foo&page=2&tag=a,b".
59
+ """
60
+ params = []
61
+ if term:
62
+ params.append(("q", term))
63
+ if page and page != 1:
64
+ params.append(("page", page))
65
+ parsed = (
66
+ parse_comma_separated_tags(tags) if isinstance(tags, str) else (tags or [])
67
+ )
68
+ tag_value = tags_to_query_value(parsed)
69
+ if tag_value:
70
+ params.append(("tag", tag_value))
71
+ if not params:
72
+ return ""
73
+ return "?" + urlencode(params)
74
+
75
+
76
+ def normalize_search_term(term, max_words=10):
77
+ """
78
+ Limit search term to a maximum number of words (collapse whitespace).
79
+
80
+ Args:
81
+ term: Raw search string.
82
+ max_words: Maximum number of words to keep.
83
+
84
+ Returns:
85
+ Normalized string with at most max_words.
86
+ """
87
+ if not term or not term.strip():
88
+ return ""
89
+ return " ".join(term.split()[:max_words])
90
+
91
+
92
+ def safe_int(value, default=None):
93
+ """
94
+ Safely convert a value to int.
95
+
96
+ Args:
97
+ value: Value to convert (e.g. from request.GET).
98
+ default: Value to return on failure (default None).
99
+
100
+ Returns:
101
+ int, or default on failure.
102
+ """
103
+ if value is None:
104
+ return default
105
+ try:
106
+ return int(value)
107
+ except (TypeError, ValueError):
108
+ return default
109
+
110
+
111
+ def get_search_api_url(term, current_page, tags=None):
112
+ """
113
+ Build the full search API URL for the given term, page and optional tags.
114
+
115
+ Args:
116
+ term: Search term.
117
+ current_page: Page number (1-based).
118
+ tags: Optional list of tag strings; sent as comma-separated `tags` param.
119
+
120
+ Returns:
121
+ Full URL string for the search API.
122
+ """
123
+ base_url = settings.SITE_SEARCH_API_BASE_URL
124
+ site_key = settings.SITE_SEARCH_SITE_KEY
125
+ lang = get_language()
126
+ url = "%s%s?term=%s&lang=%s&page=%s" % (
127
+ base_url, site_key, quote_plus(term), lang, current_page
128
+ )
129
+ tag_value = tags_to_query_value(tags)
130
+ if tag_value:
131
+ url += "&tags=%s" % quote_plus(tag_value)
132
+ return url
133
+
134
+
135
+ def get_search_results(term, current_page, tags=None):
136
+ """
137
+ Fetch search results from the API for the given term, page and optional tags.
138
+
139
+ Args:
140
+ term: Search term.
141
+ current_page: Page number (1-based).
142
+ tags: Optional list of tag strings (comma-separated in API).
143
+
144
+ Returns:
145
+ Dict with "total_hits" and "hits" (list). On request or JSON error,
146
+ returns {"total_hits": 0, "hits": []}.
147
+ """
148
+ api_url = get_search_api_url(term, current_page, tags=tags)
149
+ try:
150
+ response = requests.get(api_url)
151
+ return response.json()
152
+ except Exception:
153
+ return {"total_hits": 0, "hits": []}
@@ -0,0 +1,111 @@
1
+ from math import floor
2
+
3
+ from django.views.generic import TemplateView
4
+
5
+ from .utils import (
6
+ build_search_query_string,
7
+ get_search_results,
8
+ get_search_api_url,
9
+ normalize_search_term,
10
+ parse_comma_separated_tags,
11
+ safe_int,
12
+ )
13
+
14
+
15
+ def get_page_links(pages_count, current_page, term, tags=None):
16
+ page_links = []
17
+
18
+ if pages_count > 1:
19
+ if current_page == 1:
20
+ from_page = 1
21
+ to_page = from_page + 7
22
+ elif current_page == pages_count + 1:
23
+ from_page = to_page = current_page
24
+ else:
25
+ from_page = current_page - 4
26
+ to_page = current_page + 5
27
+
28
+ if from_page < 1:
29
+ from_page = 1
30
+ if to_page > pages_count:
31
+ to_page = pages_count
32
+
33
+ for page in range(from_page, to_page + 1):
34
+ page_link = build_search_query_string(term, page=page, tags=tags)
35
+ page_links.append({"page": page, "url": page_link})
36
+
37
+ return page_links
38
+
39
+
40
+ def get_prev_next_links(next_page_number, prev_page_number, term, tags=None):
41
+ next_link = build_search_query_string(term, page=next_page_number, tags=tags) if next_page_number else None
42
+ prev_link = build_search_query_string(term, page=prev_page_number, tags=tags) if prev_page_number else None
43
+ return [prev_link, next_link]
44
+
45
+
46
+ def get_prev_next_page_number(pages_count, current_page):
47
+ current_page = safe_int(current_page, 1)
48
+ pages_count = safe_int(pages_count, 0)
49
+
50
+ prev_page_number = current_page - 1 if current_page > 1 else None
51
+ next_page_number = current_page + 1 if current_page < pages_count else None
52
+ return [prev_page_number, next_page_number]
53
+
54
+
55
+ def get_total_pages(total_hits):
56
+ pages_count = floor(total_hits / 10)
57
+ if total_hits % 10 > 0:
58
+ pages_count += 1
59
+ return pages_count
60
+
61
+
62
+ def get_api_re_path(term, current_page, tags=None):
63
+ """Build the search API URL (delegates to utils for consistency)."""
64
+ return get_search_api_url(term, current_page, tags=tags)
65
+
66
+
67
+ class SearchResult(TemplateView):
68
+
69
+ template_name = "simplesitesearch/search_results.html"
70
+
71
+ def get(self, request, *args, **kwargs):
72
+ context = self.get_context_data(**kwargs)
73
+
74
+ # get term and optional tag(s) from url params (tag can be comma-separated)
75
+ term = request.GET.get("q")
76
+ tag_param = request.GET.get("tag") or request.GET.get("tags")
77
+ tags_list = parse_comma_separated_tags(tag_param)
78
+ honeypot_message = request.GET.get("message")
79
+
80
+ current_page = safe_int(request.GET.get("page"), 1)
81
+
82
+ if term and not honeypot_message:
83
+ term = normalize_search_term(term)
84
+ response_data = get_search_results(
85
+ term, current_page, tags=tags_list or None
86
+ )
87
+ pages_count = get_total_pages(response_data["total_hits"])
88
+ prev_page_number, next_page_number = get_prev_next_page_number(pages_count, current_page)
89
+ prev_link, next_link = get_prev_next_links(
90
+ next_page_number, prev_page_number, term, tags=tags_list or None
91
+ )
92
+ page_links = get_page_links(pages_count, current_page, term, tags=tags_list or None)
93
+
94
+ context.update({
95
+ "pages_count": pages_count,
96
+ "current_page": current_page,
97
+ "results_count": response_data["total_hits"],
98
+ "prev_link": prev_link,
99
+ "next_link": next_link,
100
+ "page_links": page_links,
101
+ "results": response_data["hits"],
102
+ })
103
+ else:
104
+ context.update({"results": None})
105
+
106
+ context.update({
107
+ "query": term,
108
+ "tag": tag_param or "",
109
+ })
110
+
111
+ return self.render_to_response(context)
@@ -1,16 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simplesitesearch
3
- Version: 0.0.2.dev1
3
+ Version: 0.0.4
4
4
  Summary: Reptile Simple Site Search django app
5
- Home-page: https://github.com/reptiletech/simplesitesearch
5
+ Home-page: https://github.com/FlavienLouis/simplesitesearch
6
6
  Author: Reptile Tech
7
7
  Author-email: Reptile Tech <flouis@reptile.tech>
8
8
  Maintainer-email: Reptile Tech <flouis@reptile.tech>
9
9
  License: MIT
10
- Project-URL: Homepage, https://github.com/reptiletech/simplesitesearch
11
- Project-URL: Documentation, https://github.com/reptiletech/simplesitesearch#readme
12
- Project-URL: Repository, https://github.com/reptiletech/simplesitesearch.git
13
- Project-URL: Bug Tracker, https://github.com/reptiletech/simplesitesearch/issues
10
+ Project-URL: Homepage, https://github.com/FlavienLouis/simplesitesearch
11
+ Project-URL: Documentation, https://github.com/FlavienLouis/simplesitesearch#readme
12
+ Project-URL: Repository, https://github.com/FlavienLouis/simplesitesearch.git
13
+ Project-URL: Bug Tracker, https://github.com/FlavienLouis/simplesitesearch/issues
14
14
  Keywords: django,search,cms,django-cms
15
15
  Classifier: Development Status :: 5 - Production/Stable
16
16
  Classifier: Intended Audience :: Developers
@@ -48,7 +48,6 @@ Dynamic: requires-python
48
48
 
49
49
  A simple Django app for site search functionality with Django CMS integration. This package provides a clean, easy-to-use search interface that can be integrated into Django CMS projects.
50
50
 
51
- > **Note**: This is a development version (0.0.2.dev1). The package is still under active development and may have breaking changes in future releases.
52
51
 
53
52
  ## Features
54
53
 
@@ -147,10 +146,53 @@ The search results support pagination with a `page` parameter:
147
146
  /search/?q=your+search+term&page=2
148
147
  ```
149
148
 
149
+ ### Tags filter
150
+
151
+ You can send a comma-separated list of tags to filter results. The `tag` (or `tags`) query parameter is forwarded to the API as `tags`:
152
+
153
+ ```
154
+ /search/?q=your+search+term&tag=news,blog,tutorial
155
+ ```
156
+
157
+ Pagination links preserve the tag filter.
158
+
150
159
  ### Honeypot Protection
151
160
 
152
161
  The search includes basic honeypot protection. If a `message` parameter is present, the search will not execute.
153
162
 
163
+ ## Utility functions (QOL)
164
+
165
+ The app provides helpers in `simplesitesearch.utils` for use in views, management commands, or other code.
166
+
167
+ | Function | Description |
168
+ |----------|-------------|
169
+ | **`get_search_results(term, current_page, tags=None)`** | Fetches search results from the API. Returns a dict with `total_hits` and `hits`. On error returns `{"total_hits": 0, "hits": []}`. |
170
+ | **`get_search_api_url(term, current_page, tags=None)`** | Builds the full search API URL (uses Django settings and current language). |
171
+ | **`build_search_query_string(term, page=1, tags=None)`** | Builds the query string for search URLs (e.g. pagination links), e.g. `?q=foo&page=2&tag=a,b`. |
172
+ | **`parse_comma_separated_tags(value)`** | Parses a comma-separated string into a list of stripped tags; returns `[]` if value is falsy. |
173
+ | **`normalize_search_term(term, max_words=10)`** | Trims and limits the search term to a maximum number of words. |
174
+ | **`safe_int(value, default=None)`** | Safely converts a value to `int`; returns `default` on failure. |
175
+
176
+ **Example — fetch results in code:**
177
+
178
+ ```python
179
+ from simplesitesearch.utils import get_search_results
180
+
181
+ data = get_search_results("django", current_page=1, tags=["cms", "tutorial"])
182
+ total = data["total_hits"]
183
+ results = data["hits"]
184
+ ```
185
+
186
+ **Example — build search/pagination URLs:**
187
+
188
+ ```python
189
+ from simplesitesearch.utils import build_search_query_string
190
+
191
+ # Pagination link for page 2 with tag filter
192
+ qs = build_search_query_string("my query", page=2, tags=["blog"])
193
+ # => "?q=my+query&page=2&tag=blog"
194
+ ```
195
+
154
196
  ## Customization
155
197
 
156
198
  ### Templates
@@ -239,23 +281,29 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
239
281
 
240
282
  ## Support
241
283
 
242
- For support and questions, please open an issue on the [GitHub repository](https://github.com/yourusername/simplesitesearch/issues).
284
+ For support and questions, please open an issue on the [GitHub repository](https://github.com/FlavienLouis/simplesitesearch/issues).
243
285
 
244
286
  ## Changelog
245
287
 
246
- ### 0.0.2.dev1
247
- - Updated Django CMS configuration instructions for clarity
248
- - Clarified Application ID setting (should be 'site_search')
249
- - Improved documentation flow and user experience
250
-
251
- ### 0.0.1.dev1
252
- - Initial development release
253
- - Django CMS integration
254
- - Pagination support
255
- - Multi-language support
256
- - Basic search functionality
288
+ ### 0.0.3
289
+ - Added `simplesitesearch.utils` QOL helpers: `get_search_results`, `get_search_api_url`, `build_search_query_string`, `parse_comma_separated_tags`, `normalize_search_term`, `safe_int`
290
+ - Tag filter: `tag` (or `tags`) query parameter forwarded to API as comma-separated `tags`; pagination links preserve tags
291
+ - README: Utility functions section and tags filter documentation
292
+
293
+ ### 0.0.2
294
+ - Fixed template include path in search_results.html
295
+ - Updated pagination template include to use full namespace: `simplesitesearch/pagination.html`
296
+ - Improved template isolation and namespace consistency
297
+
298
+ ### 0.0.1
299
+ - **First stable release**
300
+ - Django CMS integration with apphook support
301
+ - Pagination support for search results
302
+ - Multi-language support with Django i18n
303
+ - Basic search functionality with API integration
257
304
  - Template customization support
258
305
  - Support for Python 3.6+ and Django 2.2+
259
- - Updated API endpoint to use Reptile Search API
306
+ - Reptile Search API integration
307
+ - Comprehensive documentation and setup instructions
260
308
 
261
309
 
@@ -7,6 +7,7 @@ setup.py
7
7
  simplesitesearch/__init__.py
8
8
  simplesitesearch/cms_apps.py
9
9
  simplesitesearch/urls.py
10
+ simplesitesearch/utils.py
10
11
  simplesitesearch/views.py
11
12
  simplesitesearch.egg-info/PKG-INFO
12
13
  simplesitesearch.egg-info/SOURCES.txt
@@ -1,3 +0,0 @@
1
- __version__ = '0.0.2.dev1'
2
-
3
-
@@ -1,151 +0,0 @@
1
- from math import floor
2
-
3
- import requests
4
- from django.conf import settings
5
- from django.utils.translation import get_language
6
- from django.views.generic import TemplateView
7
-
8
-
9
- def get_page_links(pages_count, current_page, term):
10
-
11
- page_links = []
12
-
13
- if pages_count > 1:
14
-
15
- if current_page == 1:
16
- from_page = 1
17
- to_page = from_page + 7
18
- if current_page == pages_count + 1:
19
- pass
20
- else:
21
- from_page = current_page - 4
22
- to_page = current_page + 5
23
-
24
- if from_page < 1:
25
- from_page = 1
26
-
27
- if to_page > pages_count:
28
- to_page = pages_count
29
-
30
- for page in range(from_page, to_page + 1):
31
- page_link = "?q=%s&page=%s" % (term, page)
32
-
33
- page_links.append({
34
- 'page': page,
35
- 'url': page_link
36
- })
37
-
38
- return page_links
39
-
40
-
41
- def get_prev_next_links(next_page_number, prev_page_number, term):
42
- next_link = None
43
- if next_page_number:
44
- next_link = "?q=%s&page=%s" % (term, next_page_number)
45
-
46
- prev_link = None
47
- if prev_page_number:
48
- prev_link = "?q=%s&page=%s" % (term, prev_page_number)
49
-
50
- return [prev_link, next_link]
51
-
52
-
53
- def get_prev_next_page_number(pages_count, current_page):
54
-
55
- current_page = int(current_page)
56
- pages_count - int(pages_count)
57
-
58
- if current_page > 1:
59
- prev_page_number = current_page - 1
60
- else:
61
- prev_page_number = None
62
-
63
- if current_page < pages_count:
64
- next_page_number = current_page + 1
65
- else:
66
- next_page_number = None
67
-
68
- return [prev_page_number, next_page_number]
69
-
70
-
71
- def get_total_pages(total_hits):
72
- pages_count = floor(total_hits / 10)
73
-
74
- modulo = total_hits % 10
75
- if modulo > 0:
76
- pages_count = pages_count + 1
77
-
78
- return pages_count
79
-
80
-
81
- def get_api_re_path(term, current_page):
82
- base_url = settings.SITE_SEARCH_API_BASE_URL
83
- site_key = settings.SITE_SEARCH_SITE_KEY
84
- lang = get_language()
85
-
86
- return "%s%s?term=%s&lang=%s&page=%s" % (base_url, site_key, term, lang, current_page)
87
-
88
-
89
- class SearchResult(TemplateView):
90
-
91
- template_name = "simplesitesearch/search_results.html"
92
-
93
- def get(self, request, *args, **kwargs):
94
- context = self.get_context_data(**kwargs)
95
-
96
- # get Term from url params
97
- term = request.GET.get('q', None)
98
- honeypot_message = request.GET.get('message', None)
99
-
100
- # get current page from url params
101
- current_page = int(request.GET.get('page', 1))
102
-
103
- if term and not honeypot_message:
104
-
105
- term = ' '.join(term.split()[:10])
106
-
107
- # get api URl depending on current page
108
- api_url = get_api_re_path(term, current_page)
109
-
110
- # get results from api
111
- response = requests.get(api_url, verify=False)
112
- # convert results to usable json
113
- try:
114
- response_data = response.json()
115
- except:
116
- response_data = {
117
- 'total_hits': 0,
118
- 'hits': []
119
- }
120
-
121
- # get pages count
122
- pages_count = get_total_pages(response_data['total_hits'])
123
-
124
- # get prev and next page numbers
125
- prev_page_number, next_page_number = get_prev_next_page_number(pages_count, current_page)
126
-
127
- # get prev and next btn links
128
- prev_link, next_link = get_prev_next_links(next_page_number, prev_page_number, term)
129
-
130
- # get pages links
131
- page_links = get_page_links(pages_count, current_page, term)
132
-
133
- context.update({
134
- 'pages_count': pages_count,
135
- 'current_page': current_page,
136
- 'results_count': response_data['total_hits'],
137
- 'prev_link': prev_link,
138
- 'next_link': next_link,
139
- 'page_links': page_links,
140
- 'results': response_data['hits']
141
- })
142
- else:
143
- context.update({
144
- 'results': None
145
- })
146
-
147
- context.update({
148
- 'query': term
149
- })
150
-
151
- return self.render_to_response(context)