From 1ef119eb821e6c91e01ecdd0f0422c1e2d389583 Mon Sep 17 00:00:00 2001
From: Daniel Quinn <code@danielquinn.org>
Date: Wed, 9 Mar 2016 01:05:46 +0000
Subject: [PATCH] Added basic filtering + search

---
 requirements.txt          |  6 ++-
 src/documents/filters.py  | 97 +++++++++++++++++++++++++++++++++++++++
 src/documents/views.py    | 10 ++++
 src/paperless/settings.py |  1 +
 4 files changed, 112 insertions(+), 2 deletions(-)
 create mode 100644 src/documents/filters.py

diff --git a/requirements.txt b/requirements.txt
index 6a133327a..527ca4142 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,11 +1,13 @@
 Django==1.9.2
+Pillow==3.1.1
+django-crispy-forms==1.6.0
 django-extensions==1.6.1
+django-filter==0.12.0
 djangorestframework==3.3.2
-python-dotenv==0.3.0
 filemagic==1.6
 langdetect==1.0.5
-Pillow==3.1.1
 pyocr==0.3.1
 python-dateutil==2.4.2
+python-dotenv==0.3.0
 python-gnupg==0.3.8
 pytz==2015.7
diff --git a/src/documents/filters.py b/src/documents/filters.py
new file mode 100644
index 000000000..f2a5ea69a
--- /dev/null
+++ b/src/documents/filters.py
@@ -0,0 +1,97 @@
+import django_filters
+
+from rest_framework import filters
+
+from .models import Document, Correspondent, Tag
+
+
+class DocumentFilter(filters.FilterSet):
+
+    title__startswith = django_filters.CharFilter(
+        name="title", lookup_type="startswith",
+        label="Title starts with (case sensitive)"
+    )
+    title__istartswith = django_filters.CharFilter(
+        name="title", lookup_type="istartswith",
+        label="Title starts with (case insensitive)"
+    )
+    title__endswith = django_filters.CharFilter(
+        name="title", lookup_type="endswith",
+        label="Title ends with (case sensitive)"
+    )
+    title__iendswith = django_filters.CharFilter(
+        name="title", lookup_type="endswith",
+        label="Title ends with (case insensitive)"
+    )
+    title__contains = django_filters.CharFilter(
+        name="title", lookup_type="contains",
+        label="Title contains (case sensitive)"
+    )
+    title__icontains = django_filters.CharFilter(
+        name="title", lookup_type="icontains",
+        label="Title contains (case insensitive)"
+    )
+
+    content__contains = django_filters.CharFilter(
+        name="content", lookup_type="contains")
+    content__icontains = django_filters.CharFilter(
+        name="content", lookup_type="icontains")
+
+    class Meta(object):
+        model = Document
+        fields = ["title"]
+
+
+class SluggableFilter(filters.FilterSet):
+
+    name__startswith = django_filters.CharFilter(
+        name="name", lookup_type="startswith",
+        label="Name starts with (case sensitive)"
+    )
+    name__istartswith = django_filters.CharFilter(
+        name="name", lookup_type="istartswith",
+        label="Name starts with (case insensitive)"
+    )
+    name__endswith = django_filters.CharFilter(
+        name="name", lookup_type="endswith",
+        label="Name ends with (case sensitive)"
+    )
+    name__iendswith = django_filters.CharFilter(
+        name="name", lookup_type="endswith",
+        label="Name ends with (case insensitive)"
+    )
+    name__contains = django_filters.CharFilter(
+        name="name", lookup_type="contains",
+        label="Name contains (case sensitive)"
+    )
+    name__icontains = django_filters.CharFilter(
+        name="name", lookup_type="icontains",
+        label="Name contains (case insensitive)"
+    )
+
+    slug__istartswith = django_filters.CharFilter(
+        name="slug", lookup_type="istartswith",
+        label="Slug starts with (case insensitive)"
+    )
+    slug__iendswith = django_filters.CharFilter(
+        name="slug", lookup_type="endswith",
+        label="Slug ends with (case insensitive)"
+    )
+    slug__icontains = django_filters.CharFilter(
+        name="slug", lookup_type="icontains",
+        label="Slug contains (case insensitive)"
+    )
+
+
+class CorrespondentFilter(SluggableFilter):
+
+    class Meta(object):
+        model = Correspondent
+        fields = ["name"]
+
+
+class TagFilter(SluggableFilter):
+
+    class Meta(object):
+        model = Tag
+        fields = ["name"]
diff --git a/src/documents/views.py b/src/documents/views.py
index 1dc23aa4f..94db7abf0 100644
--- a/src/documents/views.py
+++ b/src/documents/views.py
@@ -3,6 +3,7 @@ from django.http import HttpResponse
 from django.views.decorators.csrf import csrf_exempt
 from django.views.generic import FormView, DetailView, TemplateView
 
+from rest_framework import filters
 from rest_framework.mixins import (
     RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, ListModelMixin)
 from rest_framework.pagination import PageNumberPagination
@@ -12,6 +13,7 @@ from rest_framework.viewsets import (
 
 from paperless.db import GnuPG
 
+from .filters import DocumentFilter, CorrespondentFilter, TagFilter
 from .forms import UploadForm
 from .models import Correspondent, Tag, Document, Log
 from .serialisers import (
@@ -92,6 +94,8 @@ class CorrespondentViewSet(ModelViewSet):
     serializer_class = CorrespondentSerializer
     pagination_class = StandardPagination
     permission_classes = (IsAuthenticated,)
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_class = CorrespondentFilter
 
 
 class TagViewSet(ModelViewSet):
@@ -100,6 +104,8 @@ class TagViewSet(ModelViewSet):
     serializer_class = TagSerializer
     pagination_class = StandardPagination
     permission_classes = (IsAuthenticated,)
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_class = TagFilter
 
 
 class DocumentViewSet(RetrieveModelMixin,
@@ -112,6 +118,9 @@ class DocumentViewSet(RetrieveModelMixin,
     serializer_class = DocumentSerializer
     pagination_class = StandardPagination
     permission_classes = (IsAuthenticated,)
+    filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter)
+    filter_class = DocumentFilter
+    search_fields = ("title", "correspondent__name", "content")
 
 
 class LogViewSet(ReadOnlyModelViewSet):
@@ -120,3 +129,4 @@ class LogViewSet(ReadOnlyModelViewSet):
     serializer_class = LogSerializer
     pagination_class = StandardPagination
     permission_classes = (IsAuthenticated,)
+    filter_backends = (filters.DjangoFilterBackend,)
diff --git a/src/paperless/settings.py b/src/paperless/settings.py
index b7daecaf8..bb1ba363b 100644
--- a/src/paperless/settings.py
+++ b/src/paperless/settings.py
@@ -46,6 +46,7 @@ INSTALLED_APPS = [
     "documents",
 
     "rest_framework",
+    "crispy_forms",
 
 ]