From d445d633b4de28853fecfdbe999d01ab96ac9e7e Mon Sep 17 00:00:00 2001 From: Miguel Astor Date: Mon, 21 Aug 2023 21:20:27 -0400 Subject: [PATCH] Added search function. --- home/models.py | 3 - viewer/models.py | 3 - viewer/static/css/styles.css | 9 ++ viewer/static/imgs/find.png | Bin 0 -> 16697 bytes viewer/templates/gallery_view.html | 140 ++++++++++++++++++++--------- viewer/templates/image_view.html | 12 +++ viewer/views.py | 121 +++++++++++++++++++------ 7 files changed, 211 insertions(+), 77 deletions(-) delete mode 100644 home/models.py delete mode 100644 viewer/models.py create mode 100755 viewer/static/imgs/find.png diff --git a/home/models.py b/home/models.py deleted file mode 100644 index 71a8362..0000000 --- a/home/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/viewer/models.py b/viewer/models.py deleted file mode 100644 index 71a8362..0000000 --- a/viewer/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/viewer/static/css/styles.css b/viewer/static/css/styles.css index 47131ed..76e506b 100644 --- a/viewer/static/css/styles.css +++ b/viewer/static/css/styles.css @@ -52,6 +52,15 @@ margin-left: 2em; } +.search-btn { + border: none; + background-color: #00000000; +} + +.search-box { + height: 2em; +} + /**************************************************************************** * Grid. * ****************************************************************************/ diff --git a/viewer/static/imgs/find.png b/viewer/static/imgs/find.png new file mode 100755 index 0000000000000000000000000000000000000000..6be3b3712b6fa6975be5f5ecafc13b82b2e3ad35 GIT binary patch literal 16697 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 + - {% if images|length > 0 %} + + + + + {% if not search %} + {% endif %} + + + + {% if num_pages > 1 %} {% endif %}

- {{request.path}} - Files ({{num_files}}) + {% if not search %} + {{request.path}} - Files: {{num_files}} + {% else %} + Search: {{search_text}} - Files: {{num_files}} + {% endif %}

- {% for page in pages %}{{page}}{% if not forloop.last %} {% endif %}{% endfor %} + {% for page in pages %}{{page}}{% if not forloop.last %} {% endif %}{% endfor %}
-
+ + + {% if not search %} +
- {% if page != 1 %} - - {% endif %} - - {% if page != num_pages %} - - {% endif %}
- - - + + +

+ Search: +

- - - {% for row in images %} - - {% for cell in row %} - - {% endfor %} - - {% endfor %} - -
- - -
- - {{cell.path|truncatechars:15}} - -
-
+ + +
+ - - - + + + +
-
+ {% endif %} + + + {% if images|length > 0 %} + + + + {% if page != 1 %} + + {% endif %} + + + + + + {% if page != num_pages %} + + {% endif %} + +
+ + + + + + + {% for row in images %} + + {% for image in row %} + + {% endfor %} + + {% endfor %} + +
+ + + + +
+ + + + {{image.name|truncatechars:15}} + +
+
+
+ + + +
+ {% endif %} + {% if subdirs|length > 0 %} +

Sub-directories

+ +
{% for row in subdirs %} - {% for cell in row %} + {% for subdir in row %} diff --git a/viewer/templates/image_view.html b/viewer/templates/image_view.html index d68c8f4..b86543f 100644 --- a/viewer/templates/image_view.html +++ b/viewer/templates/image_view.html @@ -8,14 +8,19 @@ + +
- {% if request.path == '/gallery/' %} - - {% else %} - - {% endif %} +
- {{cell.name}} + {{subdir.name}}
+ + +
@@ -23,8 +28,11 @@
+ + + + + + +
{% if prev %} @@ -32,6 +40,8 @@ {% endif %} {% if next %} diff --git a/viewer/views.py b/viewer/views.py index aa32be3..dae28bf 100644 --- a/viewer/views.py +++ b/viewer/views.py @@ -6,10 +6,11 @@ from math import ceil import filetype # Django imports. -from django.http import HttpResponseNotFound -from django.conf import settings -from django.shortcuts import (render, - redirect) +from django.http import HttpResponseNotFound +from django.conf import settings +from django.utils.http import urlencode +from django.shortcuts import (render, + redirect) # Project imports. from .utils import make_thumbnail @@ -26,6 +27,31 @@ IMAGES_PER_PAGE = CELLS_PER_ROW * ROWS_PER_PAGE # View functions. # ########################################################################################### +def do_recursive_search(start_path, query): + """ + Gets all images and sub-directories inside the start_path whose name matches the given query, + and then joins the results recursively by iterating in each sub-directory. + """ + + # Get all sub-dirs and images that match the query. + subdirs = sorted([i.absolute() for i in start_path.iterdir() if i.is_dir() and query.lower() in i.name.lower()]) + images = sorted([i for i in start_path.iterdir() if i.is_file() and filetype.is_image(str(i)) and query.lower() in i.name.lower()]) + + # For all sub-directories, regardless of the query. + for subdir in sorted([i for i in start_path.iterdir() if i.is_dir()]): + # Do a recursive search. + rec_subdirs, rec_images = do_recursive_search(subdir, query.lower()) + + # Join the results if any. + subdirs.extend(rec_subdirs) + images.extend(rec_images) + + return subdirs, images + +########################################################################################### +# View functions. # +########################################################################################### + def gallery_view(request, path = None): """ Shows a list of subdirectories and image files inside the given path. @@ -33,6 +59,10 @@ def gallery_view(request, path = None): """ def list2rows(lst, cells = CELLS_PER_ROW): + """ + Converts a list into a matrix with the given amount of cells per row. + """ + rows = [] i = 0 while i < len(lst): @@ -46,64 +76,101 @@ def gallery_view(request, path = None): i += cells return rows + # Get the absolute path to show if any, else show the base gallery path. full_path = settings.GALLERY_ROOT.joinpath(path) if path is not None else settings.GALLERY_ROOT - if full_path.exists(): - if full_path.is_dir(): - subdirs = sorted([i for i in full_path.iterdir() if i.is_dir()]) - images = sorted([i for i in full_path.iterdir() if i.is_file() and filetype.is_image(str(i))]) - img_data = [] - num_pages = ceil((len(images) / CELLS_PER_ROW) / ROWS_PER_PAGE) + # Get the search query from the request, if any. + search = request.GET.get('search', default = '') + if full_path.exists(): + # If the path exists then check if it points to a directory or an image. + if full_path.is_dir(): + if search == '': + # If there is no search query then get all images and sub-directories of the current path. + subdirs = sorted([i for i in full_path.iterdir() if i.is_dir()]) + images = sorted([i for i in full_path.iterdir() if i.is_file() and filetype.is_image(str(i))]) + + else: + # If there is a search query then search the current directory recursively. + subdirs, images = do_recursive_search(full_path, search) + + # For every sub-directory found, prepare it's name and path for rendering. + subdir_data = [] + for subdir in subdirs: + subdir_data.append({ + 'path': '/gallery/' + str(subdir.relative_to(settings.GALLERY_ROOT)), + 'name': subdir.name + }) + + # Get the page number to show, if any. Default to the first one if unavailable or error. try: page = int(request.GET.get('page', 1)) except ValueError: page = 1 + # Compute the page offset to show. + num_pages = ceil((len(images) / CELLS_PER_ROW) / ROWS_PER_PAGE) page_offset = IMAGES_PER_PAGE * (page - 1) + # Slice the images found with the page offset and prepare them for rendering. + img_data = [] for image in images[page_offset : page_offset + IMAGES_PER_PAGE]: + # Create thumbnails as needed. make_thumbnail(image) - img_context = { - 'path': image.name, + # Get the path of the image relative to the gallery root. + rel_path = image.relative_to(settings.GALLERY_ROOT) + + # For each image, prepare it's path, thumbnail path and name for rendering. + img_data.append({ + 'path': '/gallery/' + str(rel_path), 'name': image.name, - 'thumbnail': '/thumbs/' + path + '/' + image.name if path is not None else '/thumbs/' + image.name - } - - img_data.append(img_context) - - print(page) + 'thumbnail': '/thumbs/' + str(rel_path) + }) + # Rendering context. context = { - 'path': path, - 'subdirs': list2rows(subdirs), - 'images': list2rows(img_data), - 'pages': range(1, num_pages + 1), - 'num_files': len(images), - 'page': page, - 'prev_page': page - 1, - 'next_page': page + 1, - 'num_pages': num_pages + 'path': path, + 'subdirs': list2rows(subdir_data), + 'images': list2rows(img_data), + 'pages': range(1, num_pages + 1), + 'num_files': len(images), + 'page': page, + 'prev_page': page - 1, + 'next_page': page + 1, + 'num_pages': num_pages, + 'search': urlencode({'search': search}) if search != '' else None, + 'search_text': search } + # Glorious success! return render(request, 'gallery_view.html', context) else: + # If the path points to an image, then get it's real path, and parent directory path. image = Path('/imgs/').joinpath(path) img_dir = settings.GALLERY_ROOT.joinpath(path).parent + + # Then get all sibling images. images = sorted([i.name for i in img_dir.iterdir() if i.is_file() and filetype.is_image(str(i))]) + + # Get the current image's index in it's directory. index = images.index(image.name) + + # Get the previous and next image indices. previous = index - 1 if index > 0 else None following = index + 1 if index < len(images) - 1 else None + # Set the current, previous and next images as the rendering context. context = { 'image_path': image, 'prev': images[previous] if previous is not None else None, 'next': images[following] if following is not None else None } + # Glorious success! return render(request, 'image_view.html', context) else: + # 404 if the path wasn't found. return HttpResponseNotFound('Not found')