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 @@ -

+

Demo!

Time to sacrifice a kitten

@@ -168,7 +166,7 @@ https://github.com/danielquinn/paperless

- +

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",)