mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-17 10:13:56 -05:00
Merge branch 'dev' into travis-multiarch-builds
This commit is contained in:
commit
b3a13cec17
@ -5,85 +5,6 @@ Advanced topics
|
|||||||
Paperless offers a couple features that automate certain tasks and make your life
|
Paperless offers a couple features that automate certain tasks and make your life
|
||||||
easier.
|
easier.
|
||||||
|
|
||||||
Guesswork
|
|
||||||
#########
|
|
||||||
|
|
||||||
|
|
||||||
Any document you put into the consumption directory will be consumed, but if
|
|
||||||
you name the file right, it'll automatically set some values in the database
|
|
||||||
for you. This is is the logic the consumer follows:
|
|
||||||
|
|
||||||
1. Try to find the correspondent, title, and tags in the file name following
|
|
||||||
the pattern: ``Date - Correspondent - Title - tag,tag,tag.pdf``. Note that
|
|
||||||
the format of the date is **rigidly defined** as ``YYYYMMDDHHMMSSZ`` or
|
|
||||||
``YYYYMMDDZ``. The ``Z`` refers "Zulu time" AKA "UTC".
|
|
||||||
The tags are optional, so the format ``Date - Correspondent - Title.pdf``
|
|
||||||
works as well.
|
|
||||||
2. If that doesn't work, we skip the date and try this pattern:
|
|
||||||
``Correspondent - Title - tag,tag,tag.pdf``.
|
|
||||||
3. If that doesn't work, we try to find the correspondent and title in the file
|
|
||||||
name following the pattern: ``Correspondent - Title.pdf``.
|
|
||||||
4. If that doesn't work, just assume that the name of the file is the title.
|
|
||||||
|
|
||||||
So given the above, the following examples would work as you'd expect:
|
|
||||||
|
|
||||||
* ``20150314000700Z - Some Company Name - Invoice 2016-01-01 - money,invoices.pdf``
|
|
||||||
* ``20150314Z - Some Company Name - Invoice 2016-01-01 - money,invoices.pdf``
|
|
||||||
* ``Some Company Name - Invoice 2016-01-01 - money,invoices.pdf``
|
|
||||||
* ``Another Company - Letter of Reference.jpg``
|
|
||||||
* ``Dad's Recipe for Pancakes.png``
|
|
||||||
|
|
||||||
These however wouldn't work:
|
|
||||||
|
|
||||||
* ``2015-03-14 00:07:00 UTC - Some Company Name, Invoice 2016-01-01, money, invoices.pdf``
|
|
||||||
* ``2015-03-14 - Some Company Name, Invoice 2016-01-01, money, invoices.pdf``
|
|
||||||
* ``Some Company Name, Invoice 2016-01-01, money, invoices.pdf``
|
|
||||||
* ``Another Company- Letter of Reference.jpg``
|
|
||||||
|
|
||||||
Do I have to be so strict about naming?
|
|
||||||
=======================================
|
|
||||||
|
|
||||||
Rather than using the strict document naming rules, one can also set the option
|
|
||||||
``PAPERLESS_FILENAME_DATE_ORDER`` in ``paperless.conf`` to any date order
|
|
||||||
that is accepted by dateparser_. Doing so will cause ``paperless`` to default
|
|
||||||
to any date format that is found in the title, instead of a date pulled from
|
|
||||||
the document's text, without requiring the strict formatting of the document
|
|
||||||
filename as described above.
|
|
||||||
|
|
||||||
.. _dateparser: https://github.com/scrapinghub/dateparser/blob/v0.7.0/docs/usage.rst#settings
|
|
||||||
|
|
||||||
.. _advanced-transforming_filenames:
|
|
||||||
|
|
||||||
Transforming filenames for parsing
|
|
||||||
==================================
|
|
||||||
|
|
||||||
Some devices can't produce filenames that can be parsed by the default
|
|
||||||
parser. By configuring the option ``PAPERLESS_FILENAME_PARSE_TRANSFORMS`` in
|
|
||||||
``paperless.conf`` one can add transformations that are applied to the filename
|
|
||||||
before it's parsed.
|
|
||||||
|
|
||||||
The option contains a list of dictionaries of regular expressions (key:
|
|
||||||
``pattern``) and replacements (key: ``repl``) in JSON format, which are
|
|
||||||
applied in order by passing them to ``re.subn``. Transformation stops
|
|
||||||
after the first match, so at most one transformation is applied. The general
|
|
||||||
syntax is
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
[{"pattern":"pattern1", "repl":"repl1"}, {"pattern":"pattern2", "repl":"repl2"}, ..., {"pattern":"patternN", "repl":"replN"}]
|
|
||||||
|
|
||||||
The example below is for a Brother ADS-2400N, a scanner that allows
|
|
||||||
different names to different hardware buttons (useful for handling
|
|
||||||
multiple entities in one instance), but insists on adding ``_<count>``
|
|
||||||
to the filename.
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
# Brother profile configuration, support "Name_Date_Count" (the default
|
|
||||||
# setting) and "Name_Count" (use "Name" as tag and "Count" as title).
|
|
||||||
PAPERLESS_FILENAME_PARSE_TRANSFORMS=[{"pattern":"^([a-z]+)_(\\d{8})_(\\d{6})_([0-9]+)\\.", "repl":"\\2\\3Z - \\4 - \\1."}, {"pattern":"^([a-z]+)_([0-9]+)\\.", "repl":" - \\2 - \\1."}]
|
|
||||||
|
|
||||||
|
|
||||||
.. _advanced-matching:
|
.. _advanced-matching:
|
||||||
|
|
||||||
Matching tags, correspondents and document types
|
Matching tags, correspondents and document types
|
||||||
|
@ -400,11 +400,6 @@ PAPERLESS_FILENAME_DATE_ORDER=<format>
|
|||||||
|
|
||||||
Defaults to none, which disables this feature.
|
Defaults to none, which disables this feature.
|
||||||
|
|
||||||
PAPERLESS_FILENAME_PARSE_TRANSFORMS
|
|
||||||
Transforms filenames before they are processed by paperless. See
|
|
||||||
:ref:`advanced-transforming_filenames` for details.
|
|
||||||
|
|
||||||
Defaults to none, which disables this feature.
|
|
||||||
|
|
||||||
Binaries
|
Binaries
|
||||||
########
|
########
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div class="form-group paperless-input-select">
|
<div class="form-group paperless-input-select">
|
||||||
<label [for]="inputId">{{title}}</label>
|
<label [for]="inputId">{{title}}</label>
|
||||||
<div [class.input-group]="showPlusButton()">
|
<div [class.input-group]="showPlusButton()">
|
||||||
<ng-select name="correspondent" [(ngModel)]="value"
|
<ng-select name="inputId" [(ngModel)]="value"
|
||||||
[disabled]="disabled"
|
[disabled]="disabled"
|
||||||
[style.color]="textColor"
|
[style.color]="textColor"
|
||||||
[style.background]="backgroundColor"
|
[style.background]="backgroundColor"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<app-page-header title="Dashboard" subTitle="Welcome to paperless-ng!">
|
<app-page-header title="Dashboard" [subTitle]="subtitle">
|
||||||
<img src="assets/logo.svg" height="80" class="m-2 d-none d-md-block">
|
<img src="assets/logo.svg" height="80" class="m-2 d-none d-md-block">
|
||||||
</app-page-header>
|
</app-page-header>
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Meta } from '@angular/platform-browser';
|
||||||
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
|
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
|
||||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
|
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
|
||||||
|
|
||||||
@ -11,8 +12,29 @@ import { SavedViewService } from 'src/app/services/rest/saved-view.service';
|
|||||||
export class DashboardComponent implements OnInit {
|
export class DashboardComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private savedViewService: SavedViewService) { }
|
private savedViewService: SavedViewService,
|
||||||
|
private meta: Meta
|
||||||
|
) { }
|
||||||
|
|
||||||
|
get displayName() {
|
||||||
|
let tagFullName = this.meta.getTag('name=full_name')
|
||||||
|
let tagUsername = this.meta.getTag('name=username')
|
||||||
|
if (tagFullName && tagFullName.content) {
|
||||||
|
return tagFullName.content
|
||||||
|
} else if (tagUsername && tagUsername.content) {
|
||||||
|
return tagUsername.content
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get subtitle() {
|
||||||
|
if (this.displayName) {
|
||||||
|
return `Hello ${this.displayName}, welcome to Paperless-ng!`
|
||||||
|
} else {
|
||||||
|
return `Welcome to Paperless-ng!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
savedViews: PaperlessSavedView[] = []
|
savedViews: PaperlessSavedView[] = []
|
||||||
|
|
||||||
|
@ -8,10 +8,9 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
<app-input-text title="Name" formControlName="name"></app-input-text>
|
<app-input-text title="Name" formControlName="name"></app-input-text>
|
||||||
<app-input-text title="Match" formControlName="match"></app-input-text>
|
|
||||||
<app-input-select title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
|
<app-input-select title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
|
||||||
<app-input-check title="Case insensitive" formControlName="is_insensitive"></app-input-check>
|
<app-input-text title="Match" formControlName="match" hint="Auto matching does not require you to fill in this field."></app-input-text>
|
||||||
|
<app-input-check title="Case insensitive" formControlName="is_insensitive" hint="Auto matching ignores this option."></app-input-check>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline-dark" (click)="cancel()">Cancel</button>
|
<button type="button" class="btn btn-outline-dark" (click)="cancel()">Cancel</button>
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
|
||||||
<app-input-text title="Name" formControlName="name"></app-input-text>
|
<app-input-text title="Name" formControlName="name"></app-input-text>
|
||||||
<app-input-text title="Match" formControlName="match"></app-input-text>
|
|
||||||
<app-input-select title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
|
<app-input-select title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
|
||||||
<app-input-check title="Case insensitive" formControlName="is_insensitive"></app-input-check>
|
<app-input-text title="Match" formControlName="match" hint="Auto matching does not require you to fill in this field."></app-input-text>
|
||||||
|
<app-input-check title="Case insensitive" formControlName="is_insensitive" hint="Auto matching ignores this option."></app-input-check>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
@ -7,11 +7,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<app-input-text title="Name" formControlName="name"></app-input-text>
|
<app-input-text title="Name" formControlName="name"></app-input-text>
|
||||||
<app-input-select title="Colour" [items]="getColours()" formControlName="colour" [textColor]="getColor(objectForm.value.colour).textColor" [backgroundColor]="getColor(objectForm.value.colour).value"></app-input-select>
|
|
||||||
|
|
||||||
|
<div class="form-group paperless-input-select">
|
||||||
|
<label for="colour">Colour</label>
|
||||||
|
<ng-select name="colour" formControlName="colour" [items]="getColours()" bindValue="id" bindLabel="name" [clearable]="false">
|
||||||
|
<ng-template ng-option-tmp ng-label-tmp let-item="item">
|
||||||
|
<span class="badge" [style.background]="item.value" [style.color]="item.textColor">{{item.name}}</span>
|
||||||
|
</ng-template>
|
||||||
|
</ng-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<app-input-check title="Inbox tag" formControlName="is_inbox_tag" hint="Inbox tags are automatically assigned to all consumed documents."></app-input-check>
|
<app-input-check title="Inbox tag" formControlName="is_inbox_tag" hint="Inbox tags are automatically assigned to all consumed documents."></app-input-check>
|
||||||
<app-input-text title="Match" formControlName="match"></app-input-text>
|
|
||||||
<app-input-select title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
|
<app-input-select title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
|
||||||
<app-input-check title="Case insensitive" formControlName="is_insensitive"></app-input-check>
|
<app-input-text title="Match" formControlName="match" hint="Auto matching does not require you to fill in this field."></app-input-text>
|
||||||
|
<app-input-check title="Case insensitive" formControlName="is_insensitive" hint="Auto matching ignores this option."></app-input-check>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-outline-dark" (click)="cancel()">Cancel</button>
|
<button type="button" class="btn btn-outline-dark" (click)="cancel()">Cancel</button>
|
||||||
|
@ -6,14 +6,14 @@ export const TAG_COLOURS = [
|
|||||||
{id: 1, value: "#a6cee3", name: "Light Blue", textColor: "#000000"},
|
{id: 1, value: "#a6cee3", name: "Light Blue", textColor: "#000000"},
|
||||||
{id: 2, value: "#1f78b4", name: "Blue", textColor: "#ffffff"},
|
{id: 2, value: "#1f78b4", name: "Blue", textColor: "#ffffff"},
|
||||||
{id: 3, value: "#b2df8a", name: "Light Green", textColor: "#000000"},
|
{id: 3, value: "#b2df8a", name: "Light Green", textColor: "#000000"},
|
||||||
{id: 4, value: "#33a02c", name: "Green", textColor: "#000000"},
|
{id: 4, value: "#33a02c", name: "Green", textColor: "#ffffff"},
|
||||||
{id: 5, value: "#fb9a99", name: "Light Red", textColor: "#000000"},
|
{id: 5, value: "#fb9a99", name: "Light Red", textColor: "#000000"},
|
||||||
{id: 6, value: "#e31a1c", name: "Red ", textColor: "#ffffff"},
|
{id: 6, value: "#e31a1c", name: "Red ", textColor: "#ffffff"},
|
||||||
{id: 7, value: "#fdbf6f", name: "Light Orange", textColor: "#000000"},
|
{id: 7, value: "#fdbf6f", name: "Light Orange", textColor: "#000000"},
|
||||||
{id: 8, value: "#ff7f00", name: "Orange", textColor: "#000000"},
|
{id: 8, value: "#ff7f00", name: "Orange", textColor: "#000000"},
|
||||||
{id: 9, value: "#cab2d6", name: "Light Violet", textColor: "#000000"},
|
{id: 9, value: "#cab2d6", name: "Light Violet", textColor: "#000000"},
|
||||||
{id: 10, value: "#6a3d9a", name: "Violet", textColor: "#ffffff"},
|
{id: 10, value: "#6a3d9a", name: "Violet", textColor: "#ffffff"},
|
||||||
{id: 11, value: "#b15928", name: "Brown", textColor: "#000000"},
|
{id: 11, value: "#b15928", name: "Brown", textColor: "#ffffff"},
|
||||||
{id: 12, value: "#000000", name: "Black", textColor: "#ffffff"},
|
{id: 12, value: "#000000", name: "Black", textColor: "#ffffff"},
|
||||||
{id: 13, value: "#cccccc", name: "Light Grey", textColor: "#000000"}
|
{id: 13, value: "#cccccc", name: "Light Grey", textColor: "#000000"}
|
||||||
]
|
]
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
from django.db.models.signals import post_save, m2m_changed
|
||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
|
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \
|
from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \
|
||||||
EXPORTER_ARCHIVE_NAME
|
EXPORTER_ARCHIVE_NAME
|
||||||
from ...file_handling import create_source_path_directory, \
|
from ...file_handling import create_source_path_directory
|
||||||
generate_unique_filename
|
|
||||||
from ...mixins import Renderable
|
from ...mixins import Renderable
|
||||||
|
from ...signals.handlers import update_filename_and_move_files
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def disable_signal(sig, receiver, sender):
|
||||||
|
try:
|
||||||
|
sig.disconnect(receiver=receiver, sender=sender)
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
sig.connect(receiver=receiver, sender=sender)
|
||||||
|
|
||||||
|
|
||||||
class Command(Renderable, BaseCommand):
|
class Command(Renderable, BaseCommand):
|
||||||
@ -47,11 +58,16 @@ class Command(Renderable, BaseCommand):
|
|||||||
self.manifest = json.load(f)
|
self.manifest = json.load(f)
|
||||||
|
|
||||||
self._check_manifest()
|
self._check_manifest()
|
||||||
|
with disable_signal(post_save,
|
||||||
|
receiver=update_filename_and_move_files,
|
||||||
|
sender=Document):
|
||||||
|
with disable_signal(m2m_changed,
|
||||||
|
receiver=update_filename_and_move_files,
|
||||||
|
sender=Document.tags.through):
|
||||||
|
# Fill up the database with whatever is in the manifest
|
||||||
|
call_command("loaddata", manifest_path)
|
||||||
|
|
||||||
# Fill up the database with whatever is in the manifest
|
self._import_files_from_manifest()
|
||||||
call_command("loaddata", manifest_path)
|
|
||||||
|
|
||||||
self._import_files_from_manifest()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check_manifest_exists(path):
|
def _check_manifest_exists(path):
|
||||||
@ -117,9 +133,6 @@ class Command(Renderable, BaseCommand):
|
|||||||
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
|
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
|
||||||
|
|
||||||
with FileLock(settings.MEDIA_LOCK):
|
with FileLock(settings.MEDIA_LOCK):
|
||||||
document.filename = generate_unique_filename(
|
|
||||||
document, settings.ORIGINALS_DIR)
|
|
||||||
|
|
||||||
if os.path.isfile(document.source_path):
|
if os.path.isfile(document.source_path):
|
||||||
raise FileExistsError(document.source_path)
|
raise FileExistsError(document.source_path)
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
<title>Paperless-ng</title>
|
<title>Paperless-ng</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="username" content="{{username}}">
|
||||||
|
<meta name="full_name" content="{{full_name}}">
|
||||||
<meta name="cookie_prefix" content="{{cookie_prefix}}">
|
<meta name="cookie_prefix" content="{{cookie_prefix}}">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
<link rel="stylesheet" href="{% static 'frontend/styles.css' %}"></head>
|
<link rel="stylesheet" href="{% static 'frontend/styles.css' %}"></head>
|
||||||
|
@ -24,11 +24,17 @@ class TestExportImport(DirectoriesMixin, TestCase):
|
|||||||
|
|
||||||
file = os.path.join(self.dirs.originals_dir, "0000001.pdf")
|
file = os.path.join(self.dirs.originals_dir, "0000001.pdf")
|
||||||
|
|
||||||
Document.objects.create(content="Content", checksum="42995833e01aea9b3edee44bbfdd7ce1", archive_checksum="62acb0bcbfbcaa62ca6ad3668e4e404b", title="wow", filename="0000001.pdf", mime_type="application/pdf")
|
d1 = Document.objects.create(content="Content", checksum="42995833e01aea9b3edee44bbfdd7ce1", archive_checksum="62acb0bcbfbcaa62ca6ad3668e4e404b", title="wow", filename="0000001.pdf", mime_type="application/pdf")
|
||||||
Document.objects.create(content="Content", checksum="9c9691e51741c1f4f41a20896af31770", title="wow", filename="0000002.pdf.gpg", mime_type="application/pdf", storage_type=Document.STORAGE_TYPE_GPG)
|
d2 = Document.objects.create(content="Content", checksum="9c9691e51741c1f4f41a20896af31770", title="wow", filename="0000002.pdf.gpg", mime_type="application/pdf", storage_type=Document.STORAGE_TYPE_GPG)
|
||||||
Tag.objects.create(name="t")
|
t1 = Tag.objects.create(name="t")
|
||||||
DocumentType.objects.create(name="dt")
|
dt1 = DocumentType.objects.create(name="dt")
|
||||||
Correspondent.objects.create(name="c")
|
c1 = Correspondent.objects.create(name="c")
|
||||||
|
|
||||||
|
d1.tags.add(t1)
|
||||||
|
d1.correspondents = c1
|
||||||
|
d1.document_type = dt1
|
||||||
|
d1.save()
|
||||||
|
d2.save()
|
||||||
|
|
||||||
target = tempfile.mkdtemp()
|
target = tempfile.mkdtemp()
|
||||||
self.addCleanup(shutil.rmtree, target)
|
self.addCleanup(shutil.rmtree, target)
|
||||||
@ -59,11 +65,25 @@ class TestExportImport(DirectoriesMixin, TestCase):
|
|||||||
self.assertEqual(checksum, element['fields']['archive_checksum'])
|
self.assertEqual(checksum, element['fields']['archive_checksum'])
|
||||||
|
|
||||||
with paperless_environment() as dirs:
|
with paperless_environment() as dirs:
|
||||||
|
self.assertEqual(Document.objects.count(), 2)
|
||||||
|
Document.objects.all().delete()
|
||||||
|
Correspondent.objects.all().delete()
|
||||||
|
DocumentType.objects.all().delete()
|
||||||
|
Tag.objects.all().delete()
|
||||||
|
self.assertEqual(Document.objects.count(), 0)
|
||||||
|
|
||||||
call_command('document_importer', target)
|
call_command('document_importer', target)
|
||||||
|
self.assertEqual(Document.objects.count(), 2)
|
||||||
messages = check_sanity()
|
messages = check_sanity()
|
||||||
# everything is alright after the test
|
# everything is alright after the test
|
||||||
self.assertEqual(len(messages), 0, str([str(m) for m in messages]))
|
self.assertEqual(len(messages), 0, str([str(m) for m in messages]))
|
||||||
|
|
||||||
|
@override_settings(
|
||||||
|
PAPERLESS_FILENAME_FORMAT="{title}"
|
||||||
|
)
|
||||||
|
def test_exporter_with_filename_format(self):
|
||||||
|
self.test_exporter()
|
||||||
|
|
||||||
def test_export_missing_files(self):
|
def test_export_missing_files(self):
|
||||||
|
|
||||||
target = tempfile.mkdtemp()
|
target = tempfile.mkdtemp()
|
||||||
|
@ -57,6 +57,8 @@ class IndexView(TemplateView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['cookie_prefix'] = settings.COOKIE_PREFIX
|
context['cookie_prefix'] = settings.COOKIE_PREFIX
|
||||||
|
context['username'] = self.request.user.username
|
||||||
|
context['full_name'] = self.request.user.get_full_name()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user