Skip to content

Commit

Permalink
Allow filter/sort to include inferred/document dates (#1347)
Browse files Browse the repository at this point in the history
  • Loading branch information
blms committed Aug 14, 2024
1 parent 2e0d564 commit fbd83db
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 13 deletions.
7 changes: 7 additions & 0 deletions geniza/corpus/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,13 @@ class DocumentSearchForm(RangeForm):
),
)

exclude_inferred = forms.BooleanField(
# Translators: label for "exclude inferred dates" search form filter
label=_("Exclude inferred dates"),
required=False,
widget=forms.CheckboxInput,
)

doctype = FacetChoiceField(
# Translators: label for document type search form filter
label=_("Document type"),
Expand Down
15 changes: 15 additions & 0 deletions geniza/corpus/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,10 @@ def index_data(self):
),
# combined original/standard document date for display
"document_date_t": strip_tags(self.document_date) or None,
# inferred document date for display
"document_dating_t": standard_date_display(
"/".join([d.isoformat() for d in self.dating_range() if d])
),
# date range for filtering
"document_date_dr": self.solr_date_range(),
# date range for filtering, but including inferred datings if any exist
Expand All @@ -1232,6 +1236,17 @@ def index_data(self):
"end_date_i": (
self.end_date.numeric_format(mode="max") if self.end_date else None
),
# start/end of document date or date range, including inferred datings, for sort
"start_dating_i": (
self.dating_range()[0].numeric_format()
if self.dating_range()[0]
else None
),
"end_dating_i": (
self.dating_range()[1].numeric_format(mode="max")
if self.dating_range()[1]
else None
),
# library/collection possibly redundant?
"collection_ss": [str(f.collection) for f in fragments],
"tags_ss_lower": [t.name for t in self.tags.all()],
Expand Down
1 change: 1 addition & 0 deletions geniza/corpus/solr_queryset.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class DocumentSolrQuerySet(AliasedSolrQuerySet):
"shelfmark": "shelfmark_s", # string version for display
"shelfmarks": "fragment_shelfmark_ss",
"document_date": "document_date_t", # text version for search & display
"document_dating": "document_dating_t", # inferred date for display
"original_date_t": "original_date",
"collection": "collection_ss",
"tags": "tags_ss_lower",
Expand Down
5 changes: 5 additions & 0 deletions geniza/corpus/templates/corpus/document_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ <h1>{{ page_title }}</h1>
{# NOTE: stimulus action is configured via django widget attrs #}
{{ form.docdate }}
</label>
<label for="{{ form.exclude_inferred.auto_id }}">
{% render_field form.exclude_inferred data-action="search#update" %}
<span>{{ form.exclude_inferred.label }}</span>
<div class="thumb" aria-hidden="true"></div>
</label>
</div>
<fieldset class="includes-fields">
<legend><span class="fieldname">{% translate "Includes" %}</span></legend>
Expand Down
10 changes: 10 additions & 0 deletions geniza/corpus/templates/corpus/snippets/document_result.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ <h2 class="title">
{{ document.document_date.0 }} {# indexed as _t which is a multival field #}
</time>
</dd>
{% elif document.document_dating %}
<dt>
{# Translators: label for inferred date on a document #}
{% translate "Inferred date" %}
</dt>
<dd>
<time>
{{ document.document_dating.0 }} {# indexed as _t which is a multival field #}
</time>
</dd>
{% endif %}
{% if document.languages|length %}
<dt>
Expand Down
20 changes: 14 additions & 6 deletions geniza/corpus/tests/test_corpus_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,8 @@ def test_get_range_stats(self, mock_solr_queryset):
# mock_queryset_cls.return_value.stats.return_value.get_stats.return_value = {
mock_queryset_cls.return_value.get_stats.return_value = {
"stats_fields": {
"start_date_i": {"min": None},
"end_date_i": {"max": None},
"start_dating_i": {"min": None},
"end_dating_i": {"max": None},
}
}
docsearch_view = DocumentSearchView()
Expand All @@ -560,14 +560,14 @@ def test_get_range_stats(self, mock_solr_queryset):
)
assert stats == {"docdate": (None, None)}
mock_queryset_cls.return_value.stats.assert_called_with(
"start_date_i", "end_date_i"
"start_dating_i", "end_dating_i"
)

# convert integer date to year
mock_queryset_cls.return_value.get_stats.return_value = {
"stats_fields": {
"start_date_i": {"min": 10380101.0},
"end_date_i": {"max": 10421231.0},
"start_dating_i": {"min": 10380101.0},
"end_dating_i": {"max": 10421231.0},
}
}
stats = docsearch_view.get_range_stats(
Expand All @@ -577,7 +577,7 @@ def test_get_range_stats(self, mock_solr_queryset):

# test three-digit year
mock_queryset_cls.return_value.get_stats.return_value["stats_fields"][
"start_date_i"
"start_dating_i"
]["min"] = 8430101.0
stats = docsearch_view.get_range_stats(
queryset_cls=mock_queryset_cls, field_name="docdate"
Expand Down Expand Up @@ -979,6 +979,14 @@ def test_get_solr_sort(self):
assert random_sort.startswith("random_")
assert int(random_sort.split("_")[1])

# doc dating without exclude_inferred: should include inferred
dating_sort = docsearch_view.get_solr_sort("docdate_asc")
assert dating_sort.startswith("start_dating_")

# with exclude_inferred: should use start_dating, which is dates without inferred
dating_sort = docsearch_view.get_solr_sort("docdate_asc", "true")
assert dating_sort.startswith("start_date_")

def test_random_page_redirect(self, client):
# any page of results other than one should redirect to the first page
docsearch_url = reverse("corpus:document-search")
Expand Down
27 changes: 20 additions & 7 deletions geniza/corpus/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ def get_range_stats(self, queryset_cls, field_name):
the key is not added to a dictionary.
:rtype: dict
"""
stats = queryset_cls().stats("start_date_i", "end_date_i").get_stats()
stats = queryset_cls().stats("start_dating_i", "end_dating_i").get_stats()
if stats.get("stats_fields"):
# use minimum from start date and max from end date
# - we're storing YYYYMMDD as 8-digit number for this we only want year
# convert to str, take first 4 digits, then convert back to int
min_val = stats["stats_fields"]["start_date_i"]["min"]
max_val = stats["stats_fields"]["end_date_i"]["max"]
min_val = stats["stats_fields"]["start_dating_i"]["min"]
max_val = stats["stats_fields"]["end_dating_i"]["max"]

# trim from the end to handle 3-digit years; includes .0 at end
min_year = int(str(min_val)[:-6]) if min_val else None
Expand Down Expand Up @@ -96,6 +96,8 @@ class DocumentSearchView(
"shelfmark": "shelfmark_natsort",
"docdate_asc": "start_date_i",
"docdate_desc": "-end_date_i",
"docdating_asc": "start_dating_i",
"docdating_desc": "-end_dating_i",
}

def dispatch(self, request, *args, **kwargs):
Expand All @@ -116,13 +118,16 @@ def last_modified(self):
return None
return super().last_modified()

def get_solr_sort(self, sort_option):
def get_solr_sort(self, sort_option, exclude_inferred=False):
"""Return solr sort field for user-seleted sort option;
generates random sort field using solr random dynamic field;
otherwise uses solr sort field from :attr:`solr_sort`"""
if sort_option == "random":
# use solr's random dynamic field to sort randomly
return "random_%s" % randint(1000, 9999)
elif "docdate" in sort_option and not exclude_inferred:
# use inferred datings if exclude_inferred is not true
sort_option = sort_option.replace("date", "dating")
return self.solr_sort[sort_option]

def get_form_kwargs(self):
Expand Down Expand Up @@ -249,7 +254,11 @@ def get_queryset(self):
) # include relevance score in results

# order by sort option
documents = documents.order_by(self.get_solr_sort(search_opts["sort"]))
documents = documents.order_by(
self.get_solr_sort(
search_opts["sort"], search_opts.get("exclude_inferred", False)
)
)

# filter by type if specified
if search_opts["doctype"]:
Expand Down Expand Up @@ -286,9 +295,13 @@ def get_queryset(self):
if search_opts["docdate"]:
# date range filter; returns tuple of value or None for open-ended range
start, end = search_opts["docdate"]
documents = documents.filter(
document_date_dr="[%s TO %s]" % (start or "*", end or "*")
date_filter = "[%s TO %s]" % (start or "*", end or "*")
date_field = (
"document_date_dr"
if search_opts.get("exclude_inferred", False)
else "document_dating_dr"
)
documents = documents.filter(**{date_field: date_filter})
label = "%s–%s" % (start, end)
if start and not end:
label = _("After %s") % start
Expand Down
2 changes: 2 additions & 0 deletions sitemedia/scss/base/_colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ $off-white: #f7f7f7;
--primary-80: rgba(67, 96, 67, 0.8);
--secondary: #567856;
--secondary-80: rgba(86, 120, 86, 0.8);
--secondary-40: rgba(86, 120, 86, 0.4);
--tertiary: #7bac7b;
--link-primary: #567856;
--link-secondary: #436043;
Expand Down Expand Up @@ -107,6 +108,7 @@ $off-white: #f7f7f7;
--primary-80: rgba(183, 102, 128, 0.8);
--secondary: #b76680;
--secondary-80: rgba(195, 127, 151, 0.8);
--secondary-40: rgba(195, 127, 151, 0.4);
--tertiary: #b05070;
--link-primary: #c37f97;
--link-secondary: #b76680;
Expand Down
55 changes: 55 additions & 0 deletions sitemedia/scss/components/_searchform.scss
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,61 @@ main.people {
}
}

main.search form fieldset#filters div.fieldset-left-column {
@include breakpoints.for-tablet-landscape-up {
gap: 1rem;
}
label[for="id_exclude_inferred"] {
position: relative;
cursor: pointer;
input[type="checkbox"] + span::before {
display: none;
}
input[type="checkbox"] + span {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
input[type="checkbox"] + span::after {
display: block;
content: "";
border-radius: 7px;
width: 34px;
height: 14px;
background-color: var(--background-gray);
transition: background-color 0.1s ease-in-out;
}
input[type="checkbox"]:active + span::after,
input[type="checkbox"]:checked + span::after {
background-color: var(--secondary-40);
}
input[type="checkbox"]:checked:active + span::after {
background-color: var(--background-gray);
}
.thumb {
display: block;
width: 20px;
height: 20px;
position: absolute;
top: calc(50% - 10px);
right: 14px;
transition: right 0.1s ease-in-out,
background-color 0.1s ease-in-out;
border-radius: 50%;
background-color: var(--disabled-on-background-light);
box-shadow: 0px 1px 3px 0px #00000033;
box-shadow: 0px 2px 1px 0px #0000001f;
box-shadow: 0px 1px 1px 0px #00000024;
}
input[type="checkbox"]:active + span + .thumb,
input[type="checkbox"]:checked + span + .thumb {
right: 0;
background-color: var(--secondary);
}
}
}

// tweaks for RTL search form for hebrew, arabic
html[dir="rtl"] main.search form {
// search query box and button
Expand Down

0 comments on commit fbd83db

Please sign in to comment.