diff --git a/backend/api/filters.py b/backend/api/filters.py index 8dc1f484..cdd02b1e 100644 --- a/backend/api/filters.py +++ b/backend/api/filters.py @@ -31,3 +31,25 @@ class DocumentFilter(FilterSet): 'categories__label__id', 'spans__label__id', 'categories__isnull', 'spans__isnull', 'texts__isnull' ) + + +class ExampleFilter(FilterSet): + confirmed = BooleanFilter(field_name='states', method='filter_by_state') + + def filter_by_state(self, queryset, field_name, is_confirmed: bool): + queryset = queryset.annotate( + num_confirm=Count( + expression=field_name, + filter=Q(**{f'{field_name}__confirmed_by': self.request.user}) | + Q(project__collaborative_annotation=True) + ) + ) + if is_confirmed: + queryset = queryset.filter(num_confirm__gte=1) + else: + queryset = queryset.filter(num_confirm__lte=0) + return queryset + + class Meta: + model = Example + fields = ('project', 'text', 'created_at', 'updated_at') diff --git a/backend/api/tests/test_filters.py b/backend/api/tests/test_filters.py new file mode 100644 index 00000000..4c8d6312 --- /dev/null +++ b/backend/api/tests/test_filters.py @@ -0,0 +1,77 @@ +from unittest.mock import MagicMock + +from django.test import TestCase + +from ..filters import ExampleFilter +from ..models import Example +from .api.utils import make_doc, make_example_state, prepare_project + + +class TestFilterMixin(TestCase): + + def prepare(self, project): + self.example = make_doc(project.item) + self.request = MagicMock() + self.queryset = Example.objects.all() + make_example_state(self.example, project.users[0]) + self.request.user = project.users[0] + + def assert_filter(self, data, expected): + f = ExampleFilter( + data=data, + queryset=self.queryset, + request=self.request + ) + self.assertEqual(f.qs.count(), expected) + + +class TestExampleFilter(TestFilterMixin): + + def setUp(self): + self.project = prepare_project(task='DocumentClassification') + self.prepare(project=self.project) + + def test_returns_example_if_confirmed_is_true(self): + self.assert_filter(data={'confirmed': 'True'}, expected=1) + + def test_does_not_return_example_if_confirmed_is_false(self): + self.assert_filter(data={'confirmed': 'False'}, expected=0) + + def test_returns_example_if_confirmed_is_empty(self): + self.assert_filter(data={'confirmed': ''}, expected=1) + + def test_does_not_return_example_if_user_is_different(self): + self.request.user = self.project.users[1] + self.assert_filter(data={'confirmed': 'True'}, expected=0) + + def test_returns_example_if_user_is_different(self): + self.request.user = self.project.users[1] + self.assert_filter(data={'confirmed': 'False'}, expected=1) + + def test_returns_example_if_user_is_different_and_confirmed_is_empty(self): + self.request.user = self.project.users[1] + self.assert_filter(data={'confirmed': ''}, expected=1) + + +class TestExampleFilterOnCollaborative(TestFilterMixin): + + def setUp(self): + self.project = prepare_project( + task='DocumentClassification', collaborative_annotation=True + ) + self.prepare(project=self.project) + + def test_returns_example_if_confirmed_is_true(self): + for member in self.project.users: + self.request.user = member + self.assert_filter(data={'confirmed': 'True'}, expected=1) + + def test_does_not_return_example_if_confirmed_is_false(self): + for member in self.project.users: + self.request.user = member + self.assert_filter(data={'confirmed': 'False'}, expected=0) + + def test_returns_example_if_confirmed_is_empty(self): + for member in self.project.users: + self.request.user = member + self.assert_filter(data={'confirmed': ''}, expected=1)