Skip to content
142 changes: 132 additions & 10 deletions web-app/django/VIM/apps/instruments/views/instrument_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,30 @@ def get_context_data(self, **kwargs):
(x["name"] for x in hbs_facet_list if x["value"] == hbs_facet), ""
)

# Add sort data to context
sort_order = self.request.GET.get("sort", "").lower()
if sort_order not in ("asc", "desc"):
sort_order = None

context["sort"] = sort_order
context["sort_list"] = [
{"value": value, "url": None} for value in ("asc", "desc", "default")
]

# Add combined filter state for UI
context["has_filters"] = bool(search_query or hbs_facet)
context["has_filters"] = bool(search_query or hbs_facet or sort_order)
context["active_filters"] = {
"search": search_query if search_query else None,
"hbs_classification": (
{"value": hbs_facet, "name": context.get("hbs_facet_name", "")}
if hbs_facet
else None
),
"sort": (
"Ascending"
if sort_order == "asc"
else "Descending" if sort_order == "desc" else None
),
}

# Add URL building helpers for preserving search query in HBS facet links
Expand All @@ -185,24 +200,102 @@ def get_context_data(self, **kwargs):
enhanced_hbs_facets = []
for facet in hbs_facet_list:
facet_copy = facet.copy()
# Build URL that preserves search query (with proper encoding)

# Build base params list
params = []
clear_params = []

if search_query:
encoded_query = requests.utils.quote(search_query)
facet_copy["url"] = f"?query={encoded_query}&hbs_facet={facet['value']}"
facet_copy["clear_url"] = f"?query={encoded_query}"
else:
facet_copy["url"] = f"?hbs_facet={facet['value']}"
facet_copy["clear_url"] = "?"
params.append(f"query={requests.utils.quote(search_query)}")
clear_params.append(f"query={requests.utils.quote(search_query)}")

if sort_order:
params.append(f"sort={sort_order}")

# Current facet is added to main URL
params.append(f"hbs_facet={facet['value']}")

facet_copy["url"] = "?" + "&".join(params)
facet_copy["clear_url"] = (
"?" + "&".join(clear_params) if clear_params else "?"
)

# Add active state for current HBS filter
facet_copy["is_active"] = facet["value"] == hbs_facet
enhanced_hbs_facets.append(facet_copy)
context["hbs_facets"] = enhanced_hbs_facets

# Add clear filter URLs for UI
context["clear_search_url"] = f"?hbs_facet={hbs_facet}" if hbs_facet else "?"
params = {}
if search_query:
params["query"] = search_query
if hbs_facet:
params["hbs_facet"] = hbs_facet
if sort_order:
params["sort"] = sort_order

# clear_search_url: remove 'query'
clear_search_params = {k: v for k, v in params.items() if k != "query"}
context["clear_search_url"] = (
"?"
+ "&".join(
[
f"{k}={requests.utils.quote(str(v))}"
for k, v in clear_search_params.items()
]
)
if clear_search_params
else "?"
)
# clear_hbs_url: remove 'hbs_facet'
clear_hbs_params = {k: v for k, v in params.items() if k != "hbs_facet"}
context["clear_hbs_url"] = (
f"?query={requests.utils.quote(search_query)}" if search_query else "?"
"?"
+ "&".join(
[
f"{k}={requests.utils.quote(str(v))}"
for k, v in clear_hbs_params.items()
]
)
if clear_hbs_params
else "?"
)
# clear_sort_url: remove 'sort'
clear_sort_params = {k: v for k, v in params.items() if k != "sort"}
context["clear_sort_url"] = (
"?"
+ "&".join(
[
f"{k}={requests.utils.quote(str(v))}"
for k, v in clear_sort_params.items()
]
)
if clear_sort_params
else "?"
)

# Enhanced sort options for UI with proper URLs that preserve search query
sort_values = tuple(value["value"] for value in context["sort_list"])
sort_list = []
for value in sort_values:
temp_params = {k: v for k, v in params.items() if k != "sort"}
if value != "default":
temp_params["sort"] = value
url = (
"?"
+ "&".join(
[
f"{k}={requests.utils.quote(str(v))}"
for k, v in temp_params.items()
]
)
if temp_params
else "?"
)
sort_list.append({"value": value, "url": url})
context["sort_list"] = sort_list

# clear_all_filters_url: remove everything
context["clear_all_filters_url"] = "?"

return context
Expand All @@ -211,6 +304,28 @@ def _get_solr_connection(self):
"""Get a Solr connection."""
return pysolr.Solr(settings.SOLR_URL, timeout=settings.SOLR_TIMEOUT)

def _build_sort_param(self, lang_code: str) -> str | None:
"""
Builds a Solr sort expression that:
- puts missing labels FIRST
- sorts alphabetically by language-specific UMIL label
"""
sort_order = self.request.GET.get("sort", "").lower()
if sort_order not in ("asc", "desc"):
return None

umil_label_field = f"instrument_umil_label_{lang_code}_s"

if sort_order == "desc":
return (
f"if(exists({umil_label_field}),1,0) asc, "
f"{umil_label_field} {sort_order}"
)
elif sort_order == "asc":
return f"{umil_label_field} {sort_order}"
else:
pass

def _build_solr_query(self, language: Language, include_facets: bool = False):
"""Build Solr query parameters supporting combined search + HBS filtering."""
lang_code = language.wikidata_code
Expand All @@ -233,6 +348,9 @@ def _build_solr_query(self, language: Language, include_facets: bool = False):
if hbs_facet:
filter_queries.append(f"hbs_prim_cat_s:{hbs_facet}")

# Build sorting filter
sort_param = self._build_sort_param(lang_code)

umil_label_field = f"instrument_umil_label_{lang_code}_s"

params = {
Expand All @@ -251,6 +369,10 @@ def _build_solr_query(self, language: Language, include_facets: bool = False):
if filter_queries:
params["fq"] = filter_queries

# Add sort if any
if sort_param:
params["sort"] = sort_param

return params

def _get_solr_page_results(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link"
href="?page=1{% if request.GET.hbs_facet %}&hbs_facet={{ request.GET.hbs_facet }}{% endif %}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}"
href="?page=1{% if request.GET.hbs_facet %}&hbs_facet={{ request.GET.hbs_facet }}{% endif %}{% if request.GET.sort %}&sort={{ request.GET.sort }}{% endif %}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}"
aria-label="First"
title="First">
<i class="bi bi-chevron-double-left"></i>
</a>
</li>
<li class="page-item">
<a class="page-link"
href="?page={{ page_obj.previous_page_number }}{% if request.GET.hbs_facet %}&hbs_facet={{ request.GET.hbs_facet }}{% endif %}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}"
href="?page={{ page_obj.previous_page_number }}{% if request.GET.hbs_facet %}&hbs_facet={{ request.GET.hbs_facet }}{% endif %}{% if request.GET.sort %}&sort={{ request.GET.sort }}{% endif %}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}"
aria-label="Previous"
title="Previous">
<i class="bi bi-chevron-left"></i>
Expand All @@ -50,15 +50,15 @@
{% if page_obj.has_next %}
<li class="page-item {% if not page_obj.has_next %}disabled{% endif %}">
<a class="page-link"
href="?page={{ page_obj.next_page_number }}{% if request.GET.hbs_facet %}&hbs_facet={{ request.GET.hbs_facet }}{% endif %}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}"
href="?page={{ page_obj.next_page_number }}{% if request.GET.hbs_facet %}&hbs_facet={{ request.GET.hbs_facet }}{% endif %}{% if request.GET.sort %}&sort={{ request.GET.sort }}{% endif %}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}"
aria-label="Next"
title="Next">
<i class="bi bi-chevron-right"></i>
</a>
</li>
<li class="page-item {% if not page_obj.has_next %}disabled{% endif %}">
<a class="page-link"
href="?page={{ page_obj.paginator.num_pages }}{% if request.GET.hbs_facet %}&hbs_facet={{ request.GET.hbs_facet }}{% endif %}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}"
href="?page={{ page_obj.paginator.num_pages }}{% if request.GET.hbs_facet %}&hbs_facet={{ request.GET.hbs_facet }}{% endif %}{% if request.GET.sort %}&sort={{ request.GET.sort }}{% endif %}{% if request.GET.query %}&query={{ request.GET.query }}{% endif %}"
aria-label="Last"
title="Last">
<i class="bi bi-chevron-double-right"></i>
Expand Down
47 changes: 47 additions & 0 deletions web-app/django/VIM/templates/instruments/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ <h6 class="text-white">Active Filters</h6>
title="Remove search filter">×</a>
</span>
{% endif %}
{% if active_filters.sort %}
<span class="badge bg-primary text-white">
Sort: {{ active_filters.sort }}
<a href="{{ clear_sort_url }}"
class="ms-1 text-white"
title="Remove Sort filter">×</a>
</span>
{% endif %}
{% if active_filters.hbs_classification %}
<span class="badge bg-primary text-white">
HBS: {{ active_filters.hbs_classification.name }}
Expand All @@ -52,6 +60,45 @@ <h6 class="text-white">Active Filters</h6>
</div>
</div>
{% endif %}
<!-- Sort Options -->
<div class="info-block bg-light h-auto p-2 mb-3">
<label for="sort-select" class="form-label">
<strong>Sort</strong>
</label>
<div class="dropdown">
<button class="btn btn-sm btn-outline-primary dropdown-toggle w-100 text-start d-flex align-items-center justify-content-between"
type="button"
id="sortDropdownMenuButton"
data-bs-toggle="dropdown"
aria-expanded="false">
<span class="d-flex align-items-center">
{% if sort == 'asc' %}
<i class="bi bi-sort-alpha-down m-2"></i>Ascending
{% elif sort == 'desc' %}
<i class="bi bi-sort-alpha-up m-2"></i>Descending
{% else %}
<i class="bi bi-slash-circle m-2"></i>Default
{% endif %}
</span>
</button>
<ul class="dropdown-menu w-100" aria-labelledby="sortDropdownMenuButton">
{% for sort_item in sort_list %}
<li>
<a class="dropdown-item{% if sort_item.value == 'default' and not sort %} active{% elif sort == sort_item.value %} active{% endif %}"
href="{{ sort_item.url }}">
{% if sort_item.value == 'asc' %}
<i class="bi bi-sort-alpha-down m-2"></i>Ascending
{% elif sort_item.value == 'desc' %}
<i class="bi bi-sort-alpha-up m-2"></i>Descending
{% else %}
<i class="bi bi-slash-circle m-2"></i>Default
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="info-block bg-light h-auto p-2 mb-3">
<h4>Hornbostel-Sachs Classification</h4>
<hr class="mt-0" />
Expand Down
1 change: 1 addition & 0 deletions web-app/django/manage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""

import os
import sys

Expand Down
Loading