Skip to content

Commit

Permalink
feat(filters): add "only_newest" filter for files
Browse files Browse the repository at this point in the history
This helps selecting files when we're searching (or otherwise looking
for files) and only want the newest version
  • Loading branch information
winged committed Oct 17, 2024
1 parent 9de5aa2 commit c819a7d
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 4 deletions.
34 changes: 30 additions & 4 deletions alexandria/core/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from django.conf import settings
from django.contrib.postgres.search import SearchHeadline, SearchQuery, SearchRank
from django.db.models import F, FloatField, Q, TextField, Value
from django.db.models import Exists, F, FloatField, OuterRef, Q, TextField, Value
from django.db.models.fields.json import KeyTextTransform
from django.db.models.functions import Cast
from django_filters import (
Expand Down Expand Up @@ -169,17 +169,43 @@ class FileFilterSet(FilterSet):
document_metainfo = JSONValueFilter(field_name="document__metainfo")
active_group = ActiveGroupFilter()
files = BaseCSVFilter(field_name="pk", lookup_expr="in")
only_newest = BooleanFilter(method="filter_only_newest")

def filter_only_newest(self, qs, name, value):
if value:
return qs.exclude(
Exists(
# Find all newer versions of a file. A newer version is a file:
# - of the same variant
# - with a different ID (obviously)
# - a newer created_at timestamp
# - AND the same document
models.File.objects.all()
.filter(
document=OuterRef("document_id"),
created_at__gt=OuterRef("created_at"),
variant=OuterRef("variant"),
)
.exclude(pk=OuterRef("pk")),
)
)
return qs

class Meta:
model = models.File
fields = ["original", "renderings", "variant", "metainfo", "files"]
fields = [
"original",
"renderings",
"variant",
"metainfo",
"files",
"only_newest",
]


class SearchFilterSet(FileFilterSet):
query = CharFilter(method="search_files")

only_newest = BooleanFilter(method="filter_only_newest")

# Cut-off value, we don't want all the garbage matches
min_rank = NumberFilter(field_name="search_rank", lookup_expr="gte")

Expand Down
38 changes: 38 additions & 0 deletions alexandria/core/tests/test_filters.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import time
from itertools import combinations
from typing import Optional

Expand Down Expand Up @@ -344,3 +345,40 @@ def test_document_category_filters(
data = response.json()["data"]
assert len(data) == expected_count
assert sorted([doc["attributes"]["title"] for doc in data]) == snapshot


@pytest.mark.parametrize(
"filter_val, expect_v1, expect_v2",
[
(True, False, True),
(False, True, True),
(None, True, True),
],
)
def test_only_newest_filter(
db, admin_client, document_factory, file_factory, filter_val, expect_v1, expect_v2
):
doc = document_factory()
version_1 = file_factory(document=doc, variant="original")
time.sleep(0.5)
version_2 = file_factory(document=doc, variant="original")

# original and thumbnail (?)
assert doc.files.all().count() == 4

filters = {
"filter[variant]": "original",
}
if filter_val is not None:
filters["filter[only_newest]"] = filter_val

response = admin_client.get(reverse("file-list"), filters)

assert response.status_code == HTTP_200_OK, response.json()
received_file_ids = [f["id"] for f in response.json()["data"]]

has_v1 = str(version_1.pk) in received_file_ids
has_v2 = str(version_2.pk) in received_file_ids

assert has_v1 == expect_v1
assert has_v2 == expect_v2

0 comments on commit c819a7d

Please sign in to comment.