diff --git a/README.rst b/README.rst
index 5c619d994..8bdf66319 100644
--- a/README.rst
+++ b/README.rst
@@ -91,6 +91,21 @@ means that paperless should never be run on an untrusted host. Instead, I
recommend that if you do want to use it, run it locally on a server in your own
home.
+
+Donations
+=========
+
+As with all Free software, the power is less in the finances and more in the
+collective efforts. I really appreciate every pull request and bug report
+offered up by Paperless' users, so please keep that stuff coming. If however,
+you're not one for coding/design/documentation, and would like to contribute
+financially, I won't say no ;-)
+
+Unfortunately, I can't find a good way to do donations on GitHub that doesn't
+involve PayPal (I *really* hate PayPal) so all I've got at present is Bitcoin.
+So, if you'd like to donate some coin to feed my doughnut habit, by all means
+use `the magic of bitcoins`_ while they're still around.
+
.. _this one: http://www.brother.ca/en-CA/Scanners/11/ProductDetail/ADS1500W?ProductDetail=productdetail
.. _ImageMagick: http://imagemagick.org/
.. _Tesseract: https://github.com/tesseract-ocr
@@ -102,6 +117,7 @@ home.
.. _Django: https://www.djangoproject.com/
.. _Python-GNUPG: http://pythonhosted.org/python-gnupg/
.. _ReadTheDocs: https://paperless.readthedocs.org/
+.. _the magic of bitcoins: https://blockchain.info/address/16RanUWNTTbR4yhc3FG8pXpq6BfJRPCcUs
.. |Documentation| image:: https://readthedocs.org/projects/paperless/badge/?version=latest
:alt: Read the documentation at https://paperless.readthedocs.org/
:target: https://paperless.readthedocs.org/
diff --git a/docker-compose.yml.example b/docker-compose.yml.example
index 488fc83d2..2a70c9ff7 100644
--- a/docker-compose.yml.example
+++ b/docker-compose.yml.example
@@ -2,7 +2,7 @@ version: '2'
services:
webserver:
- image: paperless
+ image: pitkley/paperless
ports:
# You can adapt the port you want Paperless to listen on by
# modifying the part before the `:`.
@@ -16,7 +16,7 @@ services:
command: ["runserver", "0.0.0.0:8000"]
consumer:
- image: paperless
+ image: pitkley/paperless
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
diff --git a/docs/api.rst b/docs/api.rst
index 15ca9bc44..d08826a33 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -5,7 +5,7 @@ The REST API
Paperless makes use of the `Django REST Framework`_ standard API interface
because of its inherent awesomeness. Conveniently, the system is also
-self-documenting, so learn more about the access points, schema, what's
+self-documenting, so to learn more about the access points, schema, what's
accepted and what isn't, you need only visit ``/api`` on your local Paperless
installation.
diff --git a/presentation/img/pony.png b/presentation/img/pony.png
new file mode 100644
index 000000000..a34b1e5c3
Binary files /dev/null and b/presentation/img/pony.png differ
diff --git a/presentation/index.html b/presentation/index.html
index f58b56a66..00e9eb035 100644
--- a/presentation/index.html
+++ b/presentation/index.html
@@ -61,8 +61,6 @@
Paperless
- Daniel Quinn, London UK, March 2015
-
@searchingfortao
|
danielquinn
@@ -145,7 +143,7 @@
-
-
+
diff --git a/src/documents/filters.py b/src/documents/filters.py
index f2a5ea69a..a7c069d33 100644
--- a/src/documents/filters.py
+++ b/src/documents/filters.py
@@ -4,45 +4,31 @@ 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"]
+#
+# I hate how copy/pastey this file is. Recommendations are welcome.
+#
-class SluggableFilter(filters.FilterSet):
+# Filters
+
+
+class RelatedFilter(django_filters.MethodFilter):
+
+ def __init__(self, *args, **kwargs):
+ self.key = kwargs.pop("key")
+ self.lookup_type = kwargs.get("lookup_type")
+ django_filters.MethodFilter.__init__(self, *args, **kwargs)
+
+ def filter(self, qs, value):
+ if not value:
+ return qs
+ return qs.filter(**{"tags__{}".format(self.key): value})
+
+
+# FilterSets
+
+
+class SluggableFilterSet(filters.FilterSet):
name__startswith = django_filters.CharFilter(
name="name", lookup_type="startswith",
@@ -83,15 +69,84 @@ class SluggableFilter(filters.FilterSet):
)
-class CorrespondentFilter(SluggableFilter):
+class CorrespondentFilterSet(SluggableFilterSet):
class Meta(object):
model = Correspondent
fields = ["name"]
-class TagFilter(SluggableFilter):
+class TagFilterSet(SluggableFilterSet):
class Meta(object):
model = Tag
- fields = ["name"]
+ fields = ["name", "slug"]
+
+
+class DocumentFilterSet(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")
+
+ tags__name = RelatedFilter(key="name")
+ tags__name__startswith = RelatedFilter(key="name__startswith")
+ tags__name__istartswith = RelatedFilter(key="name__istartswith")
+ tags__name__endswith = RelatedFilter(key="name__endswith")
+ tags__name__iendswith = RelatedFilter(key="name__iendswith")
+ tags__name__contains = RelatedFilter(key="name__contains")
+ tags__name__icontains = RelatedFilter(key="name__icontains")
+
+ tags__slug = RelatedFilter(key="slug")
+ tags__slug__startswith = RelatedFilter(key="slug__startswith")
+ tags__slug__istartswith = RelatedFilter(key="slug__istartswith")
+ tags__slug__endswith = RelatedFilter(key="slug__endswith")
+ tags__slug__iendswith = RelatedFilter(key="slug__iendswith")
+ tags__slug__contains = RelatedFilter(key="slug__contains")
+ tags__slug__icontains = RelatedFilter(key="slug__icontains")
+
+ correspondent__name = RelatedFilter(key="name")
+ correspondent__name__startswith = RelatedFilter(key="name__startswith")
+ correspondent__name__istartswith = RelatedFilter(key="name__istartswith")
+ correspondent__name__endswith = RelatedFilter(key="name__endswith")
+ correspondent__name__iendswith = RelatedFilter(key="name__iendswith")
+ correspondent__name__contains = RelatedFilter(key="name__contains")
+ correspondent__name__icontains = RelatedFilter(key="name__icontains")
+
+ correspondent__slug = RelatedFilter(key="slug")
+ correspondent__slug__startswith = RelatedFilter(key="slug__startswith")
+ correspondent__slug__istartswith = RelatedFilter(key="slug__istartswith")
+ correspondent__slug__endswith = RelatedFilter(key="slug__endswith")
+ correspondent__slug__iendswith = RelatedFilter(key="slug__iendswith")
+ correspondent__slug__contains = RelatedFilter(key="slug__contains")
+ correspondent__slug__icontains = RelatedFilter(key="slug__icontains")
+
+ class Meta(object):
+ model = Document
+ fields = ["title"]
diff --git a/src/documents/views.py b/src/documents/views.py
index 94db7abf0..71d350e02 100644
--- a/src/documents/views.py
+++ b/src/documents/views.py
@@ -13,7 +13,7 @@ from rest_framework.viewsets import (
from paperless.db import GnuPG
-from .filters import DocumentFilter, CorrespondentFilter, TagFilter
+from .filters import DocumentFilterSet, CorrespondentFilterSet, TagFilterSet
from .forms import UploadForm
from .models import Correspondent, Tag, Document, Log
from .serialisers import (
@@ -94,8 +94,9 @@ class CorrespondentViewSet(ModelViewSet):
serializer_class = CorrespondentSerializer
pagination_class = StandardPagination
permission_classes = (IsAuthenticated,)
- filter_backends = (filters.DjangoFilterBackend,)
- filter_class = CorrespondentFilter
+ filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter)
+ filter_class = CorrespondentFilterSet
+ ordering_fields = ("name", "slug")
class TagViewSet(ModelViewSet):
@@ -104,8 +105,9 @@ class TagViewSet(ModelViewSet):
serializer_class = TagSerializer
pagination_class = StandardPagination
permission_classes = (IsAuthenticated,)
- filter_backends = (filters.DjangoFilterBackend,)
- filter_class = TagFilter
+ filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter)
+ filter_class = TagFilterSet
+ ordering_fields = ("name", "slug")
class DocumentViewSet(RetrieveModelMixin,
@@ -118,9 +120,15 @@ class DocumentViewSet(RetrieveModelMixin,
serializer_class = DocumentSerializer
pagination_class = StandardPagination
permission_classes = (IsAuthenticated,)
- filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter)
- filter_class = DocumentFilter
+ filter_backends = (
+ filters.DjangoFilterBackend,
+ filters.SearchFilter,
+ filters.OrderingFilter
+ )
+ filter_class = DocumentFilterSet
search_fields = ("title", "correspondent__name", "content")
+ ordering_fields = (
+ "id", "title", "correspondent__name", "created", "modified")
class LogViewSet(ReadOnlyModelViewSet):
@@ -129,4 +137,5 @@ class LogViewSet(ReadOnlyModelViewSet):
serializer_class = LogSerializer
pagination_class = StandardPagination
permission_classes = (IsAuthenticated,)
- filter_backends = (filters.DjangoFilterBackend,)
+ filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter)
+ ordering_fields = ("time",)