From d80e272b7570b7ae3b951b03bd0774f169b22cda Mon Sep 17 00:00:00 2001 From: David Martin Date: Thu, 24 Aug 2017 20:20:00 +1000 Subject: [PATCH 1/6] Add a basic financial year filter for the document overview. For now we simply hardcode the dates for the AU financial years. We simply show a list of financial years and filter the documents accordingly. --- src/documents/admin.py | 48 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/documents/admin.py b/src/documents/admin.py index 7cba10c74..4304293f0 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -1,3 +1,5 @@ +from datetime import datetime + from django.conf import settings from django.contrib import admin from django.contrib.auth.models import User, Group @@ -32,6 +34,50 @@ class MonthListFilter(admin.SimpleListFilter): return queryset.filter(created__year=year, created__month=month) +class FinancialYearFilter(admin.SimpleListFilter): + + title = "Financial Year" + parameter_name = "fy" + + def _fy_start(self, year): + """Return date of the start of financial year for the given year.""" + fy_start = "{}-07-01".format(str(year)) + return datetime.strptime(fy_start, "%Y-%m-%d").date() + + def _fy_end(self, year): + """Return date of the end of financial year for the given year.""" + fy_end = "{}-06-30".format(str(year)) + return datetime.strptime(fy_end, "%Y-%m-%d").date() + + def _determine_fy(self, date): + """Return a (query, display) financial year tuple of the given date.""" + fy_start = self._fy_start(date.year) + + if date.date() >= fy_start: + query = "{}-{}".format(date.year, date.year + 1) + else: + query = "{}-{}".format(date.year - 1, date.year) + + # To keep it simple we use the same String for both query parameters + # and the display. + return (query, query) + + def lookups(self, request, model_admin): + r = [] + for document in Document.objects.all(): + r.append(self._determine_fy(document.created)) + + return sorted(set(r), key=lambda x: x[0], reverse=True) + + def queryset(self, request, queryset): + if not self.value(): + return None + + start, end = self.value().split("-") + return queryset.filter(created__gte=self._fy_start(start), + created__lte=self._fy_end(end)) + + class CommonAdmin(admin.ModelAdmin): list_per_page = settings.PAPERLESS_LIST_PER_PAGE @@ -59,7 +105,7 @@ class DocumentAdmin(CommonAdmin): search_fields = ("correspondent__name", "title", "content") list_display = ("title", "created", "thumbnail", "correspondent", "tags_") - list_filter = ("tags", "correspondent", MonthListFilter) + list_filter = ("tags", "correspondent", FinancialYearFilter, MonthListFilter) ordering = ["-created", "correspondent"] def has_add_permission(self, request): From 24fb6cefb9c8653a5c2302323d5c03956ae874a7 Mon Sep 17 00:00:00 2001 From: David Martin Date: Thu, 24 Aug 2017 20:51:09 +1000 Subject: [PATCH 2/6] Add config settings to set the start and the end of the financial year. Now we allow to filter for any financial year dates. Note that we also only show the financial year filter if the dates are actually set. --- paperless.conf.example | 6 ++++++ src/documents/admin.py | 12 +++++++++--- src/paperless/settings.py | 3 +++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/paperless.conf.example b/paperless.conf.example index a17aac327..bb8259348 100644 --- a/paperless.conf.example +++ b/paperless.conf.example @@ -162,6 +162,12 @@ PAPERLESS_PASSPHRASE="secret" #PAPERLESS_TIME_ZONE=UTC +# If set, Paperless will show document filters per financial year. +# The dates must be in the format "mm-dd", for example "07-15" for July 15. +#PAPERLESS_FINANCIAL_YEAR_START="mm-dd" +#PAPERLESS_FINANCIAL_YEAR_END="mm-dd" + + # The number of items on each page in the web UI. This value must be a # positive integer, but if you don't define one in paperless.conf, a default of # 100 will be used. diff --git a/src/documents/admin.py b/src/documents/admin.py index 4304293f0..17f78ffb3 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -41,12 +41,14 @@ class FinancialYearFilter(admin.SimpleListFilter): def _fy_start(self, year): """Return date of the start of financial year for the given year.""" - fy_start = "{}-07-01".format(str(year)) + assert settings.FY_START + fy_start = "{}-{}".format(str(year), settings.FY_START) return datetime.strptime(fy_start, "%Y-%m-%d").date() def _fy_end(self, year): """Return date of the end of financial year for the given year.""" - fy_end = "{}-06-30".format(str(year)) + assert settings.FY_END + fy_end = "{}-{}".format(str(year), settings.FY_END) return datetime.strptime(fy_end, "%Y-%m-%d").date() def _determine_fy(self, date): @@ -105,7 +107,11 @@ class DocumentAdmin(CommonAdmin): search_fields = ("correspondent__name", "title", "content") list_display = ("title", "created", "thumbnail", "correspondent", "tags_") - list_filter = ("tags", "correspondent", FinancialYearFilter, MonthListFilter) + list_filter = ("tags", "correspondent") + if settings.FY_START and settings.FY_END: + list_filter += (FinancialYearFilter,) + list_filter += (MonthListFilter,) + ordering = ["-created", "correspondent"] def has_add_permission(self, request): diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 2e525ae41..f7b2244a7 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -254,3 +254,6 @@ POST_CONSUME_SCRIPT = os.getenv("PAPERLESS_POST_CONSUME_SCRIPT") # positive integer, but if you don't define one in paperless.conf, a default of # 100 will be used. PAPERLESS_LIST_PER_PAGE = int(os.getenv("PAPERLESS_LIST_PER_PAGE", 100)) + +FY_START = os.getenv("PAPERLESS_FINANCIAL_YEAR_START") +FY_END = os.getenv("PAPERLESS_FINANCIAL_YEAR_END") From c65c5009e44f737dc9c407056038f49cacac47c7 Mon Sep 17 00:00:00 2001 From: David Martin Date: Fri, 25 Aug 2017 17:36:09 +1000 Subject: [PATCH 3/6] Return no filter results if financial year dates are not set. This is a lot cleaner than trying to hack around whether or not the FinancialYearFilter is part of the available filters. This way it will show up if there are result for it and the dates are set, and it will not if any of those conditions is not set. --- src/documents/admin.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/documents/admin.py b/src/documents/admin.py index 17f78ffb3..7c4938c64 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -41,13 +41,11 @@ class FinancialYearFilter(admin.SimpleListFilter): def _fy_start(self, year): """Return date of the start of financial year for the given year.""" - assert settings.FY_START fy_start = "{}-{}".format(str(year), settings.FY_START) return datetime.strptime(fy_start, "%Y-%m-%d").date() def _fy_end(self, year): """Return date of the end of financial year for the given year.""" - assert settings.FY_END fy_end = "{}-{}".format(str(year), settings.FY_END) return datetime.strptime(fy_end, "%Y-%m-%d").date() @@ -65,6 +63,9 @@ class FinancialYearFilter(admin.SimpleListFilter): return (query, query) def lookups(self, request, model_admin): + if not settings.FY_START or not settings.FY_END: + return None + r = [] for document in Document.objects.all(): r.append(self._determine_fy(document.created)) @@ -72,7 +73,7 @@ class FinancialYearFilter(admin.SimpleListFilter): return sorted(set(r), key=lambda x: x[0], reverse=True) def queryset(self, request, queryset): - if not self.value(): + if not self.value() or not settings.FY_START or not settings.FY_END: return None start, end = self.value().split("-") @@ -107,10 +108,8 @@ class DocumentAdmin(CommonAdmin): search_fields = ("correspondent__name", "title", "content") list_display = ("title", "created", "thumbnail", "correspondent", "tags_") - list_filter = ("tags", "correspondent") - if settings.FY_START and settings.FY_END: - list_filter += (FinancialYearFilter,) - list_filter += (MonthListFilter,) + list_filter = ("tags", "correspondent", FinancialYearFilter, + MonthListFilter) ordering = ["-created", "correspondent"] From 1cd76634a358875dcd3a7067fbb3b21b0be68151 Mon Sep 17 00:00:00 2001 From: David Martin Date: Fri, 25 Aug 2017 20:27:39 +1000 Subject: [PATCH 4/6] Take non-wrapping financial years into account. The German financial year for example goes from January to December. In those cases we simply only show the year in the overview. --- src/documents/admin.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/documents/admin.py b/src/documents/admin.py index 7c4938c64..85c04405a 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -49,18 +49,33 @@ class FinancialYearFilter(admin.SimpleListFilter): fy_end = "{}-{}".format(str(year), settings.FY_END) return datetime.strptime(fy_end, "%Y-%m-%d").date() + def _fy_does_wrap(self): + """Return whether the financial year spans across two years.""" + start = "{}".format(settings.FY_START) + start = datetime.strptime(start, "%m-%d").date() + end = "{}".format(settings.FY_END) + end = datetime.strptime(end, "%m-%d").date() + + return end < start + def _determine_fy(self, date): """Return a (query, display) financial year tuple of the given date.""" fy_start = self._fy_start(date.year) - if date.date() >= fy_start: - query = "{}-{}".format(date.year, date.year + 1) - else: - query = "{}-{}".format(date.year - 1, date.year) + if self._fy_does_wrap(): + if date.date() >= fy_start: + query = "{}-{}".format(date.year, date.year + 1) + else: + query = "{}-{}".format(date.year - 1, date.year) - # To keep it simple we use the same String for both query parameters - # and the display. - return (query, query) + # To keep it simple we use the same string for both + # query parameter and the display. + return (query, query) + + else: + query = "{0}-{0}".format(date.year) + display = "{}".format(date.year) + return (query, display) def lookups(self, request, model_admin): if not settings.FY_START or not settings.FY_END: From 360d1e280227e3845d02ced5a4611e9448266bc8 Mon Sep 17 00:00:00 2001 From: David Martin Date: Sat, 26 Aug 2017 19:45:39 +1000 Subject: [PATCH 5/6] Store whether financial year wraps instead of re-determining it. It either wraps or it does not depending on how it is set in the config. There is no point in determining it again for each document. Instead we simply store it as a member variable the first time we check. --- src/documents/admin.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/documents/admin.py b/src/documents/admin.py index 85c04405a..e96e0f04a 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -38,6 +38,7 @@ class FinancialYearFilter(admin.SimpleListFilter): title = "Financial Year" parameter_name = "fy" + _fy_wraps = None def _fy_start(self, year): """Return date of the start of financial year for the given year.""" @@ -51,12 +52,14 @@ class FinancialYearFilter(admin.SimpleListFilter): def _fy_does_wrap(self): """Return whether the financial year spans across two years.""" - start = "{}".format(settings.FY_START) - start = datetime.strptime(start, "%m-%d").date() - end = "{}".format(settings.FY_END) - end = datetime.strptime(end, "%m-%d").date() + if self._fy_wraps is None: + start = "{}".format(settings.FY_START) + start = datetime.strptime(start, "%m-%d").date() + end = "{}".format(settings.FY_END) + end = datetime.strptime(end, "%m-%d").date() + self._fy_wraps = end < start - return end < start + return self._fy_wraps def _determine_fy(self, date): """Return a (query, display) financial year tuple of the given date.""" From 67b492bcb7c42ff525fed161f4e88df92fbc833d Mon Sep 17 00:00:00 2001 From: David Martin Date: Sat, 26 Aug 2017 19:50:57 +1000 Subject: [PATCH 6/6] Determine the start of the financial only for wrapping years. If the financial year is from Jan to Dec there we do not need to determine the start to see which year it falls into. --- src/documents/admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/documents/admin.py b/src/documents/admin.py index e96e0f04a..e239a99cb 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -63,9 +63,9 @@ class FinancialYearFilter(admin.SimpleListFilter): def _determine_fy(self, date): """Return a (query, display) financial year tuple of the given date.""" - fy_start = self._fy_start(date.year) - if self._fy_does_wrap(): + fy_start = self._fy_start(date.year) + if date.date() >= fy_start: query = "{}-{}".format(date.year, date.year + 1) else: