Merge branch 'master' of github.com:danielquinn/paperless

This commit is contained in:
Daniel Quinn 2016-03-14 10:36:48 +00:00
commit de53b1c02f
7 changed files with 133 additions and 55 deletions

View File

@ -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 recommend that if you do want to use it, run it locally on a server in your own
home. 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 .. _this one: http://www.brother.ca/en-CA/Scanners/11/ProductDetail/ADS1500W?ProductDetail=productdetail
.. _ImageMagick: http://imagemagick.org/ .. _ImageMagick: http://imagemagick.org/
.. _Tesseract: https://github.com/tesseract-ocr .. _Tesseract: https://github.com/tesseract-ocr
@ -102,6 +117,7 @@ home.
.. _Django: https://www.djangoproject.com/ .. _Django: https://www.djangoproject.com/
.. _Python-GNUPG: http://pythonhosted.org/python-gnupg/ .. _Python-GNUPG: http://pythonhosted.org/python-gnupg/
.. _ReadTheDocs: https://paperless.readthedocs.org/ .. _ReadTheDocs: https://paperless.readthedocs.org/
.. _the magic of bitcoins: https://blockchain.info/address/16RanUWNTTbR4yhc3FG8pXpq6BfJRPCcUs
.. |Documentation| image:: https://readthedocs.org/projects/paperless/badge/?version=latest .. |Documentation| image:: https://readthedocs.org/projects/paperless/badge/?version=latest
:alt: Read the documentation at https://paperless.readthedocs.org/ :alt: Read the documentation at https://paperless.readthedocs.org/
:target: https://paperless.readthedocs.org/ :target: https://paperless.readthedocs.org/

View File

@ -2,7 +2,7 @@ version: '2'
services: services:
webserver: webserver:
image: paperless image: pitkley/paperless
ports: ports:
# You can adapt the port you want Paperless to listen on by # You can adapt the port you want Paperless to listen on by
# modifying the part before the `:`. # modifying the part before the `:`.
@ -16,7 +16,7 @@ services:
command: ["runserver", "0.0.0.0:8000"] command: ["runserver", "0.0.0.0:8000"]
consumer: consumer:
image: paperless image: pitkley/paperless
volumes: volumes:
- data:/usr/src/paperless/data - data:/usr/src/paperless/data
- media:/usr/src/paperless/media - media:/usr/src/paperless/media

View File

@ -5,7 +5,7 @@ The REST API
Paperless makes use of the `Django REST Framework`_ standard API interface Paperless makes use of the `Django REST Framework`_ standard API interface
because of its inherent awesomeness. Conveniently, the system is also 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 accepted and what isn't, you need only visit ``/api`` on your local Paperless
installation. installation.

BIN
presentation/img/pony.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

@ -61,8 +61,6 @@
<br /> <br />
<h1>Paperless</h1> <h1>Paperless</h1>
<p><small> <p><small>
Daniel Quinn, London UK, March 2015<br />
<br />
<i class="fa fa-twitter"></i> &nbsp; <a class="nocolour" href="https://twitter.com/searchingfortao">@searchingfortao</a> <i class="fa fa-twitter"></i> &nbsp; <a class="nocolour" href="https://twitter.com/searchingfortao">@searchingfortao</a>
&nbsp; | &nbsp; &nbsp; | &nbsp;
<i class="fa fa-github"></i> &nbsp; <a class="nocolour" href="https://github.com/danielquinn">danielquinn</a> <i class="fa fa-github"></i> &nbsp; <a class="nocolour" href="https://github.com/danielquinn">danielquinn</a>
@ -145,7 +143,7 @@
</section> </section>
</section> </section>
<section> <section data-background="img/pony.png">
<h2>Demo!</h2> <h2>Demo!</h2>
<p>Time to sacrifice a kitten</p> <p>Time to sacrifice a kitten</p>
</section> </section>
@ -168,7 +166,7 @@
<a class="nocolour" href="https://github.com/danielquinn/paperless">https://github.com/danielquinn/paperless</a> <a class="nocolour" href="https://github.com/danielquinn/paperless">https://github.com/danielquinn/paperless</a>
</p> </p>
<p style="text-align: center;"> <p style="text-align: center;">
<img src="img/qr/repo.svg" style="width: 300px;" /> <img src="img/repo.svg" style="width: 300px;" />
</p> </p>
</section> </section>

View File

@ -4,45 +4,31 @@ from rest_framework import filters
from .models import Document, Correspondent, Tag from .models import Document, Correspondent, Tag
#
class DocumentFilter(filters.FilterSet): # I hate how copy/pastey this file is. Recommendations are welcome.
#
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): # 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__startswith = django_filters.CharFilter(
name="name", lookup_type="startswith", name="name", lookup_type="startswith",
@ -83,15 +69,84 @@ class SluggableFilter(filters.FilterSet):
) )
class CorrespondentFilter(SluggableFilter): class CorrespondentFilterSet(SluggableFilterSet):
class Meta(object): class Meta(object):
model = Correspondent model = Correspondent
fields = ["name"] fields = ["name"]
class TagFilter(SluggableFilter): class TagFilterSet(SluggableFilterSet):
class Meta(object): class Meta(object):
model = Tag 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"]

View File

@ -13,7 +13,7 @@ from rest_framework.viewsets import (
from paperless.db import GnuPG from paperless.db import GnuPG
from .filters import DocumentFilter, CorrespondentFilter, TagFilter from .filters import DocumentFilterSet, CorrespondentFilterSet, TagFilterSet
from .forms import UploadForm from .forms import UploadForm
from .models import Correspondent, Tag, Document, Log from .models import Correspondent, Tag, Document, Log
from .serialisers import ( from .serialisers import (
@ -94,8 +94,9 @@ class CorrespondentViewSet(ModelViewSet):
serializer_class = CorrespondentSerializer serializer_class = CorrespondentSerializer
pagination_class = StandardPagination pagination_class = StandardPagination
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
filter_backends = (filters.DjangoFilterBackend,) filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter)
filter_class = CorrespondentFilter filter_class = CorrespondentFilterSet
ordering_fields = ("name", "slug")
class TagViewSet(ModelViewSet): class TagViewSet(ModelViewSet):
@ -104,8 +105,9 @@ class TagViewSet(ModelViewSet):
serializer_class = TagSerializer serializer_class = TagSerializer
pagination_class = StandardPagination pagination_class = StandardPagination
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
filter_backends = (filters.DjangoFilterBackend,) filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter)
filter_class = TagFilter filter_class = TagFilterSet
ordering_fields = ("name", "slug")
class DocumentViewSet(RetrieveModelMixin, class DocumentViewSet(RetrieveModelMixin,
@ -118,9 +120,15 @@ class DocumentViewSet(RetrieveModelMixin,
serializer_class = DocumentSerializer serializer_class = DocumentSerializer
pagination_class = StandardPagination pagination_class = StandardPagination
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
filter_backends = (filters.DjangoFilterBackend, filters.SearchFilter) filter_backends = (
filter_class = DocumentFilter filters.DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter
)
filter_class = DocumentFilterSet
search_fields = ("title", "correspondent__name", "content") search_fields = ("title", "correspondent__name", "content")
ordering_fields = (
"id", "title", "correspondent__name", "created", "modified")
class LogViewSet(ReadOnlyModelViewSet): class LogViewSet(ReadOnlyModelViewSet):
@ -129,4 +137,5 @@ class LogViewSet(ReadOnlyModelViewSet):
serializer_class = LogSerializer serializer_class = LogSerializer
pagination_class = StandardPagination pagination_class = StandardPagination
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
filter_backends = (filters.DjangoFilterBackend,) filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter)
ordering_fields = ("time",)