diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 643aa0698..d57b2e7e5 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -40,7 +40,7 @@ Project maintainers who do not follow or enforce the Code of Conduct in good fai ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4 to remove puritanical language. The original is available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ diff --git a/Pipfile b/Pipfile index 1ce5a6908..a9331f134 100644 --- a/Pipfile +++ b/Pipfile @@ -4,7 +4,7 @@ verify_ssl = true name = "pypi" [packages] -django = "<2.0,>=1.11" +django = "<2.1,>=2.0" pillow = "*" coveralls = "*" dateparser = "*" @@ -12,7 +12,6 @@ django-cors-headers = "*" django-crispy-forms = "*" django-extensions = "*" django-filter = "*" -django-flat-responsive = "*" djangorestframework = "*" factory-boy = "*" filemagic = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 0b6afe9f1..614ee0e78 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f38e72c2d07bd711cf7b3dd168e4fa39df3e8b86f790bda8c2c27a762c6f7447" + "sha256": "e20c2294bcafd346ee57901df94a515a12976ed192dc37df848b39b56bdd1f4b" }, "pipfile-spec": 6, "requires": {}, @@ -106,11 +106,11 @@ }, "django": { "hashes": [ - "sha256:8176ac7985fe6737ce3d6b2531b4a2453cb7c3377c9db00bacb2b3320f4a1311", - "sha256:b18235d82426f09733d2de9910cee975cf52ff05e5f836681eb957d105a05a40" + "sha256:0c5b65847d00845ee404bbc0b4a85686f15eb3001ffddda3db4e9baa265bf136", + "sha256:68aeea369a8130259354b6ba1fa9babe0c5ee6bced505dea4afcd00f765ae38b" ], "index": "pypi", - "version": "==1.11.15" + "version": "==2.0.8" }, "django-cors-headers": { "hashes": [ @@ -144,13 +144,6 @@ "index": "pypi", "version": "==2.0.0" }, - "django-flat-responsive": { - "hashes": [ - "sha256:451caa2700c541b52fb7ce2d34d3d8dee9e980cf29f5463bc8a8c6256a1a6474" - ], - "index": "pypi", - "version": "==2.0" - }, "djangorestframework": { "hashes": [ "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9", diff --git a/docs/changelog.rst b/docs/changelog.rst index 84cc6bae9..d92c48b59 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,17 @@ Changelog ######### +2.2.0 +===== + +* Thanks to `dadosch`_ and `Wolfgang Mader`_, this is the first version of + Paperless that supports Django 2.0! As a result of their hard work, you can + now also run Paperless on Python 3.7 as well: `#386`_ & `#390`_. +* `Stéphane Brunner`_ added a few lines of code that made tagging interface a lot + easier on those of us with lots of different tags: `#391`_. +* `Kilian Koeltzsch`_ noticed a bug in how we capture & automatically create + tags, so that's fixed now too: `#384`_. + 2.1.0 ===== @@ -451,6 +462,10 @@ bulk of the work on this big change. .. _mcronce: https://github.com/mcronce .. _Enno Lohmeier: https://github.com/elohmeier .. _Mark McFate: https://github.com/SummittDweller +.. _dadosch: https://github.com/dadosch +.. _Wolfgang Mader: https://github.com/wmader +.. _Stéphane Brunner: https://github.com/sbrunner +.. _Kilian Koeltzsch: https://github.com/kiliankoe .. _#20: https://github.com/danielquinn/paperless/issues/20 .. _#44: https://github.com/danielquinn/paperless/issues/44 @@ -525,6 +540,10 @@ bulk of the work on this big change. .. _#374: https://github.com/danielquinn/paperless/pull/374 .. _#375: https://github.com/danielquinn/paperless/pull/375 .. _#376: https://github.com/danielquinn/paperless/pull/376 +.. _#384: https://github.com/danielquinn/paperless/issues/384 +.. _#386: https://github.com/danielquinn/paperless/issues/386 +.. _#391: https://github.com/danielquinn/paperless/pull/391 +.. _#390: https://github.com/danielquinn/paperless/pull/390 .. _pipenv: https://docs.pipenv.org/ .. _a new home on Docker Hub: https://hub.docker.com/r/danielquinn/paperless/ diff --git a/requirements.txt b/requirements.txt index d1b27923f..247d9993a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,8 +11,7 @@ django-cors-headers==2.4.0 django-crispy-forms==1.7.2 django-extensions==2.1.2 django-filter==2.0.0 -django-flat-responsive==2.0 -django==1.11.15 +django==2.0.8 djangorestframework==3.8.2 docopt==0.6.2 execnet==1.5.0; python_version != '3.1.*' diff --git a/src/documents/admin.py b/src/documents/admin.py index d996b24cf..36154b6ba 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -3,8 +3,13 @@ from django.conf import settings from django.contrib import admin from django.contrib.auth.models import User, Group -from django.core.urlresolvers import reverse +try: + from django.core.urlresolvers import reverse +except ImportError: + from django.urls import reverse from django.templatetags.static import static +from django.utils.safestring import mark_safe +from django.utils.html import format_html, format_html_join from .models import Correspondent, Tag, Document, Log @@ -178,7 +183,7 @@ def tags_(self, obj): ) } ) - return r + return mark_safe(r) tags_.allow_tags = True def document(self, obj): @@ -198,16 +203,13 @@ def document(self, obj): @staticmethod def _html_tag(kind, inside=None, **kwargs): - - attributes = [] - for lft, rgt in kwargs.items(): - attributes.append('{}="{}"'.format(lft, rgt)) + attributes = format_html_join(' ', '{}="{}"', kwargs.items()) if inside is not None: - return "<{kind} {attributes}>{inside}".format( - kind=kind, attributes=" ".join(attributes), inside=inside) + return format_html("<{kind} {attributes}>{inside}", + kind=kind, attributes=attributes, inside=inside) - return "<{} {}/>".format(kind, " ".join(attributes)) + return format_html("<{} {}/>", kind, attributes) class LogAdmin(CommonAdmin): diff --git a/src/documents/filters.py b/src/documents/filters.py index 2ed7f29db..68861d967 100644 --- a/src/documents/filters.py +++ b/src/documents/filters.py @@ -5,7 +5,7 @@ class CorrespondentFilterSet(FilterSet): - class Meta(object): + class Meta: model = Correspondent fields = { "name": [ @@ -18,7 +18,7 @@ class Meta(object): class TagFilterSet(FilterSet): - class Meta(object): + class Meta: model = Tag fields = { "name": [ @@ -42,15 +42,18 @@ class DocumentFilterSet(FilterSet): ) } - correspondent__name = CharFilter(name="correspondent__name", **CHAR_KWARGS) - correspondent__slug = CharFilter(name="correspondent__slug", **CHAR_KWARGS) - tags__name = CharFilter(name="tags__name", **CHAR_KWARGS) - tags__slug = CharFilter(name="tags__slug", **CHAR_KWARGS) - tags__empty = BooleanFilter(name='tags', - lookup_expr='isnull', - distinct=True) + correspondent__name = CharFilter( + field_name="correspondent__name", **CHAR_KWARGS) + correspondent__slug = CharFilter( + field_name="correspondent__slug", **CHAR_KWARGS) + tags__name = CharFilter( + field_name="tags__name", **CHAR_KWARGS) + tags__slug = CharFilter( + field_name="tags__slug", **CHAR_KWARGS) + tags__empty = BooleanFilter( + field_name="tags", lookup_expr="isnull", distinct=True) - class Meta(object): + class Meta: model = Document fields = { "title": [ diff --git a/src/documents/migrations/0003_sender.py b/src/documents/migrations/0003_sender.py index ce2508994..27eead032 100644 --- a/src/documents/migrations/0003_sender.py +++ b/src/documents/migrations/0003_sender.py @@ -32,7 +32,6 @@ def realign_senders(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('documents', '0002_auto_20151226_1316'), ] diff --git a/src/documents/migrations/0011_auto_20160303_1929.py b/src/documents/migrations/0011_auto_20160303_1929.py index af4ee4c66..7b77a8835 100644 --- a/src/documents/migrations/0011_auto_20160303_1929.py +++ b/src/documents/migrations/0011_auto_20160303_1929.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): - + atomic = False dependencies = [ ('documents', '0010_log'), ] diff --git a/src/documents/migrations/0012_auto_20160305_0040.py b/src/documents/migrations/0012_auto_20160305_0040.py index 5168c9206..a0b4b27af 100644 --- a/src/documents/migrations/0012_auto_20160305_0040.py +++ b/src/documents/migrations/0012_auto_20160305_0040.py @@ -112,7 +112,6 @@ def move_documents_and_create_thumbnails(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('documents', '0011_auto_20160303_1929'), ] diff --git a/src/documents/migrations/0014_document_checksum.py b/src/documents/migrations/0014_document_checksum.py index 167245dea..bc563cf86 100644 --- a/src/documents/migrations/0014_document_checksum.py +++ b/src/documents/migrations/0014_document_checksum.py @@ -128,7 +128,6 @@ def do_nothing(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('documents', '0013_auto_20160325_2111'), ] diff --git a/src/documents/migrations/0019_add_consumer_user.py b/src/documents/migrations/0019_add_consumer_user.py index a3d7d787e..bc52ae7f6 100644 --- a/src/documents/migrations/0019_add_consumer_user.py +++ b/src/documents/migrations/0019_add_consumer_user.py @@ -15,7 +15,6 @@ def reverse_func(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ('documents', '0018_auto_20170715_1712'), ] diff --git a/src/documents/migrations/0020_document_added.py b/src/documents/migrations/0020_document_added.py index dbddf80ae..bd3566481 100644 --- a/src/documents/migrations/0020_document_added.py +++ b/src/documents/migrations/0020_document_added.py @@ -11,8 +11,8 @@ def set_added_time_to_created_time(apps, schema_editor): doc.added = doc.created doc.save() -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ ('documents', '0019_add_consumer_user'), ] diff --git a/src/documents/models.py b/src/documents/models.py index 2ee446ae6..36466bbac 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -10,7 +10,10 @@ from fuzzywuzzy import fuzz from django.conf import settings -from django.core.urlresolvers import reverse +try: + from django.core.urlresolvers import reverse +except ImportError: + from django.urls import reverse from django.db import models from django.template.defaultfilters import slugify from django.utils import timezone diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 504999276..f71a0c5ab 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -5,14 +5,14 @@ class CorrespondentSerializer(serializers.HyperlinkedModelSerializer): - class Meta(object): + class Meta: model = Correspondent fields = ("id", "slug", "name") class TagSerializer(serializers.HyperlinkedModelSerializer): - class Meta(object): + class Meta: model = Tag fields = ( "id", "slug", "name", "colour", "match", "matching_algorithm") @@ -34,7 +34,7 @@ class DocumentSerializer(serializers.ModelSerializer): view_name="drf:correspondent-detail", allow_null=True) tags = TagsField(view_name="drf:tag-detail", many=True) - class Meta(object): + class Meta: model = Document fields = ( "id", @@ -57,7 +57,7 @@ class LogSerializer(serializers.ModelSerializer): time = serializers.DateTimeField() messages = serializers.CharField() - class Meta(object): + class Meta: model = Log fields = ( "time", diff --git a/src/paperless/settings.py b/src/paperless/settings.py index e40af01d1..2eb6d655a 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -68,7 +68,6 @@ "reminders.apps.RemindersConfig", "paperless_tesseract.apps.PaperlessTesseractConfig", - "flat_responsive", # TODO: Remove as of Django 2.x "django.contrib.admin", "rest_framework", @@ -82,14 +81,13 @@ -MIDDLEWARE_CLASSES = [ +MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] @@ -99,9 +97,9 @@ # If auth is disabled, we just use our "bypass" authentication middleware if bool(os.getenv("PAPERLESS_DISABLE_LOGIN", "false").lower() in ("yes", "y", "1", "t", "true")): - _index = MIDDLEWARE_CLASSES.index("django.contrib.auth.middleware.AuthenticationMiddleware") - MIDDLEWARE_CLASSES[_index] = "paperless.middleware.Middleware" - MIDDLEWARE_CLASSES.remove("django.contrib.auth.middleware.SessionAuthenticationMiddleware") + _index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware") + MIDDLEWARE[_index] = "paperless.middleware.Middleware" + MIDDLEWARE.remove("django.contrib.auth.middleware.SessionAuthenticationMiddleware") ROOT_URLCONF = 'paperless.urls' diff --git a/src/paperless/urls.py b/src/paperless/urls.py index e5e559f12..f66ce6664 100644 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -28,9 +28,11 @@ # API url( r"^api/auth/", - include('rest_framework.urls', namespace="rest_framework") + include( + ('rest_framework.urls', 'rest_framework'), + namespace="rest_framework") ), - url(r"^api/", include(router.urls, namespace="drf")), + url(r"^api/", include((router.urls, 'drf'), namespace="drf")), # File downloads url( diff --git a/src/paperless/version.py b/src/paperless/version.py index fc2d6561c..2de334c18 100644 --- a/src/paperless/version.py +++ b/src/paperless/version.py @@ -1 +1 @@ -__version__ = (2, 1, 0) +__version__ = (2, 2, 0) diff --git a/src/reminders/models.py b/src/reminders/models.py index d6fb744f7..77d872afb 100644 --- a/src/reminders/models.py +++ b/src/reminders/models.py @@ -3,6 +3,8 @@ class Reminder(models.Model): - document = models.ForeignKey("documents.Document") + document = models.ForeignKey( + "documents.Document", on_delete=models.PROTECT + ) date = models.DateTimeField() note = models.TextField(blank=True)