paperless-ngx/src/documents/tasks.py

197 lines
6.2 KiB
Python
Raw Normal View History

import logging
import tqdm
from django.conf import settings
2020-12-11 14:27:54 +01:00
from django.db.models.signals import post_save
from documents import index
from documents import sanity_checker
from documents.classifier import DocumentClassifier
from documents.classifier import load_classifier
from documents.consumer import Consumer
from documents.consumer import ConsumerError
from documents.models import Correspondent
from documents.models import Document
from documents.models import DocumentType
from documents.models import Tag
from documents.sanity_checker import SanityCheckFailedException
from whoosh.writing import AsyncWriter
# barcode decoder
import os
from pyzbar import pyzbar
from pdf2image import convert_from_path
import tempfile
from pikepdf import Pdf
2021-02-05 01:10:29 +01:00
logger = logging.getLogger("paperless.tasks")
def index_optimize():
2020-11-28 11:49:46 +01:00
ix = index.open_index()
2020-11-30 21:38:21 +01:00
writer = AsyncWriter(ix)
writer.commit(optimize=True)
def index_reindex(progress_bar_disable=False):
documents = Document.objects.all()
ix = index.open_index(recreate=True)
with AsyncWriter(ix) as writer:
for document in tqdm.tqdm(documents, disable=progress_bar_disable):
index.update_document(writer, document)
def train_classifier():
2022-02-27 15:26:41 +01:00
if (
not Tag.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
and not DocumentType.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
and not Correspondent.objects.filter(matching_algorithm=Tag.MATCH_AUTO).exists()
):
return
classifier = load_classifier()
if not classifier:
2020-12-30 21:54:36 +01:00
classifier = DocumentClassifier()
try:
if classifier.train():
2021-02-05 01:10:29 +01:00
logger.info(
"Saving updated classifier model to {}...".format(settings.MODEL_FILE),
)
2021-02-06 20:54:58 +01:00
classifier.save()
else:
2022-02-27 15:26:41 +01:00
logger.debug("Training data unchanged.")
except Exception as e:
2022-02-27 15:26:41 +01:00
logger.warning("Classifier error: " + str(e))
2020-11-16 18:26:54 +01:00
def barcode_reader(page) -> list:
"""
Read any barcodes contained in page
Returns a list containing all found barcodes
"""
barcodes = [ ]
# Decode the barcode image
detected_barcodes = pyzbar.decode(page)
if not detected_barcodes:
logger.debug(f"No barcode detected")
else:
# Traverse through all the detected barcodes in image
for barcode in detected_barcodes:
if barcode.data!="":
barcodes = barcodes + [str(barcode.data)]
logger.debug(f"Barcode of type {str(barcode.type)} found: {str(barcode.data)}")
return barcodes
def scan_file_for_seperating_barcodes(filepath) -> list:
"""
Scan the provided file for page seperating barcodes
Returns a list of pagenumbers, which seperate the file
"""
seperator_page_numbers = [ ]
# use a temporary directory in case the file os too big to handle in memory
with tempfile.TemporaryDirectory() as path:
pages_from_path = convert_from_path(filepath, output_folder=path)
for current_page_number, page in enumerate(pages_from_path):
current_barcodes = barcode_reader(page)
if current_barcodes.isin("PATCHT"):
seperator_page_numbers = seperator_page_numbers + current_page_number
return seperator_page_numbers
def seperate_pages(filepath, pages_to_split_on: list):
"""
Seperate the provided file on the pages_to_split_on.
The pages which are defined by page_numbers will be removed.
"""
pages_to_split_on = scan_file_for_seperating_barcodes(filepath)
fname = os.path.splitext(os.path.basename(filepath))[0]
pdf = Pdf.open(filepath)
# TODO: Get the directory of the file and save the other files there
# TODO: Return list of new paths of the new files
for count, page_number in enumerate(pages_to_split_on):
# First element, so iterate from zero to the first seperator page
if count == 0:
dst = Pdf.new()
for page in range(0, page_number):
dst.pages.append(page)
output_filename = '{}_page_{}.pdf'.format(
fname, str(count))
with open(output_filename, 'wb') as out:
dst.save(out)
else:
dst = Pdf.new()
for page in range(pages_to_split_on[count-1], page_number):
dst.pages.append(page)
output_filename = '{}_page_{}.pdf'.format(
fname, page+1)
with open(output_filename, 'wb') as out:
dst.save(out)
2022-02-27 15:26:41 +01:00
def consume_file(
path,
override_filename=None,
override_title=None,
override_correspondent_id=None,
override_document_type_id=None,
override_tag_ids=None,
task_id=None,
):
2020-11-16 18:26:54 +01:00
# check for seperators in current document
seperator_page_numbers = scan_file_for_seperating_barcodes(path)
if seperator_page_numbers != [ ]:
logger.debug(f"Pages with seperators found: {str(seperator_page_numbers)}")
2020-11-16 18:26:54 +01:00
document = Consumer().try_consume_file(
2020-11-17 11:49:44 +01:00
path,
override_filename=override_filename,
override_title=override_title,
override_correspondent_id=override_correspondent_id,
override_document_type_id=override_document_type_id,
2021-01-26 00:51:20 +01:00
override_tag_ids=override_tag_ids,
2022-02-27 15:26:41 +01:00
task_id=task_id,
2021-01-26 00:51:20 +01:00
)
2020-11-16 18:26:54 +01:00
if document:
2022-02-27 15:26:41 +01:00
return "Success. New document id {} created".format(document.pk)
2020-11-16 18:26:54 +01:00
else:
2022-02-27 15:26:41 +01:00
raise ConsumerError(
"Unknown error: Returned document was null, but "
"no error message was given.",
2022-02-27 15:26:41 +01:00
)
2020-11-25 16:04:58 +01:00
def sanity_check():
messages = sanity_checker.check_sanity()
messages.log_messages()
if messages.has_error():
2022-02-27 15:26:41 +01:00
raise SanityCheckFailedException("Sanity check failed with errors. See log.")
elif messages.has_warning():
return "Sanity check exited with warnings. See log."
elif len(messages) > 0:
return "Sanity check exited with infos. See log."
2020-11-25 16:04:58 +01:00
else:
return "No issues detected."
2020-12-11 14:27:54 +01:00
def bulk_update_documents(document_ids):
documents = Document.objects.filter(id__in=document_ids)
ix = index.open_index()
for doc in documents:
post_save.send(Document, instance=doc, created=False)
with AsyncWriter(ix) as writer:
for doc in documents:
index.update_document(writer, doc)