mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-28 18:24:38 -05:00
Format Python code with black
This commit is contained in:
@@ -19,7 +19,7 @@ class Command(BaseCommand):
|
||||
parser.add_argument(
|
||||
"--passphrase",
|
||||
help="If PAPERLESS_PASSPHRASE isn't set already, you need to "
|
||||
"specify it here"
|
||||
"specify it here",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
@@ -50,12 +50,12 @@ class Command(BaseCommand):
|
||||
def __gpg_to_unencrypted(passphrase):
|
||||
|
||||
encrypted_files = Document.objects.filter(
|
||||
storage_type=Document.STORAGE_TYPE_GPG)
|
||||
storage_type=Document.STORAGE_TYPE_GPG
|
||||
)
|
||||
|
||||
for document in encrypted_files:
|
||||
|
||||
print("Decrypting {}".format(
|
||||
document).encode('utf-8'))
|
||||
print("Decrypting {}".format(document).encode("utf-8"))
|
||||
|
||||
old_paths = [document.source_path, document.thumbnail_path]
|
||||
|
||||
@@ -66,10 +66,11 @@ class Command(BaseCommand):
|
||||
|
||||
ext = os.path.splitext(document.filename)[1]
|
||||
|
||||
if not ext == '.gpg':
|
||||
if not ext == ".gpg":
|
||||
raise CommandError(
|
||||
f"Abort: encrypted file {document.source_path} does not "
|
||||
f"end with .gpg")
|
||||
f"end with .gpg"
|
||||
)
|
||||
|
||||
document.filename = os.path.splitext(document.filename)[0]
|
||||
|
||||
@@ -80,7 +81,8 @@ class Command(BaseCommand):
|
||||
f.write(raw_thumb)
|
||||
|
||||
Document.objects.filter(id=document.id).update(
|
||||
storage_type=document.storage_type, filename=document.filename)
|
||||
storage_type=document.storage_type, filename=document.filename
|
||||
)
|
||||
|
||||
for path in old_paths:
|
||||
os.unlink(path)
|
||||
|
@@ -16,8 +16,7 @@ from whoosh.writing import AsyncWriter
|
||||
|
||||
from documents.models import Document
|
||||
from ... import index
|
||||
from ...file_handling import create_source_path_directory, \
|
||||
generate_unique_filename
|
||||
from ...file_handling import create_source_path_directory, generate_unique_filename
|
||||
from ...parsers import get_parser_class_for_mime_type
|
||||
|
||||
|
||||
@@ -32,51 +31,49 @@ def handle_document(document_id):
|
||||
parser_class = get_parser_class_for_mime_type(mime_type)
|
||||
|
||||
if not parser_class:
|
||||
logger.error(f"No parser found for mime type {mime_type}, cannot "
|
||||
f"archive document {document} (ID: {document_id})")
|
||||
logger.error(
|
||||
f"No parser found for mime type {mime_type}, cannot "
|
||||
f"archive document {document} (ID: {document_id})"
|
||||
)
|
||||
return
|
||||
|
||||
parser = parser_class(logging_group=uuid.uuid4())
|
||||
|
||||
try:
|
||||
parser.parse(
|
||||
document.source_path,
|
||||
mime_type,
|
||||
document.get_public_filename())
|
||||
parser.parse(document.source_path, mime_type, document.get_public_filename())
|
||||
|
||||
thumbnail = parser.get_optimised_thumbnail(
|
||||
document.source_path,
|
||||
mime_type,
|
||||
document.get_public_filename()
|
||||
document.source_path, mime_type, document.get_public_filename()
|
||||
)
|
||||
|
||||
if parser.get_archive_path():
|
||||
with transaction.atomic():
|
||||
with open(parser.get_archive_path(), 'rb') as f:
|
||||
with open(parser.get_archive_path(), "rb") as f:
|
||||
checksum = hashlib.md5(f.read()).hexdigest()
|
||||
# I'm going to save first so that in case the file move
|
||||
# fails, the database is rolled back.
|
||||
# We also don't use save() since that triggers the filehandling
|
||||
# logic, and we don't want that yet (file not yet in place)
|
||||
document.archive_filename = generate_unique_filename(
|
||||
document, archive_filename=True)
|
||||
document, archive_filename=True
|
||||
)
|
||||
Document.objects.filter(pk=document.pk).update(
|
||||
archive_checksum=checksum,
|
||||
content=parser.get_text(),
|
||||
archive_filename=document.archive_filename
|
||||
archive_filename=document.archive_filename,
|
||||
)
|
||||
with FileLock(settings.MEDIA_LOCK):
|
||||
create_source_path_directory(document.archive_path)
|
||||
shutil.move(parser.get_archive_path(),
|
||||
document.archive_path)
|
||||
shutil.move(parser.get_archive_path(), document.archive_path)
|
||||
shutil.move(thumbnail, document.thumbnail_path)
|
||||
|
||||
with index.open_index_writer() as writer:
|
||||
index.update_document(writer, document)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Error while parsing document {document} "
|
||||
f"(ID: {document_id})")
|
||||
logger.exception(
|
||||
f"Error while parsing document {document} " f"(ID: {document_id})"
|
||||
)
|
||||
finally:
|
||||
parser.cleanup()
|
||||
|
||||
@@ -88,29 +85,33 @@ class Command(BaseCommand):
|
||||
and document types to all documents, effectively allowing you to
|
||||
back-tag all previously indexed documents with metadata created (or
|
||||
modified) after their initial import.
|
||||
""".replace(" ", "")
|
||||
""".replace(
|
||||
" ", ""
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"-f", "--overwrite",
|
||||
"-f",
|
||||
"--overwrite",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Recreates the archived document for documents that already "
|
||||
"have an archived version."
|
||||
"have an archived version.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d", "--document",
|
||||
"-d",
|
||||
"--document",
|
||||
default=None,
|
||||
type=int,
|
||||
required=False,
|
||||
help="Specify the ID of a document, and this command will only "
|
||||
"run on this specific document."
|
||||
"run on this specific document.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-progress-bar",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If set, the progress bar will not be shown"
|
||||
help="If set, the progress bar will not be shown",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
@@ -119,18 +120,17 @@ class Command(BaseCommand):
|
||||
|
||||
overwrite = options["overwrite"]
|
||||
|
||||
if options['document']:
|
||||
documents = Document.objects.filter(pk=options['document'])
|
||||
if options["document"]:
|
||||
documents = Document.objects.filter(pk=options["document"])
|
||||
else:
|
||||
documents = Document.objects.all()
|
||||
|
||||
document_ids = list(map(
|
||||
lambda doc: doc.id,
|
||||
filter(
|
||||
lambda d: overwrite or not d.has_archive_version,
|
||||
documents
|
||||
document_ids = list(
|
||||
map(
|
||||
lambda doc: doc.id,
|
||||
filter(lambda d: overwrite or not d.has_archive_version, documents),
|
||||
)
|
||||
))
|
||||
)
|
||||
|
||||
# Note to future self: this prevents django from reusing database
|
||||
# conncetions between processes, which is bad and does not work
|
||||
@@ -141,13 +141,12 @@ class Command(BaseCommand):
|
||||
|
||||
logging.getLogger().handlers[0].level = logging.ERROR
|
||||
with multiprocessing.Pool(processes=settings.TASK_WORKERS) as pool:
|
||||
list(tqdm.tqdm(
|
||||
pool.imap_unordered(
|
||||
handle_document,
|
||||
document_ids
|
||||
),
|
||||
total=len(document_ids),
|
||||
disable=options['no_progress_bar']
|
||||
))
|
||||
list(
|
||||
tqdm.tqdm(
|
||||
pool.imap_unordered(handle_document, document_ids),
|
||||
total=len(document_ids),
|
||||
disable=options["no_progress_bar"],
|
||||
)
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
print("Aborting...")
|
||||
|
@@ -23,24 +23,21 @@ logger = logging.getLogger("paperless.management.consumer")
|
||||
|
||||
def _tags_from_path(filepath):
|
||||
"""Walk up the directory tree from filepath to CONSUMPTION_DIR
|
||||
and get or create Tag IDs for every directory.
|
||||
and get or create Tag IDs for every directory.
|
||||
"""
|
||||
tag_ids = set()
|
||||
path_parts = Path(filepath).relative_to(
|
||||
settings.CONSUMPTION_DIR).parent.parts
|
||||
path_parts = Path(filepath).relative_to(settings.CONSUMPTION_DIR).parent.parts
|
||||
for part in path_parts:
|
||||
tag_ids.add(Tag.objects.get_or_create(name__iexact=part, defaults={
|
||||
"name": part
|
||||
})[0].pk)
|
||||
tag_ids.add(
|
||||
Tag.objects.get_or_create(name__iexact=part, defaults={"name": part})[0].pk
|
||||
)
|
||||
|
||||
return tag_ids
|
||||
|
||||
|
||||
def _is_ignored(filepath: str) -> bool:
|
||||
filepath_relative = PurePath(filepath).relative_to(
|
||||
settings.CONSUMPTION_DIR)
|
||||
return any(
|
||||
filepath_relative.match(p) for p in settings.CONSUMER_IGNORE_PATTERNS)
|
||||
filepath_relative = PurePath(filepath).relative_to(settings.CONSUMPTION_DIR)
|
||||
return any(filepath_relative.match(p) for p in settings.CONSUMER_IGNORE_PATTERNS)
|
||||
|
||||
|
||||
def _consume(filepath):
|
||||
@@ -48,13 +45,11 @@ def _consume(filepath):
|
||||
return
|
||||
|
||||
if not os.path.isfile(filepath):
|
||||
logger.debug(
|
||||
f"Not consuming file {filepath}: File has moved.")
|
||||
logger.debug(f"Not consuming file {filepath}: File has moved.")
|
||||
return
|
||||
|
||||
if not is_file_ext_supported(os.path.splitext(filepath)[1]):
|
||||
logger.warning(
|
||||
f"Not consuming file {filepath}: Unknown file extension.")
|
||||
logger.warning(f"Not consuming file {filepath}: Unknown file extension.")
|
||||
return
|
||||
|
||||
tag_ids = None
|
||||
@@ -66,10 +61,12 @@ def _consume(filepath):
|
||||
|
||||
try:
|
||||
logger.info(f"Adding {filepath} to the task queue.")
|
||||
async_task("documents.tasks.consume_file",
|
||||
filepath,
|
||||
override_tag_ids=tag_ids if tag_ids else None,
|
||||
task_name=os.path.basename(filepath)[:100])
|
||||
async_task(
|
||||
"documents.tasks.consume_file",
|
||||
filepath,
|
||||
override_tag_ids=tag_ids if tag_ids else None,
|
||||
task_name=os.path.basename(filepath)[:100],
|
||||
)
|
||||
except Exception as e:
|
||||
# Catch all so that the consumer won't crash.
|
||||
# This is also what the test case is listening for to check for
|
||||
@@ -88,8 +85,9 @@ def _consume_wait_unmodified(file):
|
||||
try:
|
||||
new_mtime = os.stat(file).st_mtime
|
||||
except FileNotFoundError:
|
||||
logger.debug(f"File {file} moved while waiting for it to remain "
|
||||
f"unmodified.")
|
||||
logger.debug(
|
||||
f"File {file} moved while waiting for it to remain " f"unmodified."
|
||||
)
|
||||
return
|
||||
if new_mtime == mtime:
|
||||
_consume(file)
|
||||
@@ -102,16 +100,11 @@ def _consume_wait_unmodified(file):
|
||||
|
||||
|
||||
class Handler(FileSystemEventHandler):
|
||||
|
||||
def on_created(self, event):
|
||||
Thread(
|
||||
target=_consume_wait_unmodified, args=(event.src_path,)
|
||||
).start()
|
||||
Thread(target=_consume_wait_unmodified, args=(event.src_path,)).start()
|
||||
|
||||
def on_moved(self, event):
|
||||
Thread(
|
||||
target=_consume_wait_unmodified, args=(event.dest_path,)
|
||||
).start()
|
||||
Thread(target=_consume_wait_unmodified, args=(event.dest_path,)).start()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@@ -130,26 +123,19 @@ class Command(BaseCommand):
|
||||
"directory",
|
||||
default=settings.CONSUMPTION_DIR,
|
||||
nargs="?",
|
||||
help="The consumption directory."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--oneshot",
|
||||
action="store_true",
|
||||
help="Run only once."
|
||||
help="The consumption directory.",
|
||||
)
|
||||
parser.add_argument("--oneshot", action="store_true", help="Run only once.")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
directory = options["directory"]
|
||||
recursive = settings.CONSUMER_RECURSIVE
|
||||
|
||||
if not directory:
|
||||
raise CommandError(
|
||||
"CONSUMPTION_DIR does not appear to be set."
|
||||
)
|
||||
raise CommandError("CONSUMPTION_DIR does not appear to be set.")
|
||||
|
||||
if not os.path.isdir(directory):
|
||||
raise CommandError(
|
||||
f"Consumption directory {directory} does not exist")
|
||||
raise CommandError(f"Consumption directory {directory} does not exist")
|
||||
|
||||
if recursive:
|
||||
for dirpath, _, filenames in os.walk(directory):
|
||||
@@ -171,8 +157,7 @@ class Command(BaseCommand):
|
||||
logger.debug("Consumer exiting.")
|
||||
|
||||
def handle_polling(self, directory, recursive):
|
||||
logger.info(
|
||||
f"Polling directory for changes: {directory}")
|
||||
logger.info(f"Polling directory for changes: {directory}")
|
||||
self.observer = PollingObserver(timeout=settings.CONSUMER_POLLING)
|
||||
self.observer.schedule(Handler(), directory, recursive=recursive)
|
||||
self.observer.start()
|
||||
@@ -186,8 +171,7 @@ class Command(BaseCommand):
|
||||
self.observer.join()
|
||||
|
||||
def handle_inotify(self, directory, recursive):
|
||||
logger.info(
|
||||
f"Using inotify to watch directory for changes: {directory}")
|
||||
logger.info(f"Using inotify to watch directory for changes: {directory}")
|
||||
|
||||
inotify = INotify()
|
||||
inotify_flags = flags.CLOSE_WRITE | flags.MOVED_TO
|
||||
|
@@ -8,7 +8,9 @@ class Command(BaseCommand):
|
||||
help = """
|
||||
Trains the classifier on your data and saves the resulting models to a
|
||||
file. The document consumer will then automatically use this new model.
|
||||
""".replace(" ", "")
|
||||
""".replace(
|
||||
" ", ""
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
BaseCommand.__init__(self, *args, **kwargs)
|
||||
|
@@ -12,10 +12,19 @@ from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import transaction
|
||||
from filelock import FileLock
|
||||
|
||||
from documents.models import Document, Correspondent, Tag, DocumentType, \
|
||||
SavedView, SavedViewFilterRule
|
||||
from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \
|
||||
EXPORTER_ARCHIVE_NAME
|
||||
from documents.models import (
|
||||
Document,
|
||||
Correspondent,
|
||||
Tag,
|
||||
DocumentType,
|
||||
SavedView,
|
||||
SavedViewFilterRule,
|
||||
)
|
||||
from documents.settings import (
|
||||
EXPORTER_FILE_NAME,
|
||||
EXPORTER_THUMBNAIL_NAME,
|
||||
EXPORTER_ARCHIVE_NAME,
|
||||
)
|
||||
from paperless.db import GnuPG
|
||||
from paperless_mail.models import MailAccount, MailRule
|
||||
from ...file_handling import generate_filename, delete_empty_directories
|
||||
@@ -27,41 +36,46 @@ class Command(BaseCommand):
|
||||
Decrypt and rename all files in our collection into a given target
|
||||
directory. And include a manifest file containing document data for
|
||||
easy import.
|
||||
""".replace(" ", "")
|
||||
""".replace(
|
||||
" ", ""
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("target")
|
||||
|
||||
parser.add_argument(
|
||||
"-c", "--compare-checksums",
|
||||
"-c",
|
||||
"--compare-checksums",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Compare file checksums when determining whether to export "
|
||||
"a file or not. If not specified, file size and time "
|
||||
"modified is used instead."
|
||||
"a file or not. If not specified, file size and time "
|
||||
"modified is used instead.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-f", "--use-filename-format",
|
||||
"-f",
|
||||
"--use-filename-format",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Use PAPERLESS_FILENAME_FORMAT for storing files in the "
|
||||
"export directory, if configured."
|
||||
"export directory, if configured.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-d", "--delete",
|
||||
"-d",
|
||||
"--delete",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="After exporting, delete files in the export directory that "
|
||||
"do not belong to the current export, such as files from "
|
||||
"deleted documents."
|
||||
"do not belong to the current export, such as files from "
|
||||
"deleted documents.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-progress-bar",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If set, the progress bar will not be shown"
|
||||
help="If set, the progress bar will not be shown",
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -76,9 +90,9 @@ class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
|
||||
self.target = options["target"]
|
||||
self.compare_checksums = options['compare_checksums']
|
||||
self.use_filename_format = options['use_filename_format']
|
||||
self.delete = options['delete']
|
||||
self.compare_checksums = options["compare_checksums"]
|
||||
self.use_filename_format = options["use_filename_format"]
|
||||
self.delete = options["delete"]
|
||||
|
||||
if not os.path.exists(self.target):
|
||||
raise CommandError("That path doesn't exist")
|
||||
@@ -87,7 +101,7 @@ class Command(BaseCommand):
|
||||
raise CommandError("That path doesn't appear to be writable")
|
||||
|
||||
with FileLock(settings.MEDIA_LOCK):
|
||||
self.dump(options['no_progress_bar'])
|
||||
self.dump(options["no_progress_bar"])
|
||||
|
||||
def dump(self, progress_bar_disable=False):
|
||||
# 1. Take a snapshot of what files exist in the current export folder
|
||||
@@ -100,43 +114,48 @@ class Command(BaseCommand):
|
||||
# documents
|
||||
with transaction.atomic():
|
||||
manifest = json.loads(
|
||||
serializers.serialize("json", Correspondent.objects.all()))
|
||||
serializers.serialize("json", Correspondent.objects.all())
|
||||
)
|
||||
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", Tag.objects.all()))
|
||||
manifest += json.loads(serializers.serialize("json", Tag.objects.all()))
|
||||
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", DocumentType.objects.all()))
|
||||
manifest += json.loads(
|
||||
serializers.serialize("json", DocumentType.objects.all())
|
||||
)
|
||||
|
||||
documents = Document.objects.order_by("id")
|
||||
document_map = {d.pk: d for d in documents}
|
||||
document_manifest = json.loads(
|
||||
serializers.serialize("json", documents))
|
||||
document_manifest = json.loads(serializers.serialize("json", documents))
|
||||
manifest += document_manifest
|
||||
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", MailAccount.objects.all()))
|
||||
manifest += json.loads(
|
||||
serializers.serialize("json", MailAccount.objects.all())
|
||||
)
|
||||
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", MailRule.objects.all()))
|
||||
manifest += json.loads(
|
||||
serializers.serialize("json", MailRule.objects.all())
|
||||
)
|
||||
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", SavedView.objects.all()))
|
||||
manifest += json.loads(
|
||||
serializers.serialize("json", SavedView.objects.all())
|
||||
)
|
||||
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", SavedViewFilterRule.objects.all()))
|
||||
manifest += json.loads(
|
||||
serializers.serialize("json", SavedViewFilterRule.objects.all())
|
||||
)
|
||||
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", User.objects.all()))
|
||||
manifest += json.loads(serializers.serialize("json", User.objects.all()))
|
||||
|
||||
# 3. Export files from each document
|
||||
for index, document_dict in tqdm.tqdm(
|
||||
enumerate(document_manifest),
|
||||
total=len(document_manifest),
|
||||
disable=progress_bar_disable
|
||||
disable=progress_bar_disable,
|
||||
):
|
||||
# 3.1. store files unencrypted
|
||||
document_dict["fields"]["storage_type"] = Document.STORAGE_TYPE_UNENCRYPTED # NOQA: E501
|
||||
document_dict["fields"][
|
||||
"storage_type"
|
||||
] = Document.STORAGE_TYPE_UNENCRYPTED # NOQA: E501
|
||||
|
||||
document = document_map[document_dict["pk"]]
|
||||
|
||||
@@ -145,11 +164,10 @@ class Command(BaseCommand):
|
||||
while True:
|
||||
if self.use_filename_format:
|
||||
base_name = generate_filename(
|
||||
document, counter=filename_counter,
|
||||
append_gpg=False)
|
||||
document, counter=filename_counter, append_gpg=False
|
||||
)
|
||||
else:
|
||||
base_name = document.get_public_filename(
|
||||
counter=filename_counter)
|
||||
base_name = document.get_public_filename(counter=filename_counter)
|
||||
|
||||
if base_name not in self.exported_files:
|
||||
self.exported_files.append(base_name)
|
||||
@@ -193,22 +211,19 @@ class Command(BaseCommand):
|
||||
f.write(GnuPG.decrypted(document.archive_path))
|
||||
os.utime(archive_target, times=(t, t))
|
||||
else:
|
||||
self.check_and_copy(document.source_path,
|
||||
document.checksum,
|
||||
original_target)
|
||||
self.check_and_copy(
|
||||
document.source_path, document.checksum, original_target
|
||||
)
|
||||
|
||||
self.check_and_copy(document.thumbnail_path,
|
||||
None,
|
||||
thumbnail_target)
|
||||
self.check_and_copy(document.thumbnail_path, None, thumbnail_target)
|
||||
|
||||
if archive_target:
|
||||
self.check_and_copy(document.archive_path,
|
||||
document.archive_checksum,
|
||||
archive_target)
|
||||
self.check_and_copy(
|
||||
document.archive_path, document.archive_checksum, archive_target
|
||||
)
|
||||
|
||||
# 4. write manifest to target forlder
|
||||
manifest_path = os.path.abspath(
|
||||
os.path.join(self.target, "manifest.json"))
|
||||
manifest_path = os.path.abspath(os.path.join(self.target, "manifest.json"))
|
||||
|
||||
with open(manifest_path, "w") as f:
|
||||
json.dump(manifest, f, indent=2)
|
||||
@@ -222,8 +237,9 @@ class Command(BaseCommand):
|
||||
for f in self.files_in_export_dir:
|
||||
os.remove(f)
|
||||
|
||||
delete_empty_directories(os.path.abspath(os.path.dirname(f)),
|
||||
os.path.abspath(self.target))
|
||||
delete_empty_directories(
|
||||
os.path.abspath(os.path.dirname(f)), os.path.abspath(self.target)
|
||||
)
|
||||
|
||||
def check_and_copy(self, source, source_checksum, target):
|
||||
if os.path.abspath(target) in self.files_in_export_dir:
|
||||
|
@@ -12,8 +12,11 @@ from django.db.models.signals import post_save, m2m_changed
|
||||
from filelock import FileLock
|
||||
|
||||
from documents.models import Document
|
||||
from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \
|
||||
EXPORTER_ARCHIVE_NAME
|
||||
from documents.settings import (
|
||||
EXPORTER_FILE_NAME,
|
||||
EXPORTER_THUMBNAIL_NAME,
|
||||
EXPORTER_ARCHIVE_NAME,
|
||||
)
|
||||
from ...file_handling import create_source_path_directory
|
||||
from ...signals.handlers import update_filename_and_move_files
|
||||
|
||||
@@ -32,7 +35,9 @@ class Command(BaseCommand):
|
||||
help = """
|
||||
Using a manifest.json file, load the data from there, and import the
|
||||
documents it refers to.
|
||||
""".replace(" ", "")
|
||||
""".replace(
|
||||
" ", ""
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("source")
|
||||
@@ -40,7 +45,7 @@ class Command(BaseCommand):
|
||||
"--no-progress-bar",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If set, the progress bar will not be shown"
|
||||
help="If set, the progress bar will not be shown",
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -67,26 +72,27 @@ class Command(BaseCommand):
|
||||
self.manifest = json.load(f)
|
||||
|
||||
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):
|
||||
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)
|
||||
|
||||
self._import_files_from_manifest(options['no_progress_bar'])
|
||||
self._import_files_from_manifest(options["no_progress_bar"])
|
||||
|
||||
print("Updating search index...")
|
||||
call_command('document_index', 'reindex')
|
||||
call_command("document_index", "reindex")
|
||||
|
||||
@staticmethod
|
||||
def _check_manifest_exists(path):
|
||||
if not os.path.exists(path):
|
||||
raise CommandError(
|
||||
"That directory doesn't appear to contain a manifest.json "
|
||||
"file."
|
||||
"That directory doesn't appear to contain a manifest.json " "file."
|
||||
)
|
||||
|
||||
def _check_manifest(self):
|
||||
@@ -98,15 +104,15 @@ class Command(BaseCommand):
|
||||
|
||||
if EXPORTER_FILE_NAME not in record:
|
||||
raise CommandError(
|
||||
'The manifest file contains a record which does not '
|
||||
'refer to an actual document file.'
|
||||
"The manifest file contains a record which does not "
|
||||
"refer to an actual document file."
|
||||
)
|
||||
|
||||
doc_file = record[EXPORTER_FILE_NAME]
|
||||
if not os.path.exists(os.path.join(self.source, doc_file)):
|
||||
raise CommandError(
|
||||
'The manifest file refers to "{}" which does not '
|
||||
'appear to be in the source directory.'.format(doc_file)
|
||||
"appear to be in the source directory.".format(doc_file)
|
||||
)
|
||||
|
||||
if EXPORTER_ARCHIVE_NAME in record:
|
||||
@@ -125,14 +131,11 @@ class Command(BaseCommand):
|
||||
|
||||
print("Copy files into paperless...")
|
||||
|
||||
manifest_documents = list(filter(
|
||||
lambda r: r["model"] == "documents.document",
|
||||
self.manifest))
|
||||
manifest_documents = list(
|
||||
filter(lambda r: r["model"] == "documents.document", self.manifest)
|
||||
)
|
||||
|
||||
for record in tqdm.tqdm(
|
||||
manifest_documents,
|
||||
disable=progress_bar_disable
|
||||
):
|
||||
for record in tqdm.tqdm(manifest_documents, disable=progress_bar_disable):
|
||||
|
||||
document = Document.objects.get(pk=record["pk"])
|
||||
|
||||
|
@@ -9,17 +9,17 @@ class Command(BaseCommand):
|
||||
help = "Manages the document index."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("command", choices=['reindex', 'optimize'])
|
||||
parser.add_argument("command", choices=["reindex", "optimize"])
|
||||
parser.add_argument(
|
||||
"--no-progress-bar",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If set, the progress bar will not be shown"
|
||||
help="If set, the progress bar will not be shown",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
with transaction.atomic():
|
||||
if options['command'] == 'reindex':
|
||||
index_reindex(progress_bar_disable=options['no_progress_bar'])
|
||||
elif options['command'] == 'optimize':
|
||||
if options["command"] == "reindex":
|
||||
index_reindex(progress_bar_disable=options["no_progress_bar"])
|
||||
elif options["command"] == "optimize":
|
||||
index_optimize()
|
||||
|
@@ -11,14 +11,16 @@ class Command(BaseCommand):
|
||||
|
||||
help = """
|
||||
This will rename all documents to match the latest filename format.
|
||||
""".replace(" ", "")
|
||||
""".replace(
|
||||
" ", ""
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--no-progress-bar",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If set, the progress bar will not be shown"
|
||||
help="If set, the progress bar will not be shown",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
@@ -26,7 +28,6 @@ class Command(BaseCommand):
|
||||
logging.getLogger().handlers[0].level = logging.ERROR
|
||||
|
||||
for document in tqdm.tqdm(
|
||||
Document.objects.all(),
|
||||
disable=options['no_progress_bar']
|
||||
Document.objects.all(), disable=options["no_progress_bar"]
|
||||
):
|
||||
post_save.send(Document, instance=document)
|
||||
|
@@ -18,60 +18,46 @@ class Command(BaseCommand):
|
||||
and document types to all documents, effectively allowing you to
|
||||
back-tag all previously indexed documents with metadata created (or
|
||||
modified) after their initial import.
|
||||
""".replace(" ", "")
|
||||
""".replace(
|
||||
" ", ""
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"-c", "--correspondent",
|
||||
default=False,
|
||||
action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-T", "--tags",
|
||||
default=False,
|
||||
action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t", "--document_type",
|
||||
default=False,
|
||||
action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i", "--inbox-only",
|
||||
default=False,
|
||||
action="store_true"
|
||||
)
|
||||
parser.add_argument("-c", "--correspondent", default=False, action="store_true")
|
||||
parser.add_argument("-T", "--tags", default=False, action="store_true")
|
||||
parser.add_argument("-t", "--document_type", default=False, action="store_true")
|
||||
parser.add_argument("-i", "--inbox-only", default=False, action="store_true")
|
||||
parser.add_argument(
|
||||
"--use-first",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="By default this command won't try to assign a correspondent "
|
||||
"if more than one matches the document. Use this flag if "
|
||||
"you'd rather it just pick the first one it finds."
|
||||
"if more than one matches the document. Use this flag if "
|
||||
"you'd rather it just pick the first one it finds.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f", "--overwrite",
|
||||
"-f",
|
||||
"--overwrite",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If set, the document retagger will overwrite any previously"
|
||||
"set correspondent, document and remove correspondents, types"
|
||||
"and tags that do not match anymore due to changed rules."
|
||||
"set correspondent, document and remove correspondents, types"
|
||||
"and tags that do not match anymore due to changed rules.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-progress-bar",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If set, the progress bar will not be shown"
|
||||
help="If set, the progress bar will not be shown",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--suggest",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Return the suggestion, don't change anything."
|
||||
help="Return the suggestion, don't change anything.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--base-url",
|
||||
help="The base URL to use to build the link to the documents."
|
||||
"--base-url", help="The base URL to use to build the link to the documents."
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
@@ -86,38 +72,39 @@ class Command(BaseCommand):
|
||||
|
||||
classifier = load_classifier()
|
||||
|
||||
for document in tqdm.tqdm(
|
||||
documents,
|
||||
disable=options['no_progress_bar']
|
||||
):
|
||||
for document in tqdm.tqdm(documents, disable=options["no_progress_bar"]):
|
||||
|
||||
if options['correspondent']:
|
||||
if options["correspondent"]:
|
||||
set_correspondent(
|
||||
sender=None,
|
||||
document=document,
|
||||
classifier=classifier,
|
||||
replace=options['overwrite'],
|
||||
use_first=options['use_first'],
|
||||
suggest=options['suggest'],
|
||||
base_url=options['base_url'],
|
||||
color=color)
|
||||
replace=options["overwrite"],
|
||||
use_first=options["use_first"],
|
||||
suggest=options["suggest"],
|
||||
base_url=options["base_url"],
|
||||
color=color,
|
||||
)
|
||||
|
||||
if options['document_type']:
|
||||
set_document_type(sender=None,
|
||||
document=document,
|
||||
classifier=classifier,
|
||||
replace=options['overwrite'],
|
||||
use_first=options['use_first'],
|
||||
suggest=options['suggest'],
|
||||
base_url=options['base_url'],
|
||||
color=color)
|
||||
if options["document_type"]:
|
||||
set_document_type(
|
||||
sender=None,
|
||||
document=document,
|
||||
classifier=classifier,
|
||||
replace=options["overwrite"],
|
||||
use_first=options["use_first"],
|
||||
suggest=options["suggest"],
|
||||
base_url=options["base_url"],
|
||||
color=color,
|
||||
)
|
||||
|
||||
if options['tags']:
|
||||
if options["tags"]:
|
||||
set_tags(
|
||||
sender=None,
|
||||
document=document,
|
||||
classifier=classifier,
|
||||
replace=options['overwrite'],
|
||||
suggest=options['suggest'],
|
||||
base_url=options['base_url'],
|
||||
color=color)
|
||||
replace=options["overwrite"],
|
||||
suggest=options["suggest"],
|
||||
base_url=options["base_url"],
|
||||
color=color,
|
||||
)
|
||||
|
@@ -6,18 +6,20 @@ class Command(BaseCommand):
|
||||
|
||||
help = """
|
||||
This command checks your document archive for issues.
|
||||
""".replace(" ", "")
|
||||
""".replace(
|
||||
" ", ""
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--no-progress-bar",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If set, the progress bar will not be shown"
|
||||
help="If set, the progress bar will not be shown",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
messages = check_sanity(progress=not options['no_progress_bar'])
|
||||
messages = check_sanity(progress=not options["no_progress_bar"])
|
||||
|
||||
messages.log_messages()
|
||||
|
@@ -22,9 +22,7 @@ def _process_document(doc_in):
|
||||
|
||||
try:
|
||||
thumb = parser.get_optimised_thumbnail(
|
||||
document.source_path,
|
||||
document.mime_type,
|
||||
document.get_public_filename()
|
||||
document.source_path, document.mime_type, document.get_public_filename()
|
||||
)
|
||||
|
||||
shutil.move(thumb, document.thumbnail_path)
|
||||
@@ -36,29 +34,32 @@ class Command(BaseCommand):
|
||||
|
||||
help = """
|
||||
This will regenerate the thumbnails for all documents.
|
||||
""".replace(" ", "")
|
||||
""".replace(
|
||||
" ", ""
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"-d", "--document",
|
||||
"-d",
|
||||
"--document",
|
||||
default=None,
|
||||
type=int,
|
||||
required=False,
|
||||
help="Specify the ID of a document, and this command will only "
|
||||
"run on this specific document."
|
||||
"run on this specific document.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-progress-bar",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If set, the progress bar will not be shown"
|
||||
help="If set, the progress bar will not be shown",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
logging.getLogger().handlers[0].level = logging.ERROR
|
||||
|
||||
if options['document']:
|
||||
documents = Document.objects.filter(pk=options['document'])
|
||||
if options["document"]:
|
||||
documents = Document.objects.filter(pk=options["document"])
|
||||
else:
|
||||
documents = Document.objects.all()
|
||||
|
||||
@@ -70,8 +71,10 @@ class Command(BaseCommand):
|
||||
db.connections.close_all()
|
||||
|
||||
with multiprocessing.Pool() as pool:
|
||||
list(tqdm.tqdm(
|
||||
pool.imap_unordered(_process_document, ids),
|
||||
total=len(ids),
|
||||
disable=options['no_progress_bar']
|
||||
))
|
||||
list(
|
||||
tqdm.tqdm(
|
||||
pool.imap_unordered(_process_document, ids),
|
||||
total=len(ids),
|
||||
disable=options["no_progress_bar"],
|
||||
)
|
||||
)
|
||||
|
@@ -10,11 +10,11 @@ class Command(LoadDataCommand):
|
||||
"""
|
||||
|
||||
def parse_name(self, fixture_name):
|
||||
self.compression_formats['stdin'] = (lambda x, y: sys.stdin, None)
|
||||
if fixture_name == '-':
|
||||
return '-', 'json', 'stdin'
|
||||
self.compression_formats["stdin"] = (lambda x, y: sys.stdin, None)
|
||||
if fixture_name == "-":
|
||||
return "-", "json", "stdin"
|
||||
|
||||
def find_fixtures(self, fixture_label):
|
||||
if fixture_label == '-':
|
||||
return [('-', None, '-')]
|
||||
if fixture_label == "-":
|
||||
return [("-", None, "-")]
|
||||
return super(Command, self).find_fixtures(fixture_label)
|
||||
|
@@ -12,16 +12,18 @@ class Command(BaseCommand):
|
||||
|
||||
help = """
|
||||
Creates a Django superuser based on env variables.
|
||||
""".replace(" ", "")
|
||||
""".replace(
|
||||
" ", ""
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
username = os.getenv('PAPERLESS_ADMIN_USER')
|
||||
username = os.getenv("PAPERLESS_ADMIN_USER")
|
||||
if not username:
|
||||
return
|
||||
|
||||
mail = os.getenv('PAPERLESS_ADMIN_MAIL', 'root@localhost')
|
||||
password = os.getenv('PAPERLESS_ADMIN_PASSWORD')
|
||||
mail = os.getenv("PAPERLESS_ADMIN_MAIL", "root@localhost")
|
||||
password = os.getenv("PAPERLESS_ADMIN_PASSWORD")
|
||||
|
||||
# Check if user exists already, leave as is if it does
|
||||
if User.objects.filter(username=username).exists():
|
||||
@@ -32,11 +34,10 @@ class Command(BaseCommand):
|
||||
elif password:
|
||||
# Create superuser based on env variables
|
||||
User.objects.create_superuser(username, mail, password)
|
||||
self.stdout.write(
|
||||
f'Created superuser "{username}" with provided password.')
|
||||
self.stdout.write(f'Created superuser "{username}" with provided password.')
|
||||
else:
|
||||
self.stdout.write(
|
||||
f'Did not create superuser "{username}".')
|
||||
self.stdout.write(f'Did not create superuser "{username}".')
|
||||
self.stdout.write(
|
||||
'Make sure you specified "PAPERLESS_ADMIN_PASSWORD" in your '
|
||||
'"docker-compose.env" file.')
|
||||
'"docker-compose.env" file.'
|
||||
)
|
||||
|
Reference in New Issue
Block a user