Compare commits

...

26 Commits

Author SHA1 Message Date
Jonas Winkler
702b985ceb Merge pull request #558 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_de
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'de'
2021-02-17 14:46:25 +01:00
Jonas Winkler
7d87bcbb98 Merge pull request #560 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_en_GB
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'en_GB'
2021-02-17 14:46:14 +01:00
Jonas Winkler
340521aa0d Merge pull request #559 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_nl_NL
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'nl_NL'
2021-02-17 14:46:03 +01:00
transifex-integration[bot]
7bc557a999 Apply translations in en_GB
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'en_GB' language.
2021-02-17 13:36:15 +00:00
jonaswinkler
dfa7cdf47e Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-17 14:26:26 +01:00
jonaswinkler
0d78e58d77 fixed paperless not properly selecting en-gb 2021-02-17 14:26:06 +01:00
transifex-integration[bot]
58df3d5767 Apply translations in nl_NL
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'nl_NL' language.
2021-02-17 12:57:48 +00:00
Jonas Winkler
4e4d6e806c Update Crowdin configuration file 2021-02-17 13:22:45 +01:00
Jonas Winkler
6ff99945f3 Update Crowdin configuration file 2021-02-17 13:18:19 +01:00
transifex-integration[bot]
b7f1b9f8ad Apply translations in de
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'de' language.
2021-02-17 11:50:01 +00:00
jonaswinkler
08a44cf468 changelog and version 2021-02-17 12:31:19 +01:00
jonaswinkler
a1162d6d5a update requirements 2021-02-17 12:25:34 +01:00
jonaswinkler
1c81d88013 add support for iso 8601 date display 2021-02-17 12:15:22 +01:00
jonaswinkler
1e4ec7e29e added en-GB language 2021-02-16 14:54:18 +01:00
jonaswinkler
2c4e34dd0c changelog 2021-02-15 23:44:48 +01:00
jonaswinkler
cb308fae7b only show inbox statistics if inbox tags are defined 2021-02-15 23:14:54 +01:00
jonaswinkler
3f03d51b24 version bump 2021-02-15 16:52:45 +01:00
jonaswinkler
831db6ab87 note regarding Python 3.6 2021-02-15 16:46:06 +01:00
jonaswinkler
43fdf634f2 added a note regarding python 3.6 2021-02-15 16:37:44 +01:00
jonaswinkler
f07a6b4586 PAPERLESS_WEBSERVER_WORKERS option 2021-02-15 16:27:35 +01:00
jonaswinkler
2fcf484229 bugfix dismissing wrong status messages 2021-02-15 14:52:47 +01:00
jonaswinkler
8bf4241b16 some search index optimizations 2021-02-15 13:26:36 +01:00
jonaswinkler
56bd966c02 local import of ocrmypdf so that the webserver does not load that 2021-02-15 12:18:10 +01:00
jonaswinkler
416101d557 only import dateparser when required 2021-02-15 11:52:46 +01:00
jonaswinkler
c330cca2c9 remove unused imports 2021-02-15 11:26:13 +01:00
jonaswinkler
7e88085377 load sklearn modules only when training data has changed 2021-02-15 11:25:25 +01:00
34 changed files with 644 additions and 433 deletions

18
Pipfile.lock generated
View File

@@ -591,11 +591,11 @@
}, },
"ocrmypdf": { "ocrmypdf": {
"hashes": [ "hashes": [
"sha256:a54634d017a2f44aa2115b0b6ae5aa41a7cec018f5c53d16ad3abec1e70b3db7", "sha256:0f624456a50be0b0bc8c0b59704d159f637616c093a1cabe8bb383706561bcf7",
"sha256:d0e2da48d4abd90f48f0937b2cd4ba57503b56c603f5e3aa91e20e3b21a036cd" "sha256:b829ad640a6160423162012e094ee2f7cd074ec99efadd7f7486954ec9182985"
], ],
"index": "pypi", "index": "pypi",
"version": "==11.6.0" "version": "==11.6.2"
}, },
"pathvalidate": { "pathvalidate": {
"hashes": [ "hashes": [
@@ -841,11 +841,11 @@
}, },
"python-magic": { "python-magic": {
"hashes": [ "hashes": [
"sha256:356efa93c8899047d1eb7d3eb91e871ba2f5b1376edbaf4cc305e3c872207355", "sha256:8551e804c09a3398790bd9e392acb26554ae2609f29c72abb0b9dee9a5571eae",
"sha256:b757db2a5289ea3f1ced9e60f072965243ea43a2221430048fd8cacab17be0ce" "sha256:ca884349f2c92ce830e3f498c5b7c7051fe2942c3ee4332f65213b8ebff15a62"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.4.18" "version": "==0.4.22"
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
@@ -1837,11 +1837,11 @@
}, },
"tox": { "tox": {
"hashes": [ "hashes": [
"sha256:65d0e90ceb816638a50d64f4b47b11da767b284c0addda2294cb3cd69bd72425", "sha256:89afa9c59c04beb55eda789c7a65feb1a70fde117f85f1bd1c27c66758456e60",
"sha256:cf7fef81a3a2434df4d7af2a6d1bf606d2970220addfbe7dea2615bd4bb2c252" "sha256:ed1e650cf6368bcbc4a071eeeba363c480920e0ed8a9ad1793c7caaa5ad33d49"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.21.4" "version": "==3.22.0"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [

View File

@@ -1,5 +1,5 @@
files: files:
- source: /src/locale/en-us/LC_MESSAGES/django.po - source: /src/locale/en_US/LC_MESSAGES/django.po
translation: /src/locale/%two_letters_code%/LC_MESSAGES/django.po translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po
- source: /src-ui/messages.xlf - source: /src-ui/messages.xlf
translation: /src-ui/src/locale/messages.%two_letters_code%.xlf translation: /src-ui/src/locale/messages.%locale_with_underscore%.xlf

View File

@@ -5,6 +5,34 @@
Changelog Changelog
********* *********
paperless-ng 1.1.4
##################
* Added English (GB) locale.
* Added ISO-8601 date display option.
.. note::
Some packages that paperless depends on are slowly dropping Python 3.6
support one after another, including the web server. Supporting Python
3.6 means that I cannot update these packages anymore.
At some point, paperless will drop Python 3.6 support. If using a bare
metal installation and you're still on Python 3.6, upgrade to 3.7 or newer.
If using docker, this does not affect you.
paperless-ng 1.1.3
##################
* Added a docker-specific configuration option to adjust the number of
worker processes of the web server. See :ref:`configuration-docker`.
* Some more memory usage optimizations.
* Don't show inbox statistics if no inbox tag is defined.
paperless-ng 1.1.2 paperless-ng 1.1.2
################## ##################

View File

@@ -555,3 +555,65 @@ PAPERLESS_GS_BINARY=<path>
PAPERLESS_OPTIPNG_BINARY=<path> PAPERLESS_OPTIPNG_BINARY=<path>
Defaults to "/usr/bin/optipng". Defaults to "/usr/bin/optipng".
.. _configuration-docker:
Docker-specific options
#######################
These options don't have any effect in ``paperless.conf``. These options adjust
the behavior of the docker container. Configure these in `docker-compose.env`.
PAPERLESS_WEBSERVER_WORKERS=<num>
The number of worker processes the webserver should spawn. More worker processes
usually result in the front end to load data much quicker. However, each worker process
also loads the entire application into memory separately, so increasing this value
will increase RAM usage.
Consider configuring this to 1 on low power devices with limited amount of RAM.
Defaults to 2.
USERMAP_UID=<uid>
The ID of the paperless user in the container. Set this to your actual user ID on the
host system, which you can get by executing
.. code:: shell-session
$ id -u
Paperless will change ownership on its folders to this user, so you need to get this right
in order to be able to write to the consumption directory.
Defaults to 1000.
USERMAP_GID=<gid>
The ID of the paperless Group in the container. Set this to your actual group ID on the
host system, which you can get by executing
.. code:: shell-session
$ id -g
Paperless will change ownership on its folders to this group, so you need to get this right
in order to be able to write to the consumption directory.
Defaults to 1000.
PAPERLESS_OCR_LANGUAGES=<list>
Additional OCR languages to install. By default, paperless comes with
English, German, Italian, Spanish and French. If your language is not in this list, install
additional languages with this configuration option:
.. code:: bash
PAPERLESS_OCR_LANGUAGES=tur ces
To actually use these languages, also set the default OCR language of paperless:
.. code:: bash
PAPERLESS_OCR_LANGUAGE=tur
Defaults to none, which does not install any additional languages.

View File

@@ -763,7 +763,8 @@ configuring some options in paperless can help improve performance immensely:
* Stick with SQLite to save some resources. * Stick with SQLite to save some resources.
* Consider setting ``PAPERLESS_OCR_PAGES`` to 1, so that paperless will only OCR * Consider setting ``PAPERLESS_OCR_PAGES`` to 1, so that paperless will only OCR
the first page of your documents. the first page of your documents. In most cases, this page contains enough
information to be able to find it.
* ``PAPERLESS_TASK_WORKERS`` and ``PAPERLESS_THREADS_PER_WORKER`` are configured * ``PAPERLESS_TASK_WORKERS`` and ``PAPERLESS_THREADS_PER_WORKER`` are configured
to use all cores. The Raspberry Pi models 3 and up have 4 cores, meaning that to use all cores. The Raspberry Pi models 3 and up have 4 cores, meaning that
paperless will use 2 workers and 2 threads per worker. This may result in paperless will use 2 workers and 2 threads per worker. This may result in
@@ -776,6 +777,8 @@ configuring some options in paperless can help improve performance immensely:
file generation for already ocr'ed documents entirely. file generation for already ocr'ed documents entirely.
* Set ``PAPERLESS_OPTIMIZE_THUMBNAILS`` to 'false' if you want faster consumption * Set ``PAPERLESS_OPTIMIZE_THUMBNAILS`` to 'false' if you want faster consumption
times. Thumbnails will be about 20% larger. times. Thumbnails will be about 20% larger.
* If using docker, consider setting ``PAPERLESS_WEBSERVER_WORKERS`` to
1. This will save some memory.
For details, refer to :ref:`configuration`. For details, refer to :ref:`configuration`.

View File

@@ -1,5 +1,7 @@
import os
bind = '0.0.0.0:8000' bind = '0.0.0.0:8000'
workers = 2 workers = int(os.getenv("PAPERLESS_WEBSERVER_WORKERS", 2))
worker_class = 'uvicorn.workers.UvicornWorker' worker_class = 'uvicorn.workers.UvicornWorker'
timeout = 120 timeout = 120

View File

@@ -53,7 +53,7 @@ langdetect==1.0.8
lxml==4.6.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' lxml==4.6.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
msgpack==1.0.2 msgpack==1.0.2
numpy==1.19.5 numpy==1.19.5
ocrmypdf==11.6.0 ocrmypdf==11.6.2
pathvalidate==2.3.2 pathvalidate==2.3.2
pdfminer.six==20201018; python_version >= '3.4' pdfminer.six==20201018; python_version >= '3.4'
pdftotext==2.1.5 pdftotext==2.1.5
@@ -71,7 +71,7 @@ python-dateutil==2.8.1
python-dotenv==0.15.0 python-dotenv==0.15.0
python-gnupg==0.4.6 python-gnupg==0.4.6
python-levenshtein==0.12.2 python-levenshtein==0.12.2
python-magic==0.4.18 python-magic==0.4.22
pytz==2021.1 pytz==2021.1
pyyaml==5.4.1 pyyaml==5.4.1
redis==3.5.3 redis==3.5.3

View File

@@ -18,7 +18,8 @@
"locales": { "locales": {
"de": "src/locale/messages.de.xlf", "de": "src/locale/messages.de.xlf",
"nl-NL": "src/locale/messages.nl_NL.xlf", "nl-NL": "src/locale/messages.nl_NL.xlf",
"fr": "src/locale/messages.fr.xlf" "fr": "src/locale/messages.fr.xlf",
"en-GB": "src/locale/messages.en_GB.xlf"
} }
}, },
"architect": { "architect": {

View File

@@ -64,10 +64,12 @@ import { CustomDatePipe } from './pipes/custom-date.pipe';
import localeFr from '@angular/common/locales/fr'; import localeFr from '@angular/common/locales/fr';
import localeNl from '@angular/common/locales/nl'; import localeNl from '@angular/common/locales/nl';
import localeDe from '@angular/common/locales/de'; import localeDe from '@angular/common/locales/de';
import localeEnGb from '@angular/common/locales/en-GB';
registerLocaleData(localeFr) registerLocaleData(localeFr)
registerLocaleData(localeNl) registerLocaleData(localeNl)
registerLocaleData(localeDe) registerLocaleData(localeDe)
registerLocaleData(localeEnGb)
@NgModule({ @NgModule({
declarations: [ declarations: [

View File

@@ -1,6 +1,6 @@
<app-widget-frame title="Statistics" i18n-title> <app-widget-frame title="Statistics" i18n-title>
<ng-container content> <ng-container content>
<p class="card-text" i18n>Documents in inbox: {{statistics.documents_inbox}}</p> <p class="card-text" i18n *ngIf="statistics?.documents_inbox != null">Documents in inbox: {{statistics?.documents_inbox}}</p>
<p class="card-text" i18n>Total documents: {{statistics.documents_total}}</p> <p class="card-text" i18n>Total documents: {{statistics?.documents_total}}</p>
</ng-container> </ng-container>
</app-widget-frame> </app-widget-frame>

View File

@@ -34,7 +34,7 @@
<div class="col"> <div class="col">
<select class="form-control" formControlName="dateLocale"> <select class="form-control" formControlName="dateLocale">
<option *ngFor="let lang of dateLocaleOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> - {{today | date:'shortDate':null:lang.code}}</span></option> <option *ngFor="let lang of dateLocaleOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> - {{today | customDate:'shortDate':null:lang.code}}</span></option>
</select> </select>
</div> </div>
@@ -167,7 +167,7 @@
</li> </li>
</ul> </ul>
<div [ngbNavOutlet]="nav" class="border-left border-right border-bottom p-3 mb-3 shadow"></div> <div [ngbNavOutlet]="nav" class="border-left border-right border-bottom p-3 mb-3 shadow-sm"></div>
<button type="submit" class="btn btn-primary" i18n>Save</button> <button type="submit" class="btn btn-primary" i18n>Save</button>
</form> </form>

View File

@@ -35,7 +35,7 @@ export class SettingsComponent implements OnInit {
savedViews: PaperlessSavedView[] savedViews: PaperlessSavedView[]
get computedDateLocale(): string { get computedDateLocale(): string {
return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage || this.currentLocale
} }
constructor( constructor(
@@ -92,7 +92,10 @@ export class SettingsComponent implements OnInit {
} }
get dateLocaleOptions(): LanguageOption[] { get dateLocaleOptions(): LanguageOption[] {
return [{code: "", name: $localize`Use date format of display language`}].concat(this.settings.getLanguageOptions()) return [
{code: "", name: $localize`Use date format of display language`},
{code: "iso-8601", name: $localize`ISO 8601`}
].concat(this.settings.getLanguageOptions())
} }
get today() { get today() {

View File

@@ -2,18 +2,29 @@ import { DatePipe } from '@angular/common';
import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core'; import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core';
import { SettingsService, SETTINGS_KEYS } from '../services/settings.service'; import { SettingsService, SETTINGS_KEYS } from '../services/settings.service';
const FORMAT_TO_ISO_FORMAT = {
"longDate": "y-MM-dd",
"mediumDate": "yy-MM-dd",
"shortDate": "yy-MM-dd"
}
@Pipe({ @Pipe({
name: 'customDate' name: 'customDate'
}) })
export class CustomDatePipe extends DatePipe implements PipeTransform { export class CustomDatePipe extends DatePipe implements PipeTransform {
constructor(@Inject(LOCALE_ID) locale: string, private settings: SettingsService) { constructor(@Inject(LOCALE_ID) locale: string, private settings: SettingsService) {
super(settings.get(SETTINGS_KEYS.DATE_LOCALE) || locale) super(locale)
} }
transform(value: any, format?: string, timezone?: string, locale?: string): string | null { transform(value: any, format?: string, timezone?: string, locale?: string): string | null {
return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, locale) let l = locale || this.settings.get(SETTINGS_KEYS.DATE_LOCALE)
let f = format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT)
if (l == "iso-8601") {
return super.transform(value, FORMAT_TO_ISO_FORMAT[f], timezone)
} else {
return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, locale)
}
} }
} }

View File

@@ -169,7 +169,12 @@ export class ConsumerStatusService {
} }
dismiss(status: FileStatus) { dismiss(status: FileStatus) {
let index = this.consumerStatus.findIndex(s => s.filename == status.filename) let index
if (status.taskId != null) {
index = this.consumerStatus.findIndex(s => s.taskId == status.taskId)
} else {
index = this.consumerStatus.findIndex(s => s.filename == status.filename)
}
if (index > -1) { if (index > -1) {
this.consumerStatus.splice(index, 1) this.consumerStatus.splice(index, 1)

View File

@@ -79,7 +79,8 @@ export class SettingsService {
getLanguageOptions(): LanguageOption[] { getLanguageOptions(): LanguageOption[] {
return [ return [
{code: "en-US", name: $localize`English (US)`, englishName: "English (US)"}, {code: "en-us", name: $localize`English (US)`, englishName: "English (US)"},
{code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)"},
{code: "de", name: $localize`German`, englishName: "German"}, {code: "de", name: $localize`German`, englishName: "German"},
{code: "nl", name: $localize`Dutch`, englishName: "Dutch"}, {code: "nl", name: $localize`Dutch`, englishName: "Dutch"},
{code: "fr", name: $localize`French`, englishName: "French"} {code: "fr", name: $localize`French`, englishName: "French"}

View File

@@ -2,7 +2,7 @@ export const environment = {
production: true, production: true,
apiBaseUrl: "/api/", apiBaseUrl: "/api/",
appTitle: "Paperless-ng", appTitle: "Paperless-ng",
version: "1.1.2", version: "1.1.4",
webSocketHost: window.location.host, webSocketHost: window.location.host,
webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:") webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:")
}; };

View File

@@ -515,7 +515,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8fa4d523f7b91df4390120b85ed0406138273e1a"> <trans-unit datatype="html" id="8fa4d523f7b91df4390120b85ed0406138273e1a">
<source>Color</source> <source>Color</source>
<target>Color</target> <target>Colour</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context> <context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
<context context-type="linenumber">20</context> <context context-type="linenumber">20</context>

View File

@@ -1,10 +1,6 @@
from django.contrib import admin from django.contrib import admin
from django.utils.html import format_html, format_html_join
from django.utils.safestring import mark_safe
from whoosh.writing import AsyncWriter
from . import index from .models import Correspondent, Document, DocumentType, Tag, \
from .models import Correspondent, Document, DocumentType, Log, Tag, \
SavedView, SavedViewFilterRule SavedView, SavedViewFilterRule
@@ -86,17 +82,21 @@ class DocumentAdmin(admin.ModelAdmin):
created_.short_description = "Created" created_.short_description = "Created"
def delete_queryset(self, request, queryset): def delete_queryset(self, request, queryset):
ix = index.open_index() from documents import index
with AsyncWriter(ix) as writer:
with index.open_index_writer() as writer:
for o in queryset: for o in queryset:
index.remove_document(writer, o) index.remove_document(writer, o)
super(DocumentAdmin, self).delete_queryset(request, queryset) super(DocumentAdmin, self).delete_queryset(request, queryset)
def delete_model(self, request, obj): def delete_model(self, request, obj):
from documents import index
index.remove_document_from_index(obj) index.remove_document_from_index(obj)
super(DocumentAdmin, self).delete_model(request, obj) super(DocumentAdmin, self).delete_model(request, obj)
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
from documents import index
index.add_or_update_document(obj) index.add_or_update_document(obj)
super(DocumentAdmin, self).save_model(request, obj, form, change) super(DocumentAdmin, self).save_model(request, obj, form, change)

View File

@@ -2,9 +2,7 @@ import itertools
from django.db.models import Q from django.db.models import Q
from django_q.tasks import async_task from django_q.tasks import async_task
from whoosh.writing import AsyncWriter
from documents import index
from documents.models import Document, Correspondent, DocumentType from documents.models import Document, Correspondent, DocumentType
@@ -99,8 +97,9 @@ def modify_tags(doc_ids, add_tags, remove_tags):
def delete(doc_ids): def delete(doc_ids):
Document.objects.filter(id__in=doc_ids).delete() Document.objects.filter(id__in=doc_ids).delete()
ix = index.open_index() from documents import index
with AsyncWriter(ix) as writer:
with index.open_index_writer() as writer:
for id in doc_ids: for id in doc_ids:
index.remove_document_by_id(writer, id) index.remove_document_by_id(writer, id)

View File

@@ -95,9 +95,6 @@ class DocumentClassifier(object):
pickle.dump(self.document_type_classifier, f) pickle.dump(self.document_type_classifier, f)
def train(self): def train(self):
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import MultiLabelBinarizer, LabelBinarizer
data = list() data = list()
labels_tags = list() labels_tags = list()
@@ -162,6 +159,10 @@ class DocumentClassifier(object):
) )
) )
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import MultiLabelBinarizer, LabelBinarizer
# Step 2: vectorize data # Step 2: vectorize data
logger.debug("Vectorizing data...") logger.debug("Vectorizing data...")
self.data_vectorizer = CountVectorizer( self.data_vectorizer = CountVectorizer(

View File

@@ -86,6 +86,22 @@ def open_index(recreate=False):
return create_in(settings.INDEX_DIR, get_schema()) return create_in(settings.INDEX_DIR, get_schema())
@contextmanager
def open_index_writer(ix=None, optimize=False):
if ix:
writer = AsyncWriter(ix)
else:
writer = AsyncWriter(open_index())
try:
yield writer
except Exception as e:
logger.exception(str(e))
writer.cancel()
finally:
writer.commit(optimize=optimize)
def update_document(writer, doc): def update_document(writer, doc):
tags = ",".join([t.name for t in doc.tags.all()]) tags = ",".join([t.name for t in doc.tags.all()])
writer.update_document( writer.update_document(
@@ -110,14 +126,12 @@ def remove_document_by_id(writer, doc_id):
def add_or_update_document(document): def add_or_update_document(document):
ix = open_index() with open_index_writer() as writer:
with AsyncWriter(ix) as writer:
update_document(writer, document) update_document(writer, document)
def remove_document_from_index(document): def remove_document_from_index(document):
ix = open_index() with open_index_writer() as writer:
with AsyncWriter(ix) as writer:
remove_document(writer, document) remove_document(writer, document)

View File

@@ -6,7 +6,6 @@ import shutil
import subprocess import subprocess
import tempfile import tempfile
import dateparser
import magic import magic
from django.conf import settings from django.conf import settings
from django.utils import timezone from django.utils import timezone
@@ -200,6 +199,8 @@ def parse_date(filename, text):
""" """
Call dateparser.parse with a particular date ordering Call dateparser.parse with a particular date ordering
""" """
import dateparser
return dateparser.parse( return dateparser.parse(
ds, ds,
settings={ settings={

View File

@@ -11,7 +11,7 @@ from django.dispatch import receiver
from django.utils import timezone from django.utils import timezone
from filelock import FileLock from filelock import FileLock
from .. import index, matching from .. import matching
from ..file_handling import delete_empty_directories, \ from ..file_handling import delete_empty_directories, \
create_source_path_directory, \ create_source_path_directory, \
generate_unique_filename generate_unique_filename
@@ -305,4 +305,6 @@ def set_log_entry(sender, document=None, logging_group=None, **kwargs):
def add_to_index(sender, document, **kwargs): def add_to_index(sender, document, **kwargs):
from documents import index
index.add_or_update_document(document) index.add_or_update_document(document)

View File

@@ -4,6 +4,7 @@ from django.contrib.admin.sites import AdminSite
from django.test import TestCase from django.test import TestCase
from django.utils import timezone from django.utils import timezone
from documents import index
from documents.admin import DocumentAdmin from documents.admin import DocumentAdmin
from documents.models import Document from documents.models import Document
from documents.tests.utils import DirectoriesMixin from documents.tests.utils import DirectoriesMixin
@@ -11,37 +12,52 @@ from documents.tests.utils import DirectoriesMixin
class TestDocumentAdmin(DirectoriesMixin, TestCase): class TestDocumentAdmin(DirectoriesMixin, TestCase):
def get_document_from_index(self, doc):
ix = index.open_index()
with ix.searcher() as searcher:
return searcher.document(id=doc.id)
def setUp(self) -> None: def setUp(self) -> None:
super(TestDocumentAdmin, self).setUp() super(TestDocumentAdmin, self).setUp()
self.doc_admin = DocumentAdmin(model=Document, admin_site=AdminSite()) self.doc_admin = DocumentAdmin(model=Document, admin_site=AdminSite())
@mock.patch("documents.admin.index.add_or_update_document") def test_save_model(self):
def test_save_model(self, m):
doc = Document.objects.create(title="test") doc = Document.objects.create(title="test")
doc.title = "new title" doc.title = "new title"
self.doc_admin.save_model(None, doc, None, None) self.doc_admin.save_model(None, doc, None, None)
self.assertEqual(Document.objects.get(id=doc.id).title, "new title") self.assertEqual(Document.objects.get(id=doc.id).title, "new title")
m.assert_called_once() self.assertEqual(self.get_document_from_index(doc)['title'], "new title")
@mock.patch("documents.admin.index.remove_document") def test_delete_model(self):
def test_delete_model(self, m):
doc = Document.objects.create(title="test") doc = Document.objects.create(title="test")
self.doc_admin.delete_model(None, doc) index.add_or_update_document(doc)
self.assertRaises(Document.DoesNotExist, Document.objects.get, id=doc.id) self.assertIsNotNone(self.get_document_from_index(doc))
m.assert_called_once()
@mock.patch("documents.admin.index.remove_document") self.doc_admin.delete_model(None, doc)
def test_delete_queryset(self, m):
self.assertRaises(Document.DoesNotExist, Document.objects.get, id=doc.id)
self.assertIsNone(self.get_document_from_index(doc))
def test_delete_queryset(self):
docs = []
for i in range(42): for i in range(42):
Document.objects.create(title="Many documents with the same title", checksum=f"{i:02}") doc = Document.objects.create(title="Many documents with the same title", checksum=f"{i:02}")
docs.append(doc)
index.add_or_update_document(doc)
self.assertEqual(Document.objects.count(), 42) self.assertEqual(Document.objects.count(), 42)
for doc in docs:
self.assertIsNotNone(self.get_document_from_index(doc))
self.doc_admin.delete_queryset(None, Document.objects.all()) self.doc_admin.delete_queryset(None, Document.objects.all())
self.assertEqual(m.call_count, 42)
self.assertEqual(Document.objects.count(), 0) self.assertEqual(Document.objects.count(), 0)
for doc in docs:
self.assertIsNone(self.get_document_from_index(doc))
def test_created(self): def test_created(self):
doc = Document.objects.create(title="test", created=timezone.datetime(2020, 4, 12)) doc = Document.objects.create(title="test", created=timezone.datetime(2020, 4, 12))
self.assertEqual(self.doc_admin.created_(doc), "2020-04-12") self.assertEqual(self.doc_admin.created_(doc), "2020-04-12")

View File

@@ -442,6 +442,13 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
self.assertEqual(response.data['documents_total'], 3) self.assertEqual(response.data['documents_total'], 3)
self.assertEqual(response.data['documents_inbox'], 1) self.assertEqual(response.data['documents_inbox'], 1)
def test_statistics_no_inbox_tag(self):
Document.objects.create(title="none1", checksum="A")
response = self.client.get("/api/statistics/")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['documents_inbox'], None)
@mock.patch("documents.views.async_task") @mock.patch("documents.views.async_task")
def test_upload(self, m): def test_upload(self, m):

View File

@@ -32,7 +32,6 @@ from rest_framework.viewsets import (
ViewSet ViewSet
) )
import documents.index as index
from paperless.db import GnuPG from paperless.db import GnuPG
from paperless.views import StandardPagination from paperless.views import StandardPagination
from .classifier import load_classifier from .classifier import load_classifier
@@ -176,10 +175,12 @@ class DocumentViewSet(RetrieveModelMixin,
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
response = super(DocumentViewSet, self).update( response = super(DocumentViewSet, self).update(
request, *args, **kwargs) request, *args, **kwargs)
from documents import index
index.add_or_update_document(self.get_object()) index.add_or_update_document(self.get_object())
return response return response
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
from documents import index
index.remove_document_from_index(self.get_object()) index.remove_document_from_index(self.get_object())
return super(DocumentViewSet, self).destroy(request, *args, **kwargs) return super(DocumentViewSet, self).destroy(request, *args, **kwargs)
@@ -501,10 +502,6 @@ class SearchView(APIView):
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
def __init__(self, *args, **kwargs):
super(SearchView, self).__init__(*args, **kwargs)
self.ix = index.open_index()
def add_infos_to_hit(self, r): def add_infos_to_hit(self, r):
try: try:
doc = Document.objects.get(id=r['id']) doc = Document.objects.get(id=r['id'])
@@ -525,6 +522,7 @@ class SearchView(APIView):
} }
def get(self, request, format=None): def get(self, request, format=None):
from documents import index
if 'query' in request.query_params: if 'query' in request.query_params:
query = request.query_params['query'] query = request.query_params['query']
@@ -554,8 +552,10 @@ class SearchView(APIView):
if page < 1: if page < 1:
page = 1 page = 1
ix = index.open_index()
try: try:
with index.query_page(self.ix, page, query, more_like_id, more_like_content) as (result_page, corrected_query): # NOQA: E501 with index.query_page(ix, page, query, more_like_id, more_like_content) as (result_page, corrected_query): # NOQA: E501
return Response( return Response(
{'count': len(result_page), {'count': len(result_page),
'page': result_page.pagenum, 'page': result_page.pagenum,
@@ -570,10 +570,6 @@ class SearchAutoCompleteView(APIView):
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
def __init__(self, *args, **kwargs):
super(SearchAutoCompleteView, self).__init__(*args, **kwargs)
self.ix = index.open_index()
def get(self, request, format=None): def get(self, request, format=None):
if 'term' in request.query_params: if 'term' in request.query_params:
term = request.query_params['term'] term = request.query_params['term']
@@ -587,7 +583,11 @@ class SearchAutoCompleteView(APIView):
else: else:
limit = 10 limit = 10
return Response(index.autocomplete(self.ix, term, limit)) from documents import index
ix = index.open_index()
return Response(index.autocomplete(ix, term, limit))
class StatisticsView(APIView): class StatisticsView(APIView):
@@ -595,8 +595,14 @@ class StatisticsView(APIView):
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
def get(self, request, format=None): def get(self, request, format=None):
return Response({ documents_total = Document.objects.all().count()
'documents_total': Document.objects.all().count(), if Tag.objects.filter(is_inbox_tag=True).exists():
'documents_inbox': Document.objects.filter( documents_inbox = Document.objects.filter(
tags__is_inbox_tag=True).distinct().count() tags__is_inbox_tag=True).distinct().count()
else:
documents_inbox = None
return Response({
'documents_total': documents_total,
'documents_inbox': documents_inbox,
}) })

View File

@@ -11,8 +11,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-28 22:02+0100\n" "POT-Creation-Date: 2021-02-16 14:52+0100\n"
"PO-Revision-Date: 2020-12-30 19:27+0000\n" "PO-Revision-Date: 2021-02-16 18:37+0000\n"
"Last-Translator: Jonas Winkler, 2021\n" "Last-Translator: Jonas Winkler, 2021\n"
"Language-Team: German (https://www.transifex.com/paperless/teams/115905/de/)\n" "Language-Team: German (https://www.transifex.com/paperless/teams/115905/de/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -25,64 +25,64 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumente" msgstr "Dokumente"
#: documents/models.py:33 #: documents/models.py:32
msgid "Any word" msgid "Any word"
msgstr "Irgendein Wort" msgstr "Irgendein Wort"
#: documents/models.py:34 #: documents/models.py:33
msgid "All words" msgid "All words"
msgstr "Alle Wörter" msgstr "Alle Wörter"
#: documents/models.py:35 #: documents/models.py:34
msgid "Exact match" msgid "Exact match"
msgstr "Exakte Übereinstimmung" msgstr "Exakte Übereinstimmung"
#: documents/models.py:36 #: documents/models.py:35
msgid "Regular expression" msgid "Regular expression"
msgstr "Regulärer Ausdruck" msgstr "Regulärer Ausdruck"
#: documents/models.py:37 #: documents/models.py:36
msgid "Fuzzy word" msgid "Fuzzy word"
msgstr "Ungenaues Wort" msgstr "Ungenaues Wort"
#: documents/models.py:38 #: documents/models.py:37
msgid "Automatic" msgid "Automatic"
msgstr "Automatisch" msgstr "Automatisch"
#: documents/models.py:42 documents/models.py:352 paperless_mail/models.py:25 #: documents/models.py:41 documents/models.py:364 paperless_mail/models.py:25
#: paperless_mail/models.py:109 #: paperless_mail/models.py:109
msgid "name" msgid "name"
msgstr "Name" msgstr "Name"
#: documents/models.py:46 #: documents/models.py:45
msgid "match" msgid "match"
msgstr "Zuweisungsmuster" msgstr "Zuweisungsmuster"
#: documents/models.py:50 #: documents/models.py:49
msgid "matching algorithm" msgid "matching algorithm"
msgstr "Zuweisungsalgorithmus" msgstr "Zuweisungsalgorithmus"
#: documents/models.py:56 #: documents/models.py:55
msgid "is insensitive" msgid "is insensitive"
msgstr "Groß-/Kleinschreibung irrelevant" msgstr "Groß-/Kleinschreibung irrelevant"
#: documents/models.py:75 documents/models.py:135 #: documents/models.py:74 documents/models.py:134
msgid "correspondent" msgid "correspondent"
msgstr "Korrespondent" msgstr "Korrespondent"
#: documents/models.py:76 #: documents/models.py:75
msgid "correspondents" msgid "correspondents"
msgstr "Korrespondenten" msgstr "Korrespondenten"
#: documents/models.py:98 #: documents/models.py:97
msgid "color" msgid "color"
msgstr "Farbe" msgstr "Farbe"
#: documents/models.py:102 #: documents/models.py:101
msgid "is inbox tag" msgid "is inbox tag"
msgstr "Posteingangs-Tag" msgstr "Posteingangs-Tag"
#: documents/models.py:104 #: documents/models.py:103
msgid "" msgid ""
"Marks this tag as an inbox tag: All newly consumed documents will be tagged " "Marks this tag as an inbox tag: All newly consumed documents will be tagged "
"with inbox tags." "with inbox tags."
@@ -90,39 +90,39 @@ msgstr ""
"Markiert das Tag als Posteingangs-Tag. Neue Dokumente werden immer mit " "Markiert das Tag als Posteingangs-Tag. Neue Dokumente werden immer mit "
"diesem Tag versehen." "diesem Tag versehen."
#: documents/models.py:109 #: documents/models.py:108
msgid "tag" msgid "tag"
msgstr "Tag" msgstr "Tag"
#: documents/models.py:110 documents/models.py:166 #: documents/models.py:109 documents/models.py:165
msgid "tags" msgid "tags"
msgstr "Tags" msgstr "Tags"
#: documents/models.py:116 documents/models.py:148 #: documents/models.py:115 documents/models.py:147
msgid "document type" msgid "document type"
msgstr "Dokumenttyp" msgstr "Dokumenttyp"
#: documents/models.py:117 #: documents/models.py:116
msgid "document types" msgid "document types"
msgstr "Dokumenttypen" msgstr "Dokumenttypen"
#: documents/models.py:125 #: documents/models.py:124
msgid "Unencrypted" msgid "Unencrypted"
msgstr "Nicht verschlüsselt" msgstr "Nicht verschlüsselt"
#: documents/models.py:126 #: documents/models.py:125
msgid "Encrypted with GNU Privacy Guard" msgid "Encrypted with GNU Privacy Guard"
msgstr "Verschlüsselt mit GNU Privacy Guard" msgstr "Verschlüsselt mit GNU Privacy Guard"
#: documents/models.py:139 #: documents/models.py:138
msgid "title" msgid "title"
msgstr "Titel" msgstr "Titel"
#: documents/models.py:152 #: documents/models.py:151
msgid "content" msgid "content"
msgstr "Inhalt" msgstr "Inhalt"
#: documents/models.py:154 #: documents/models.py:153
msgid "" msgid ""
"The raw, text-only data of the document. This field is primarily used for " "The raw, text-only data of the document. This field is primarily used for "
"searching." "searching."
@@ -130,43 +130,43 @@ msgstr ""
"Der Inhalt des Dokuments in Textform. Dieses Feld wird primär für die Suche " "Der Inhalt des Dokuments in Textform. Dieses Feld wird primär für die Suche "
"verwendet." "verwendet."
#: documents/models.py:159 #: documents/models.py:158
msgid "mime type" msgid "mime type"
msgstr "MIME-Typ" msgstr "MIME-Typ"
#: documents/models.py:170 #: documents/models.py:169
msgid "checksum" msgid "checksum"
msgstr "Prüfsumme" msgstr "Prüfsumme"
#: documents/models.py:174 #: documents/models.py:173
msgid "The checksum of the original document." msgid "The checksum of the original document."
msgstr "Die Prüfsumme des originalen Dokuments." msgstr "Die Prüfsumme des originalen Dokuments."
#: documents/models.py:178 #: documents/models.py:177
msgid "archive checksum" msgid "archive checksum"
msgstr "Archiv-Prüfsumme" msgstr "Archiv-Prüfsumme"
#: documents/models.py:183 #: documents/models.py:182
msgid "The checksum of the archived document." msgid "The checksum of the archived document."
msgstr "Die Prüfsumme des archivierten Dokuments." msgstr "Die Prüfsumme des archivierten Dokuments."
#: documents/models.py:187 documents/models.py:330 #: documents/models.py:186 documents/models.py:342
msgid "created" msgid "created"
msgstr "Ausgestellt" msgstr "Ausgestellt"
#: documents/models.py:191 #: documents/models.py:190
msgid "modified" msgid "modified"
msgstr "Geändert" msgstr "Geändert"
#: documents/models.py:195 #: documents/models.py:194
msgid "storage type" msgid "storage type"
msgstr "Speichertyp" msgstr "Speichertyp"
#: documents/models.py:203 #: documents/models.py:202
msgid "added" msgid "added"
msgstr "Hinzugefügt" msgstr "Hinzugefügt"
#: documents/models.py:207 #: documents/models.py:206
msgid "filename" msgid "filename"
msgstr "Dateiname" msgstr "Dateiname"
@@ -175,178 +175,186 @@ msgid "Current filename in storage"
msgstr "Aktueller Dateiname im Datenspeicher" msgstr "Aktueller Dateiname im Datenspeicher"
#: documents/models.py:216 #: documents/models.py:216
msgid "archive filename"
msgstr "Archiv-Dateiname"
#: documents/models.py:222
msgid "Current archive filename in storage"
msgstr "Aktueller Dateiname im Archiv"
#: documents/models.py:226
msgid "archive serial number" msgid "archive serial number"
msgstr "Archiv-Seriennummer" msgstr "Archiv-Seriennummer"
#: documents/models.py:221 #: documents/models.py:231
msgid "The position of this document in your physical document archive." msgid "The position of this document in your physical document archive."
msgstr "Die Position dieses Dokuments in Ihrem physischen Dokumentenarchiv." msgstr "Die Position dieses Dokuments in Ihrem physischen Dokumentenarchiv."
#: documents/models.py:227 #: documents/models.py:237
msgid "document" msgid "document"
msgstr "Dokument" msgstr "Dokument"
#: documents/models.py:228 #: documents/models.py:238
msgid "documents" msgid "documents"
msgstr "Dokumente" msgstr "Dokumente"
#: documents/models.py:313 #: documents/models.py:325
msgid "debug" msgid "debug"
msgstr "Debug" msgstr "Debug"
#: documents/models.py:314 #: documents/models.py:326
msgid "information" msgid "information"
msgstr "Information" msgstr "Information"
#: documents/models.py:315 #: documents/models.py:327
msgid "warning" msgid "warning"
msgstr "Warnung" msgstr "Warnung"
#: documents/models.py:316 #: documents/models.py:328
msgid "error" msgid "error"
msgstr "Fehler" msgstr "Fehler"
#: documents/models.py:317 #: documents/models.py:329
msgid "critical" msgid "critical"
msgstr "Kritisch" msgstr "Kritisch"
#: documents/models.py:321 #: documents/models.py:333
msgid "group" msgid "group"
msgstr "Gruppe" msgstr "Gruppe"
#: documents/models.py:324 #: documents/models.py:336
msgid "message" msgid "message"
msgstr "Nachricht" msgstr "Nachricht"
#: documents/models.py:327 #: documents/models.py:339
msgid "level" msgid "level"
msgstr "Level" msgstr "Level"
#: documents/models.py:334 #: documents/models.py:346
msgid "log" msgid "log"
msgstr "Protokoll" msgstr "Protokoll"
#: documents/models.py:335 #: documents/models.py:347
msgid "logs" msgid "logs"
msgstr "Protokoll" msgstr "Protokoll"
#: documents/models.py:346 documents/models.py:396 #: documents/models.py:358 documents/models.py:408
msgid "saved view" msgid "saved view"
msgstr "Gespeicherte Ansicht" msgstr "Gespeicherte Ansicht"
#: documents/models.py:347 #: documents/models.py:359
msgid "saved views" msgid "saved views"
msgstr "Gespeicherte Ansichten" msgstr "Gespeicherte Ansichten"
#: documents/models.py:350 #: documents/models.py:362
msgid "user" msgid "user"
msgstr "Benutzer" msgstr "Benutzer"
#: documents/models.py:356 #: documents/models.py:368
msgid "show on dashboard" msgid "show on dashboard"
msgstr "Auf Startseite zeigen" msgstr "Auf Startseite zeigen"
#: documents/models.py:359 #: documents/models.py:371
msgid "show in sidebar" msgid "show in sidebar"
msgstr "In Seitenleiste zeigen" msgstr "In Seitenleiste zeigen"
#: documents/models.py:363 #: documents/models.py:375
msgid "sort field" msgid "sort field"
msgstr "Sortierfeld" msgstr "Sortierfeld"
#: documents/models.py:366 #: documents/models.py:378
msgid "sort reverse" msgid "sort reverse"
msgstr "Umgekehrte Sortierung" msgstr "Umgekehrte Sortierung"
#: documents/models.py:372 #: documents/models.py:384
msgid "title contains" msgid "title contains"
msgstr "Titel enthält" msgstr "Titel enthält"
#: documents/models.py:373 #: documents/models.py:385
msgid "content contains" msgid "content contains"
msgstr "Inhalt enthält" msgstr "Inhalt enthält"
#: documents/models.py:374 #: documents/models.py:386
msgid "ASN is" msgid "ASN is"
msgstr "ASN ist" msgstr "ASN ist"
#: documents/models.py:375 #: documents/models.py:387
msgid "correspondent is" msgid "correspondent is"
msgstr "Korrespondent ist" msgstr "Korrespondent ist"
#: documents/models.py:376 #: documents/models.py:388
msgid "document type is" msgid "document type is"
msgstr "Dokumenttyp ist" msgstr "Dokumenttyp ist"
#: documents/models.py:377 #: documents/models.py:389
msgid "is in inbox" msgid "is in inbox"
msgstr "Ist im Posteingang" msgstr "Ist im Posteingang"
#: documents/models.py:378 #: documents/models.py:390
msgid "has tag" msgid "has tag"
msgstr "Hat Tag" msgstr "Hat Tag"
#: documents/models.py:379 #: documents/models.py:391
msgid "has any tag" msgid "has any tag"
msgstr "Hat irgendein Tag" msgstr "Hat irgendein Tag"
#: documents/models.py:380 #: documents/models.py:392
msgid "created before" msgid "created before"
msgstr "Ausgestellt vor" msgstr "Ausgestellt vor"
#: documents/models.py:381 #: documents/models.py:393
msgid "created after" msgid "created after"
msgstr "Ausgestellt nach" msgstr "Ausgestellt nach"
#: documents/models.py:382 #: documents/models.py:394
msgid "created year is" msgid "created year is"
msgstr "Ausgestellt im Jahr" msgstr "Ausgestellt im Jahr"
#: documents/models.py:383 #: documents/models.py:395
msgid "created month is" msgid "created month is"
msgstr "Ausgestellt im Monat" msgstr "Ausgestellt im Monat"
#: documents/models.py:384 #: documents/models.py:396
msgid "created day is" msgid "created day is"
msgstr "Ausgestellt am Tag" msgstr "Ausgestellt am Tag"
#: documents/models.py:385 #: documents/models.py:397
msgid "added before" msgid "added before"
msgstr "Hinzugefügt vor" msgstr "Hinzugefügt vor"
#: documents/models.py:386 #: documents/models.py:398
msgid "added after" msgid "added after"
msgstr "Hinzugefügt nach" msgstr "Hinzugefügt nach"
#: documents/models.py:387 #: documents/models.py:399
msgid "modified before" msgid "modified before"
msgstr "Geändert vor" msgstr "Geändert vor"
#: documents/models.py:388 #: documents/models.py:400
msgid "modified after" msgid "modified after"
msgstr "Geändert nach" msgstr "Geändert nach"
#: documents/models.py:389 #: documents/models.py:401
msgid "does not have tag" msgid "does not have tag"
msgstr "Hat nicht folgendes Tag" msgstr "Hat nicht folgendes Tag"
#: documents/models.py:400 #: documents/models.py:412
msgid "rule type" msgid "rule type"
msgstr "Regeltyp" msgstr "Regeltyp"
#: documents/models.py:404 #: documents/models.py:416
msgid "value" msgid "value"
msgstr "Wert" msgstr "Wert"
#: documents/models.py:410 #: documents/models.py:422
msgid "filter rule" msgid "filter rule"
msgstr "Filterregel" msgstr "Filterregel"
#: documents/models.py:411 #: documents/models.py:423
msgid "filter rules" msgid "filter rules"
msgstr "Filterregeln" msgstr "Filterregeln"
#: documents/serialisers.py:383 #: documents/serialisers.py:370
#, python-format #, python-format
msgid "File type %(type)s not supported" msgid "File type %(type)s not supported"
msgstr "Dateityp %(type)s nicht unterstützt" msgstr "Dateityp %(type)s nicht unterstützt"
@@ -393,19 +401,23 @@ msgstr "Passwort"
msgid "Sign in" msgid "Sign in"
msgstr "Anmelden" msgstr "Anmelden"
#: paperless/settings.py:286 #: paperless/settings.py:291
msgid "English" msgid "English (US)"
msgstr "Englisch" msgstr "Englisch (US)"
#: paperless/settings.py:287 #: paperless/settings.py:292
msgid "English (GB)"
msgstr "Englisch (UK)"
#: paperless/settings.py:293
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: paperless/settings.py:288 #: paperless/settings.py:294
msgid "Dutch" msgid "Dutch"
msgstr "Niederländisch" msgstr "Niederländisch"
#: paperless/settings.py:289 #: paperless/settings.py:295
msgid "French" msgid "French"
msgstr "Französisch" msgstr "Französisch"

View File

@@ -4,16 +4,17 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# Ali Bates <xadium@gmail.com>, 2021 # Ali Bates, 2021
# Jonas Winkler, 2021
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-28 22:02+0100\n" "POT-Creation-Date: 2021-02-16 14:52+0100\n"
"PO-Revision-Date: 2020-12-30 19:27+0000\n" "PO-Revision-Date: 2021-02-16 18:37+0000\n"
"Last-Translator: Ali Bates <xadium@gmail.com>, 2021\n" "Last-Translator: Jonas Winkler, 2021\n"
"Language-Team: English (United Kingdom) (https://www.transifex.com/paperless/teams/115905/en_GB/)\n" "Language-Team: English (United Kingdom) (https://www.transifex.com/paperless/teams/115905/en_GB/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@@ -25,64 +26,64 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Documents" msgstr "Documents"
#: documents/models.py:33 #: documents/models.py:32
msgid "Any word" msgid "Any word"
msgstr "Any word" msgstr "Any word"
#: documents/models.py:34 #: documents/models.py:33
msgid "All words" msgid "All words"
msgstr "All words" msgstr "All words"
#: documents/models.py:35 #: documents/models.py:34
msgid "Exact match" msgid "Exact match"
msgstr "Exact match" msgstr "Exact match"
#: documents/models.py:36 #: documents/models.py:35
msgid "Regular expression" msgid "Regular expression"
msgstr "Regular expression" msgstr "Regular expression"
#: documents/models.py:37 #: documents/models.py:36
msgid "Fuzzy word" msgid "Fuzzy word"
msgstr "Fuzzy word" msgstr "Fuzzy word"
#: documents/models.py:38 #: documents/models.py:37
msgid "Automatic" msgid "Automatic"
msgstr "Automatic" msgstr "Automatic"
#: documents/models.py:42 documents/models.py:352 paperless_mail/models.py:25 #: documents/models.py:41 documents/models.py:364 paperless_mail/models.py:25
#: paperless_mail/models.py:109 #: paperless_mail/models.py:109
msgid "name" msgid "name"
msgstr "name" msgstr "name"
#: documents/models.py:46 #: documents/models.py:45
msgid "match" msgid "match"
msgstr "match" msgstr "match"
#: documents/models.py:50 #: documents/models.py:49
msgid "matching algorithm" msgid "matching algorithm"
msgstr "matching algorithm" msgstr "matching algorithm"
#: documents/models.py:56 #: documents/models.py:55
msgid "is insensitive" msgid "is insensitive"
msgstr "is insensitive" msgstr "is insensitive"
#: documents/models.py:75 documents/models.py:135 #: documents/models.py:74 documents/models.py:134
msgid "correspondent" msgid "correspondent"
msgstr "correspondent" msgstr "correspondent"
#: documents/models.py:76 #: documents/models.py:75
msgid "correspondents" msgid "correspondents"
msgstr "correspondents" msgstr "correspondents"
#: documents/models.py:98 #: documents/models.py:97
msgid "color" msgid "color"
msgstr "color" msgstr "colour"
#: documents/models.py:102 #: documents/models.py:101
msgid "is inbox tag" msgid "is inbox tag"
msgstr "is inbox tag" msgstr "is inbox tag"
#: documents/models.py:104 #: documents/models.py:103
msgid "" msgid ""
"Marks this tag as an inbox tag: All newly consumed documents will be tagged " "Marks this tag as an inbox tag: All newly consumed documents will be tagged "
"with inbox tags." "with inbox tags."
@@ -90,39 +91,39 @@ msgstr ""
"Marks this tag as an inbox tag: All newly consumed documents will be tagged " "Marks this tag as an inbox tag: All newly consumed documents will be tagged "
"with inbox tags." "with inbox tags."
#: documents/models.py:109 #: documents/models.py:108
msgid "tag" msgid "tag"
msgstr "tag" msgstr "tag"
#: documents/models.py:110 documents/models.py:166 #: documents/models.py:109 documents/models.py:165
msgid "tags" msgid "tags"
msgstr "tags" msgstr "tags"
#: documents/models.py:116 documents/models.py:148 #: documents/models.py:115 documents/models.py:147
msgid "document type" msgid "document type"
msgstr "document type" msgstr "document type"
#: documents/models.py:117 #: documents/models.py:116
msgid "document types" msgid "document types"
msgstr "document types" msgstr "document types"
#: documents/models.py:125 #: documents/models.py:124
msgid "Unencrypted" msgid "Unencrypted"
msgstr "Unencrypted" msgstr "Unencrypted"
#: documents/models.py:126 #: documents/models.py:125
msgid "Encrypted with GNU Privacy Guard" msgid "Encrypted with GNU Privacy Guard"
msgstr "Encrypted with GNU Privacy Guard" msgstr "Encrypted with GNU Privacy Guard"
#: documents/models.py:139 #: documents/models.py:138
msgid "title" msgid "title"
msgstr "title" msgstr "title"
#: documents/models.py:152 #: documents/models.py:151
msgid "content" msgid "content"
msgstr "content" msgstr "content"
#: documents/models.py:154 #: documents/models.py:153
msgid "" msgid ""
"The raw, text-only data of the document. This field is primarily used for " "The raw, text-only data of the document. This field is primarily used for "
"searching." "searching."
@@ -130,43 +131,43 @@ msgstr ""
"The raw, text-only data of the document. This field is primarily used for " "The raw, text-only data of the document. This field is primarily used for "
"searching." "searching."
#: documents/models.py:159 #: documents/models.py:158
msgid "mime type" msgid "mime type"
msgstr "mime type" msgstr "mime type"
#: documents/models.py:170 #: documents/models.py:169
msgid "checksum" msgid "checksum"
msgstr "checksum" msgstr "checksum"
#: documents/models.py:174 #: documents/models.py:173
msgid "The checksum of the original document." msgid "The checksum of the original document."
msgstr "The checksum of the original document." msgstr "The checksum of the original document."
#: documents/models.py:178 #: documents/models.py:177
msgid "archive checksum" msgid "archive checksum"
msgstr "archive checksum" msgstr "archive checksum"
#: documents/models.py:183 #: documents/models.py:182
msgid "The checksum of the archived document." msgid "The checksum of the archived document."
msgstr "The checksum of the archived document." msgstr "The checksum of the archived document."
#: documents/models.py:187 documents/models.py:330 #: documents/models.py:186 documents/models.py:342
msgid "created" msgid "created"
msgstr "created" msgstr "created"
#: documents/models.py:191 #: documents/models.py:190
msgid "modified" msgid "modified"
msgstr "modified" msgstr "modified"
#: documents/models.py:195 #: documents/models.py:194
msgid "storage type" msgid "storage type"
msgstr "storage type" msgstr "storage type"
#: documents/models.py:203 #: documents/models.py:202
msgid "added" msgid "added"
msgstr "added" msgstr "added"
#: documents/models.py:207 #: documents/models.py:206
msgid "filename" msgid "filename"
msgstr "filename" msgstr "filename"
@@ -175,178 +176,186 @@ msgid "Current filename in storage"
msgstr "Current filename in storage" msgstr "Current filename in storage"
#: documents/models.py:216 #: documents/models.py:216
msgid "archive filename"
msgstr "archive filename"
#: documents/models.py:222
msgid "Current archive filename in storage"
msgstr "Current archive filename in storage"
#: documents/models.py:226
msgid "archive serial number" msgid "archive serial number"
msgstr "archive serial number" msgstr "archive serial number"
#: documents/models.py:221 #: documents/models.py:231
msgid "The position of this document in your physical document archive." msgid "The position of this document in your physical document archive."
msgstr "The position of this document in your physical document archive." msgstr "The position of this document in your physical document archive."
#: documents/models.py:227 #: documents/models.py:237
msgid "document" msgid "document"
msgstr "document" msgstr "document"
#: documents/models.py:228 #: documents/models.py:238
msgid "documents" msgid "documents"
msgstr "documents" msgstr "documents"
#: documents/models.py:313 #: documents/models.py:325
msgid "debug" msgid "debug"
msgstr "debug" msgstr "debug"
#: documents/models.py:314 #: documents/models.py:326
msgid "information" msgid "information"
msgstr "information" msgstr "information"
#: documents/models.py:315 #: documents/models.py:327
msgid "warning" msgid "warning"
msgstr "warning" msgstr "warning"
#: documents/models.py:316 #: documents/models.py:328
msgid "error" msgid "error"
msgstr "error" msgstr "error"
#: documents/models.py:317 #: documents/models.py:329
msgid "critical" msgid "critical"
msgstr "critical" msgstr "critical"
#: documents/models.py:321 #: documents/models.py:333
msgid "group" msgid "group"
msgstr "group" msgstr "group"
#: documents/models.py:324 #: documents/models.py:336
msgid "message" msgid "message"
msgstr "message" msgstr "message"
#: documents/models.py:327 #: documents/models.py:339
msgid "level" msgid "level"
msgstr "level" msgstr "level"
#: documents/models.py:334 #: documents/models.py:346
msgid "log" msgid "log"
msgstr "log" msgstr "log"
#: documents/models.py:335 #: documents/models.py:347
msgid "logs" msgid "logs"
msgstr "logs" msgstr "logs"
#: documents/models.py:346 documents/models.py:396 #: documents/models.py:358 documents/models.py:408
msgid "saved view" msgid "saved view"
msgstr "saved view" msgstr "saved view"
#: documents/models.py:347 #: documents/models.py:359
msgid "saved views" msgid "saved views"
msgstr "saved views" msgstr "saved views"
#: documents/models.py:350 #: documents/models.py:362
msgid "user" msgid "user"
msgstr "user" msgstr "user"
#: documents/models.py:356 #: documents/models.py:368
msgid "show on dashboard" msgid "show on dashboard"
msgstr "show on dashboard" msgstr "show on dashboard"
#: documents/models.py:359 #: documents/models.py:371
msgid "show in sidebar" msgid "show in sidebar"
msgstr "show in sidebar" msgstr "show in sidebar"
#: documents/models.py:363 #: documents/models.py:375
msgid "sort field" msgid "sort field"
msgstr "sort field" msgstr "sort field"
#: documents/models.py:366 #: documents/models.py:378
msgid "sort reverse" msgid "sort reverse"
msgstr "sort reverse" msgstr "sort reverse"
#: documents/models.py:372 #: documents/models.py:384
msgid "title contains" msgid "title contains"
msgstr "title contains" msgstr "title contains"
#: documents/models.py:373 #: documents/models.py:385
msgid "content contains" msgid "content contains"
msgstr "content contains" msgstr "content contains"
#: documents/models.py:374 #: documents/models.py:386
msgid "ASN is" msgid "ASN is"
msgstr "ASN is" msgstr "ASN is"
#: documents/models.py:375 #: documents/models.py:387
msgid "correspondent is" msgid "correspondent is"
msgstr "correspondent is" msgstr "correspondent is"
#: documents/models.py:376 #: documents/models.py:388
msgid "document type is" msgid "document type is"
msgstr "document type is" msgstr "document type is"
#: documents/models.py:377 #: documents/models.py:389
msgid "is in inbox" msgid "is in inbox"
msgstr "is in inbox" msgstr "is in inbox"
#: documents/models.py:378 #: documents/models.py:390
msgid "has tag" msgid "has tag"
msgstr "has tag" msgstr "has tag"
#: documents/models.py:379 #: documents/models.py:391
msgid "has any tag" msgid "has any tag"
msgstr "has any tag" msgstr "has any tag"
#: documents/models.py:380 #: documents/models.py:392
msgid "created before" msgid "created before"
msgstr "created before" msgstr "created before"
#: documents/models.py:381 #: documents/models.py:393
msgid "created after" msgid "created after"
msgstr "created after" msgstr "created after"
#: documents/models.py:382 #: documents/models.py:394
msgid "created year is" msgid "created year is"
msgstr "created year is" msgstr "created year is"
#: documents/models.py:383 #: documents/models.py:395
msgid "created month is" msgid "created month is"
msgstr "created month is" msgstr "created month is"
#: documents/models.py:384 #: documents/models.py:396
msgid "created day is" msgid "created day is"
msgstr "created day is" msgstr "created day is"
#: documents/models.py:385 #: documents/models.py:397
msgid "added before" msgid "added before"
msgstr "added before" msgstr "added before"
#: documents/models.py:386 #: documents/models.py:398
msgid "added after" msgid "added after"
msgstr "added after" msgstr "added after"
#: documents/models.py:387 #: documents/models.py:399
msgid "modified before" msgid "modified before"
msgstr "modified before" msgstr "modified before"
#: documents/models.py:388 #: documents/models.py:400
msgid "modified after" msgid "modified after"
msgstr "modified after" msgstr "modified after"
#: documents/models.py:389 #: documents/models.py:401
msgid "does not have tag" msgid "does not have tag"
msgstr "does not have tag" msgstr "does not have tag"
#: documents/models.py:400 #: documents/models.py:412
msgid "rule type" msgid "rule type"
msgstr "rule type" msgstr "rule type"
#: documents/models.py:404 #: documents/models.py:416
msgid "value" msgid "value"
msgstr "value" msgstr "value"
#: documents/models.py:410 #: documents/models.py:422
msgid "filter rule" msgid "filter rule"
msgstr "filter rule" msgstr "filter rule"
#: documents/models.py:411 #: documents/models.py:423
msgid "filter rules" msgid "filter rules"
msgstr "filter rules" msgstr "filter rules"
#: documents/serialisers.py:383 #: documents/serialisers.py:370
#, python-format #, python-format
msgid "File type %(type)s not supported" msgid "File type %(type)s not supported"
msgstr "File type %(type)s not supported" msgstr "File type %(type)s not supported"
@@ -391,19 +400,23 @@ msgstr "Password"
msgid "Sign in" msgid "Sign in"
msgstr "Sign in" msgstr "Sign in"
#: paperless/settings.py:286 #: paperless/settings.py:291
msgid "English" msgid "English (US)"
msgstr "English" msgstr "English (US)"
#: paperless/settings.py:287 #: paperless/settings.py:292
msgid "English (GB)"
msgstr "English (GB)"
#: paperless/settings.py:293
msgid "German" msgid "German"
msgstr "German" msgstr "German"
#: paperless/settings.py:288 #: paperless/settings.py:294
msgid "Dutch" msgid "Dutch"
msgstr "Dutch" msgstr "Dutch"
#: paperless/settings.py:289 #: paperless/settings.py:295
msgid "French" msgid "French"
msgstr "French" msgstr "French"

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-28 22:02+0100\n" "POT-Creation-Date: 2021-02-16 14:52+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -21,144 +21,144 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "" msgstr ""
#: documents/models.py:33 #: documents/models.py:32
msgid "Any word" msgid "Any word"
msgstr "" msgstr ""
#: documents/models.py:34 #: documents/models.py:33
msgid "All words" msgid "All words"
msgstr "" msgstr ""
#: documents/models.py:35 #: documents/models.py:34
msgid "Exact match" msgid "Exact match"
msgstr "" msgstr ""
#: documents/models.py:36 #: documents/models.py:35
msgid "Regular expression" msgid "Regular expression"
msgstr "" msgstr ""
#: documents/models.py:37 #: documents/models.py:36
msgid "Fuzzy word" msgid "Fuzzy word"
msgstr "" msgstr ""
#: documents/models.py:38 #: documents/models.py:37
msgid "Automatic" msgid "Automatic"
msgstr "" msgstr ""
#: documents/models.py:42 documents/models.py:352 paperless_mail/models.py:25 #: documents/models.py:41 documents/models.py:364 paperless_mail/models.py:25
#: paperless_mail/models.py:109 #: paperless_mail/models.py:109
msgid "name" msgid "name"
msgstr "" msgstr ""
#: documents/models.py:46 #: documents/models.py:45
msgid "match" msgid "match"
msgstr "" msgstr ""
#: documents/models.py:50 #: documents/models.py:49
msgid "matching algorithm" msgid "matching algorithm"
msgstr "" msgstr ""
#: documents/models.py:56 #: documents/models.py:55
msgid "is insensitive" msgid "is insensitive"
msgstr "" msgstr ""
#: documents/models.py:75 documents/models.py:135 #: documents/models.py:74 documents/models.py:134
msgid "correspondent" msgid "correspondent"
msgstr "" msgstr ""
#: documents/models.py:76 #: documents/models.py:75
msgid "correspondents" msgid "correspondents"
msgstr "" msgstr ""
#: documents/models.py:98 #: documents/models.py:97
msgid "color" msgid "color"
msgstr "" msgstr ""
#: documents/models.py:102 #: documents/models.py:101
msgid "is inbox tag" msgid "is inbox tag"
msgstr "" msgstr ""
#: documents/models.py:104 #: documents/models.py:103
msgid "" msgid ""
"Marks this tag as an inbox tag: All newly consumed documents will be tagged " "Marks this tag as an inbox tag: All newly consumed documents will be tagged "
"with inbox tags." "with inbox tags."
msgstr "" msgstr ""
#: documents/models.py:109 #: documents/models.py:108
msgid "tag" msgid "tag"
msgstr "" msgstr ""
#: documents/models.py:110 documents/models.py:166 #: documents/models.py:109 documents/models.py:165
msgid "tags" msgid "tags"
msgstr "" msgstr ""
#: documents/models.py:116 documents/models.py:148 #: documents/models.py:115 documents/models.py:147
msgid "document type" msgid "document type"
msgstr "" msgstr ""
#: documents/models.py:117 #: documents/models.py:116
msgid "document types" msgid "document types"
msgstr "" msgstr ""
#: documents/models.py:125 #: documents/models.py:124
msgid "Unencrypted" msgid "Unencrypted"
msgstr "" msgstr ""
#: documents/models.py:126 #: documents/models.py:125
msgid "Encrypted with GNU Privacy Guard" msgid "Encrypted with GNU Privacy Guard"
msgstr "" msgstr ""
#: documents/models.py:139 #: documents/models.py:138
msgid "title" msgid "title"
msgstr "" msgstr ""
#: documents/models.py:152 #: documents/models.py:151
msgid "content" msgid "content"
msgstr "" msgstr ""
#: documents/models.py:154 #: documents/models.py:153
msgid "" msgid ""
"The raw, text-only data of the document. This field is primarily used for " "The raw, text-only data of the document. This field is primarily used for "
"searching." "searching."
msgstr "" msgstr ""
#: documents/models.py:159 #: documents/models.py:158
msgid "mime type" msgid "mime type"
msgstr "" msgstr ""
#: documents/models.py:170 #: documents/models.py:169
msgid "checksum" msgid "checksum"
msgstr "" msgstr ""
#: documents/models.py:174 #: documents/models.py:173
msgid "The checksum of the original document." msgid "The checksum of the original document."
msgstr "" msgstr ""
#: documents/models.py:178 #: documents/models.py:177
msgid "archive checksum" msgid "archive checksum"
msgstr "" msgstr ""
#: documents/models.py:183 #: documents/models.py:182
msgid "The checksum of the archived document." msgid "The checksum of the archived document."
msgstr "" msgstr ""
#: documents/models.py:187 documents/models.py:330 #: documents/models.py:186 documents/models.py:342
msgid "created" msgid "created"
msgstr "" msgstr ""
#: documents/models.py:191 #: documents/models.py:190
msgid "modified" msgid "modified"
msgstr "" msgstr ""
#: documents/models.py:195 #: documents/models.py:194
msgid "storage type" msgid "storage type"
msgstr "" msgstr ""
#: documents/models.py:203 #: documents/models.py:202
msgid "added" msgid "added"
msgstr "" msgstr ""
#: documents/models.py:207 #: documents/models.py:206
msgid "filename" msgid "filename"
msgstr "" msgstr ""
@@ -167,178 +167,186 @@ msgid "Current filename in storage"
msgstr "" msgstr ""
#: documents/models.py:216 #: documents/models.py:216
msgid "archive filename"
msgstr ""
#: documents/models.py:222
msgid "Current archive filename in storage"
msgstr ""
#: documents/models.py:226
msgid "archive serial number" msgid "archive serial number"
msgstr "" msgstr ""
#: documents/models.py:221 #: documents/models.py:231
msgid "The position of this document in your physical document archive." msgid "The position of this document in your physical document archive."
msgstr "" msgstr ""
#: documents/models.py:227 #: documents/models.py:237
msgid "document" msgid "document"
msgstr "" msgstr ""
#: documents/models.py:228 #: documents/models.py:238
msgid "documents" msgid "documents"
msgstr "" msgstr ""
#: documents/models.py:313 #: documents/models.py:325
msgid "debug" msgid "debug"
msgstr "" msgstr ""
#: documents/models.py:314 #: documents/models.py:326
msgid "information" msgid "information"
msgstr "" msgstr ""
#: documents/models.py:315 #: documents/models.py:327
msgid "warning" msgid "warning"
msgstr "" msgstr ""
#: documents/models.py:316 #: documents/models.py:328
msgid "error" msgid "error"
msgstr "" msgstr ""
#: documents/models.py:317 #: documents/models.py:329
msgid "critical" msgid "critical"
msgstr "" msgstr ""
#: documents/models.py:321 #: documents/models.py:333
msgid "group" msgid "group"
msgstr "" msgstr ""
#: documents/models.py:324 #: documents/models.py:336
msgid "message" msgid "message"
msgstr "" msgstr ""
#: documents/models.py:327 #: documents/models.py:339
msgid "level" msgid "level"
msgstr "" msgstr ""
#: documents/models.py:334 #: documents/models.py:346
msgid "log" msgid "log"
msgstr "" msgstr ""
#: documents/models.py:335 #: documents/models.py:347
msgid "logs" msgid "logs"
msgstr "" msgstr ""
#: documents/models.py:346 documents/models.py:396 #: documents/models.py:358 documents/models.py:408
msgid "saved view" msgid "saved view"
msgstr "" msgstr ""
#: documents/models.py:347 #: documents/models.py:359
msgid "saved views" msgid "saved views"
msgstr "" msgstr ""
#: documents/models.py:350 #: documents/models.py:362
msgid "user" msgid "user"
msgstr "" msgstr ""
#: documents/models.py:356 #: documents/models.py:368
msgid "show on dashboard" msgid "show on dashboard"
msgstr "" msgstr ""
#: documents/models.py:359 #: documents/models.py:371
msgid "show in sidebar" msgid "show in sidebar"
msgstr "" msgstr ""
#: documents/models.py:363 #: documents/models.py:375
msgid "sort field" msgid "sort field"
msgstr "" msgstr ""
#: documents/models.py:366 #: documents/models.py:378
msgid "sort reverse" msgid "sort reverse"
msgstr "" msgstr ""
#: documents/models.py:372 #: documents/models.py:384
msgid "title contains" msgid "title contains"
msgstr "" msgstr ""
#: documents/models.py:373 #: documents/models.py:385
msgid "content contains" msgid "content contains"
msgstr "" msgstr ""
#: documents/models.py:374 #: documents/models.py:386
msgid "ASN is" msgid "ASN is"
msgstr "" msgstr ""
#: documents/models.py:375 #: documents/models.py:387
msgid "correspondent is" msgid "correspondent is"
msgstr "" msgstr ""
#: documents/models.py:376 #: documents/models.py:388
msgid "document type is" msgid "document type is"
msgstr "" msgstr ""
#: documents/models.py:377 #: documents/models.py:389
msgid "is in inbox" msgid "is in inbox"
msgstr "" msgstr ""
#: documents/models.py:378 #: documents/models.py:390
msgid "has tag" msgid "has tag"
msgstr "" msgstr ""
#: documents/models.py:379 #: documents/models.py:391
msgid "has any tag" msgid "has any tag"
msgstr "" msgstr ""
#: documents/models.py:380 #: documents/models.py:392
msgid "created before" msgid "created before"
msgstr "" msgstr ""
#: documents/models.py:381 #: documents/models.py:393
msgid "created after" msgid "created after"
msgstr "" msgstr ""
#: documents/models.py:382 #: documents/models.py:394
msgid "created year is" msgid "created year is"
msgstr "" msgstr ""
#: documents/models.py:383 #: documents/models.py:395
msgid "created month is" msgid "created month is"
msgstr "" msgstr ""
#: documents/models.py:384 #: documents/models.py:396
msgid "created day is" msgid "created day is"
msgstr "" msgstr ""
#: documents/models.py:385 #: documents/models.py:397
msgid "added before" msgid "added before"
msgstr "" msgstr ""
#: documents/models.py:386 #: documents/models.py:398
msgid "added after" msgid "added after"
msgstr "" msgstr ""
#: documents/models.py:387 #: documents/models.py:399
msgid "modified before" msgid "modified before"
msgstr "" msgstr ""
#: documents/models.py:388 #: documents/models.py:400
msgid "modified after" msgid "modified after"
msgstr "" msgstr ""
#: documents/models.py:389 #: documents/models.py:401
msgid "does not have tag" msgid "does not have tag"
msgstr "" msgstr ""
#: documents/models.py:400 #: documents/models.py:412
msgid "rule type" msgid "rule type"
msgstr "" msgstr ""
#: documents/models.py:404 #: documents/models.py:416
msgid "value" msgid "value"
msgstr "" msgstr ""
#: documents/models.py:410 #: documents/models.py:422
msgid "filter rule" msgid "filter rule"
msgstr "" msgstr ""
#: documents/models.py:411 #: documents/models.py:423
msgid "filter rules" msgid "filter rules"
msgstr "" msgstr ""
#: documents/serialisers.py:383 #: documents/serialisers.py:370
#, python-format #, python-format
msgid "File type %(type)s not supported" msgid "File type %(type)s not supported"
msgstr "" msgstr ""
@@ -383,19 +391,23 @@ msgstr ""
msgid "Sign in" msgid "Sign in"
msgstr "" msgstr ""
#: paperless/settings.py:286 #: paperless/settings.py:291
msgid "English" msgid "English (US)"
msgstr "" msgstr ""
#: paperless/settings.py:287 #: paperless/settings.py:292
msgid "English (GB)"
msgstr ""
#: paperless/settings.py:293
msgid "German" msgid "German"
msgstr "" msgstr ""
#: paperless/settings.py:288 #: paperless/settings.py:294
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings.py:289 #: paperless/settings.py:295
msgid "French" msgid "French"
msgstr "" msgstr ""

View File

@@ -4,17 +4,17 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
# Translators: # Translators:
# Jonas Winkler, 2021
# Jo Vandeginste <jo.vandeginste@gmail.com>, 2021 # Jo Vandeginste <jo.vandeginste@gmail.com>, 2021
# Ben <bzweekhorst@gmail.com>, 2021
# #
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-28 22:02+0100\n" "POT-Creation-Date: 2021-02-16 14:52+0100\n"
"PO-Revision-Date: 2020-12-30 19:27+0000\n" "PO-Revision-Date: 2021-02-16 18:37+0000\n"
"Last-Translator: Ben <bzweekhorst@gmail.com>, 2021\n" "Last-Translator: Jo Vandeginste <jo.vandeginste@gmail.com>, 2021\n"
"Language-Team: Dutch (Netherlands) (https://www.transifex.com/paperless/teams/115905/nl_NL/)\n" "Language-Team: Dutch (Netherlands) (https://www.transifex.com/paperless/teams/115905/nl_NL/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@@ -26,64 +26,64 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Documenten" msgstr "Documenten"
#: documents/models.py:33 #: documents/models.py:32
msgid "Any word" msgid "Any word"
msgstr "Eender welk woord" msgstr "Eender welk woord"
#: documents/models.py:34 #: documents/models.py:33
msgid "All words" msgid "All words"
msgstr "Alle woorden" msgstr "Alle woorden"
#: documents/models.py:35 #: documents/models.py:34
msgid "Exact match" msgid "Exact match"
msgstr "Exacte overeenkomst" msgstr "Exacte overeenkomst"
#: documents/models.py:36 #: documents/models.py:35
msgid "Regular expression" msgid "Regular expression"
msgstr "Reguliere expressie" msgstr "Reguliere expressie"
#: documents/models.py:37 #: documents/models.py:36
msgid "Fuzzy word" msgid "Fuzzy word"
msgstr "Gelijkaardig woord" msgstr "Gelijkaardig woord"
#: documents/models.py:38 #: documents/models.py:37
msgid "Automatic" msgid "Automatic"
msgstr "Automatisch" msgstr "Automatisch"
#: documents/models.py:42 documents/models.py:352 paperless_mail/models.py:25 #: documents/models.py:41 documents/models.py:364 paperless_mail/models.py:25
#: paperless_mail/models.py:109 #: paperless_mail/models.py:109
msgid "name" msgid "name"
msgstr "naam" msgstr "naam"
#: documents/models.py:46 #: documents/models.py:45
msgid "match" msgid "match"
msgstr "Overeenkomst" msgstr "Overeenkomst"
#: documents/models.py:50 #: documents/models.py:49
msgid "matching algorithm" msgid "matching algorithm"
msgstr "Algoritme voor het bepalen van de overeenkomst" msgstr "Algoritme voor het bepalen van de overeenkomst"
#: documents/models.py:56 #: documents/models.py:55
msgid "is insensitive" msgid "is insensitive"
msgstr "is niet hoofdlettergevoelig" msgstr "is niet hoofdlettergevoelig"
#: documents/models.py:75 documents/models.py:135 #: documents/models.py:74 documents/models.py:134
msgid "correspondent" msgid "correspondent"
msgstr "correspondent" msgstr "correspondent"
#: documents/models.py:76 #: documents/models.py:75
msgid "correspondents" msgid "correspondents"
msgstr "correspondenten" msgstr "correspondenten"
#: documents/models.py:98 #: documents/models.py:97
msgid "color" msgid "color"
msgstr "Kleur" msgstr "Kleur"
#: documents/models.py:102 #: documents/models.py:101
msgid "is inbox tag" msgid "is inbox tag"
msgstr "is \"Postvak in\"-etiket" msgstr "is \"Postvak in\"-etiket"
#: documents/models.py:104 #: documents/models.py:103
msgid "" msgid ""
"Marks this tag as an inbox tag: All newly consumed documents will be tagged " "Marks this tag as an inbox tag: All newly consumed documents will be tagged "
"with inbox tags." "with inbox tags."
@@ -91,39 +91,39 @@ msgstr ""
"Markeer dit etiket als een \"Postvak in\"-etiket: alle nieuw verwerkte " "Markeer dit etiket als een \"Postvak in\"-etiket: alle nieuw verwerkte "
"documenten krijgen de \"Postvak in\"-etiketten." "documenten krijgen de \"Postvak in\"-etiketten."
#: documents/models.py:109 #: documents/models.py:108
msgid "tag" msgid "tag"
msgstr "etiket" msgstr "etiket"
#: documents/models.py:110 documents/models.py:166 #: documents/models.py:109 documents/models.py:165
msgid "tags" msgid "tags"
msgstr "etiketten" msgstr "etiketten"
#: documents/models.py:116 documents/models.py:148 #: documents/models.py:115 documents/models.py:147
msgid "document type" msgid "document type"
msgstr "documenttype" msgstr "documenttype"
#: documents/models.py:117 #: documents/models.py:116
msgid "document types" msgid "document types"
msgstr "documenttypen" msgstr "documenttypen"
#: documents/models.py:125 #: documents/models.py:124
msgid "Unencrypted" msgid "Unencrypted"
msgstr "Niet versleuteld" msgstr "Niet versleuteld"
#: documents/models.py:126 #: documents/models.py:125
msgid "Encrypted with GNU Privacy Guard" msgid "Encrypted with GNU Privacy Guard"
msgstr "Versleuteld met GNU Privacy Guard" msgstr "Versleuteld met GNU Privacy Guard"
#: documents/models.py:139 #: documents/models.py:138
msgid "title" msgid "title"
msgstr "titel" msgstr "titel"
#: documents/models.py:152 #: documents/models.py:151
msgid "content" msgid "content"
msgstr "inhoud" msgstr "inhoud"
#: documents/models.py:154 #: documents/models.py:153
msgid "" msgid ""
"The raw, text-only data of the document. This field is primarily used for " "The raw, text-only data of the document. This field is primarily used for "
"searching." "searching."
@@ -131,43 +131,43 @@ msgstr ""
"De onbewerkte gegevens van het document. Dit veld wordt voornamelijk " "De onbewerkte gegevens van het document. Dit veld wordt voornamelijk "
"gebruikt om te zoeken." "gebruikt om te zoeken."
#: documents/models.py:159 #: documents/models.py:158
msgid "mime type" msgid "mime type"
msgstr "mimetype" msgstr "mimetype"
#: documents/models.py:170 #: documents/models.py:169
msgid "checksum" msgid "checksum"
msgstr "checksum" msgstr "checksum"
#: documents/models.py:174 #: documents/models.py:173
msgid "The checksum of the original document." msgid "The checksum of the original document."
msgstr "Het controlecijfer van het originele document." msgstr "Het controlecijfer van het originele document."
#: documents/models.py:178 #: documents/models.py:177
msgid "archive checksum" msgid "archive checksum"
msgstr "archief checksum" msgstr "archief checksum"
#: documents/models.py:183 #: documents/models.py:182
msgid "The checksum of the archived document." msgid "The checksum of the archived document."
msgstr "De checksum van het gearchiveerde document." msgstr "De checksum van het gearchiveerde document."
#: documents/models.py:187 documents/models.py:330 #: documents/models.py:186 documents/models.py:342
msgid "created" msgid "created"
msgstr "aangemaakt" msgstr "aangemaakt"
#: documents/models.py:191 #: documents/models.py:190
msgid "modified" msgid "modified"
msgstr "gewijzigd" msgstr "gewijzigd"
#: documents/models.py:195 #: documents/models.py:194
msgid "storage type" msgid "storage type"
msgstr "type opslag" msgstr "type opslag"
#: documents/models.py:203 #: documents/models.py:202
msgid "added" msgid "added"
msgstr "toegevoegd" msgstr "toegevoegd"
#: documents/models.py:207 #: documents/models.py:206
msgid "filename" msgid "filename"
msgstr "bestandsnaam" msgstr "bestandsnaam"
@@ -176,178 +176,186 @@ msgid "Current filename in storage"
msgstr "Huidige bestandsnaam in opslag" msgstr "Huidige bestandsnaam in opslag"
#: documents/models.py:216 #: documents/models.py:216
msgid "archive filename"
msgstr "Bestandsnaam in archief"
#: documents/models.py:222
msgid "Current archive filename in storage"
msgstr "Huidige bestandsnaam in archief"
#: documents/models.py:226
msgid "archive serial number" msgid "archive serial number"
msgstr "serienummer in archief" msgstr "serienummer in archief"
#: documents/models.py:221 #: documents/models.py:231
msgid "The position of this document in your physical document archive." msgid "The position of this document in your physical document archive."
msgstr "De positie van dit document in je fysieke documentenarchief." msgstr "De positie van dit document in je fysieke documentenarchief."
#: documents/models.py:227 #: documents/models.py:237
msgid "document" msgid "document"
msgstr "document" msgstr "document"
#: documents/models.py:228 #: documents/models.py:238
msgid "documents" msgid "documents"
msgstr "documenten" msgstr "documenten"
#: documents/models.py:313 #: documents/models.py:325
msgid "debug" msgid "debug"
msgstr "debug" msgstr "debug"
#: documents/models.py:314 #: documents/models.py:326
msgid "information" msgid "information"
msgstr "informatie" msgstr "informatie"
#: documents/models.py:315 #: documents/models.py:327
msgid "warning" msgid "warning"
msgstr "waarschuwing" msgstr "waarschuwing"
#: documents/models.py:316 #: documents/models.py:328
msgid "error" msgid "error"
msgstr "fout" msgstr "fout"
#: documents/models.py:317 #: documents/models.py:329
msgid "critical" msgid "critical"
msgstr "kritisch" msgstr "kritisch"
#: documents/models.py:321 #: documents/models.py:333
msgid "group" msgid "group"
msgstr "groep" msgstr "groep"
#: documents/models.py:324 #: documents/models.py:336
msgid "message" msgid "message"
msgstr "bericht" msgstr "bericht"
#: documents/models.py:327 #: documents/models.py:339
msgid "level" msgid "level"
msgstr "niveau" msgstr "niveau"
#: documents/models.py:334 #: documents/models.py:346
msgid "log" msgid "log"
msgstr "bericht" msgstr "bericht"
#: documents/models.py:335 #: documents/models.py:347
msgid "logs" msgid "logs"
msgstr "berichten" msgstr "berichten"
#: documents/models.py:346 documents/models.py:396 #: documents/models.py:358 documents/models.py:408
msgid "saved view" msgid "saved view"
msgstr "opgeslagen view" msgstr "opgeslagen view"
#: documents/models.py:347 #: documents/models.py:359
msgid "saved views" msgid "saved views"
msgstr "opgeslagen views" msgstr "opgeslagen views"
#: documents/models.py:350 #: documents/models.py:362
msgid "user" msgid "user"
msgstr "gebruiker" msgstr "gebruiker"
#: documents/models.py:356 #: documents/models.py:368
msgid "show on dashboard" msgid "show on dashboard"
msgstr "weergeven op dashboard" msgstr "weergeven op dashboard"
#: documents/models.py:359 #: documents/models.py:371
msgid "show in sidebar" msgid "show in sidebar"
msgstr "weergeven in zijbalk" msgstr "weergeven in zijbalk"
#: documents/models.py:363 #: documents/models.py:375
msgid "sort field" msgid "sort field"
msgstr "sorteerveld" msgstr "sorteerveld"
#: documents/models.py:366 #: documents/models.py:378
msgid "sort reverse" msgid "sort reverse"
msgstr "omgekeerd sorteren" msgstr "omgekeerd sorteren"
#: documents/models.py:372 #: documents/models.py:384
msgid "title contains" msgid "title contains"
msgstr "titel bevat" msgstr "titel bevat"
#: documents/models.py:373 #: documents/models.py:385
msgid "content contains" msgid "content contains"
msgstr "inhoud bevat" msgstr "inhoud bevat"
#: documents/models.py:374 #: documents/models.py:386
msgid "ASN is" msgid "ASN is"
msgstr "ASN is" msgstr "ASN is"
#: documents/models.py:375 #: documents/models.py:387
msgid "correspondent is" msgid "correspondent is"
msgstr "correspondent is" msgstr "correspondent is"
#: documents/models.py:376 #: documents/models.py:388
msgid "document type is" msgid "document type is"
msgstr "documenttype is" msgstr "documenttype is"
#: documents/models.py:377 #: documents/models.py:389
msgid "is in inbox" msgid "is in inbox"
msgstr "zit in \"Postvak in\"" msgstr "zit in \"Postvak in\""
#: documents/models.py:378 #: documents/models.py:390
msgid "has tag" msgid "has tag"
msgstr "heeft etiket" msgstr "heeft etiket"
#: documents/models.py:379 #: documents/models.py:391
msgid "has any tag" msgid "has any tag"
msgstr "heeft één van de etiketten" msgstr "heeft één van de etiketten"
#: documents/models.py:380 #: documents/models.py:392
msgid "created before" msgid "created before"
msgstr "aangemaakt voor" msgstr "aangemaakt voor"
#: documents/models.py:381 #: documents/models.py:393
msgid "created after" msgid "created after"
msgstr "aangemaakt na" msgstr "aangemaakt na"
#: documents/models.py:382 #: documents/models.py:394
msgid "created year is" msgid "created year is"
msgstr "aangemaakt jaar is" msgstr "aangemaakt jaar is"
#: documents/models.py:383 #: documents/models.py:395
msgid "created month is" msgid "created month is"
msgstr "aangemaakte maand is" msgstr "aangemaakte maand is"
#: documents/models.py:384 #: documents/models.py:396
msgid "created day is" msgid "created day is"
msgstr "aangemaakte dag is" msgstr "aangemaakte dag is"
#: documents/models.py:385 #: documents/models.py:397
msgid "added before" msgid "added before"
msgstr "toegevoegd voor" msgstr "toegevoegd voor"
#: documents/models.py:386 #: documents/models.py:398
msgid "added after" msgid "added after"
msgstr "toegevoegd na" msgstr "toegevoegd na"
#: documents/models.py:387 #: documents/models.py:399
msgid "modified before" msgid "modified before"
msgstr "gewijzigd voor" msgstr "gewijzigd voor"
#: documents/models.py:388 #: documents/models.py:400
msgid "modified after" msgid "modified after"
msgstr "gewijzigd na" msgstr "gewijzigd na"
#: documents/models.py:389 #: documents/models.py:401
msgid "does not have tag" msgid "does not have tag"
msgstr "heeft geen etiket" msgstr "heeft geen etiket"
#: documents/models.py:400 #: documents/models.py:412
msgid "rule type" msgid "rule type"
msgstr "type regel" msgstr "type regel"
#: documents/models.py:404 #: documents/models.py:416
msgid "value" msgid "value"
msgstr "waarde" msgstr "waarde"
#: documents/models.py:410 #: documents/models.py:422
msgid "filter rule" msgid "filter rule"
msgstr "filterregel" msgstr "filterregel"
#: documents/models.py:411 #: documents/models.py:423
msgid "filter rules" msgid "filter rules"
msgstr "filterregels" msgstr "filterregels"
#: documents/serialisers.py:383 #: documents/serialisers.py:370
#, python-format #, python-format
msgid "File type %(type)s not supported" msgid "File type %(type)s not supported"
msgstr "Bestandstype %(type)s niet ondersteund" msgstr "Bestandstype %(type)s niet ondersteund"
@@ -392,19 +400,23 @@ msgstr "Wachtwoord"
msgid "Sign in" msgid "Sign in"
msgstr "Aanmelden" msgstr "Aanmelden"
#: paperless/settings.py:286 #: paperless/settings.py:291
msgid "English" msgid "English (US)"
msgstr "Engels" msgstr "Engels (US)"
#: paperless/settings.py:287 #: paperless/settings.py:292
msgid "English (GB)"
msgstr "Engels (Brits)"
#: paperless/settings.py:293
msgid "German" msgid "German"
msgstr "Duits" msgstr "Duits"
#: paperless/settings.py:288 #: paperless/settings.py:294
msgid "Dutch" msgid "Dutch"
msgstr "Nederlands" msgstr "Nederlands"
#: paperless/settings.py:289 #: paperless/settings.py:295
msgid "French" msgid "French"
msgstr "Frans" msgstr "Frans"

View File

@@ -288,7 +288,8 @@ if os.getenv("PAPERLESS_DBHOST"):
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'en-us'
LANGUAGES = [ LANGUAGES = [
("en-us", _("English")), ("en-us", _("English (US)")),
("en-gb", _("English (GB)")),
("de", _("German")), ("de", _("German")),
("nl-nl", _("Dutch")), ("nl-nl", _("Dutch")),
("fr", _("French")) ("fr", _("French"))

View File

@@ -1 +1 @@
__version__ = (1, 1, 2) __version__ = (1, 1, 4)

View File

@@ -2,12 +2,8 @@ import json
import os import os
import re import re
import ocrmypdf
import pdftotext
import pikepdf
from PIL import Image from PIL import Image
from django.conf import settings from django.conf import settings
from ocrmypdf import InputFileError, EncryptedPdfError
from documents.parsers import DocumentParser, ParseError, \ from documents.parsers import DocumentParser, ParseError, \
make_thumbnail_from_pdf make_thumbnail_from_pdf
@@ -22,6 +18,8 @@ class RasterisedDocumentParser(DocumentParser):
logging_name = "paperless.parsing.tesseract" logging_name = "paperless.parsing.tesseract"
def extract_metadata(self, document_path, mime_type): def extract_metadata(self, document_path, mime_type):
import pikepdf
namespace_pattern = re.compile(r"\{(.*)\}(.*)") namespace_pattern = re.compile(r"\{(.*)\}(.*)")
result = [] result = []
@@ -91,6 +89,9 @@ class RasterisedDocumentParser(DocumentParser):
return None return None
def parse(self, document_path, mime_type, file_name=None): def parse(self, document_path, mime_type, file_name=None):
import ocrmypdf
from ocrmypdf import InputFileError, EncryptedPdfError
mode = settings.OCR_MODE mode = settings.OCR_MODE
text_original = get_text_from_pdf(document_path) text_original = get_text_from_pdf(document_path)
@@ -223,6 +224,7 @@ def strip_excess_whitespace(text):
def get_text_from_pdf(pdf_file): def get_text_from_pdf(pdf_file):
import pdftotext
if not os.path.isfile(pdf_file): if not os.path.isfile(pdf_file):
return None return None

View File

@@ -164,17 +164,12 @@ class TestParser(DirectoriesMixin, TestCase):
self.assertRaises(ParseError, f) self.assertRaises(ParseError, f)
@mock.patch("paperless_tesseract.parsers.ocrmypdf.ocr") def test_image_calc_a4_dpi(self):
def test_image_calc_a4_dpi(self, m):
parser = RasterisedDocumentParser(None) parser = RasterisedDocumentParser(None)
parser.parse(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"), "image/png") dpi = parser.calculate_a4_dpi(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"))
m.assert_called_once() self.assertEqual(dpi, 62)
args, kwargs = m.call_args
self.assertEqual(kwargs['image_dpi'], 62)
@mock.patch("paperless_tesseract.parsers.RasterisedDocumentParser.calculate_a4_dpi") @mock.patch("paperless_tesseract.parsers.RasterisedDocumentParser.calculate_a4_dpi")
def test_image_dpi_fail(self, m): def test_image_dpi_fail(self, m):