diff --git a/docs/administration.rst b/docs/administration.rst index 1139c3a69..f6b0ed659 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -287,6 +287,10 @@ When you use the provided docker compose script, put the export inside the ``export`` folder in your paperless source directory. Specify ``../export`` as the ``source``. +.. note:: + + Importing from a previous version of Paperless may work, but for best results + it is suggested to match the versions. .. _utilities-retagger: diff --git a/src/documents/management/commands/document_archiver.py b/src/documents/management/commands/document_archiver.py index f33ccd7ce..bb376c2dd 100644 --- a/src/documents/management/commands/document_archiver.py +++ b/src/documents/management/commands/document_archiver.py @@ -152,4 +152,4 @@ class Command(BaseCommand): ), ) except KeyboardInterrupt: - print("Aborting...") + self.stdout.write(self.style.NOTICE(("Aborting..."))) diff --git a/src/documents/management/commands/document_exporter.py b/src/documents/management/commands/document_exporter.py index b110475a5..27f1fd8a9 100644 --- a/src/documents/management/commands/document_exporter.py +++ b/src/documents/management/commands/document_exporter.py @@ -22,6 +22,7 @@ from documents.settings import EXPORTER_ARCHIVE_NAME from documents.settings import EXPORTER_FILE_NAME from documents.settings import EXPORTER_THUMBNAIL_NAME from filelock import FileLock +from paperless import version from paperless.db import GnuPG from paperless_mail.models import MailAccount from paperless_mail.models import MailRule @@ -232,12 +233,18 @@ class Command(BaseCommand): archive_target, ) - # 4. write manifest to target forlder + # 4.1 write manifest to target folder 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) + # 4.2 write version information to target folder + version_path = os.path.abspath(os.path.join(self.target, "version.json")) + + with open(version_path, "w") as f: + json.dump({"version": version.__full_version_str__}, f, indent=2) + if self.delete: # 5. Remove files which we did not explicitly export in this run diff --git a/src/documents/management/commands/document_importer.py b/src/documents/management/commands/document_importer.py index d1ae33afb..1a1316059 100644 --- a/src/documents/management/commands/document_importer.py +++ b/src/documents/management/commands/document_importer.py @@ -6,9 +6,11 @@ from contextlib import contextmanager import tqdm from django.conf import settings +from django.core.exceptions import FieldDoesNotExist from django.core.management import call_command from django.core.management.base import BaseCommand from django.core.management.base import CommandError +from django.core.serializers.base import DeserializationError from django.db.models.signals import m2m_changed from django.db.models.signals import post_save from documents.models import Document @@ -16,6 +18,7 @@ from documents.settings import EXPORTER_ARCHIVE_NAME from documents.settings import EXPORTER_FILE_NAME from documents.settings import EXPORTER_THUMBNAIL_NAME from filelock import FileLock +from paperless import version from ...file_handling import create_source_path_directory from ...signals.handlers import update_filename_and_move_files @@ -53,6 +56,7 @@ class Command(BaseCommand): BaseCommand.__init__(self, *args, **kwargs) self.source = None self.manifest = None + self.version = None def handle(self, *args, **options): @@ -66,12 +70,30 @@ class Command(BaseCommand): if not os.access(self.source, os.R_OK): raise CommandError("That path doesn't appear to be readable") - manifest_path = os.path.join(self.source, "manifest.json") + manifest_path = os.path.normpath(os.path.join(self.source, "manifest.json")) self._check_manifest_exists(manifest_path) with open(manifest_path) as f: self.manifest = json.load(f) + version_path = os.path.normpath(os.path.join(self.source, "version.json")) + if os.path.exists(version_path): + with open(version_path) as f: + self.version = json.load(f)["version"] + # Provide an initial warning if needed to the user + if self.version != version.__full_version_str__: + self.stdout.write( + self.style.WARNING( + "Version mismatch: " + f"Currently {version.__full_version_str__}," + f" importing {self.version}." + " Continuing, but import may fail.", + ), + ) + + else: + self.stdout.write(self.style.NOTICE("No version.json file located")) + self._check_manifest() with disable_signal( post_save, @@ -84,12 +106,36 @@ class Command(BaseCommand): sender=Document.tags.through, ): # Fill up the database with whatever is in the manifest - call_command("loaddata", manifest_path) + try: + call_command("loaddata", manifest_path) + except (FieldDoesNotExist, DeserializationError) as e: + self.stdout.write(self.style.ERROR("Database import failed")) + if ( + self.version is not None + and self.version != version.__full_version_str__ + ): + self.stdout.write( + self.style.ERROR( + "Version mismatch: " + f"Currently {version.__full_version_str__}," + f" importing {self.version}", + ), + ) + raise e + else: + self.stdout.write( + self.style.ERROR("No version information present"), + ) + raise e self._import_files_from_manifest(options["no_progress_bar"]) - print("Updating search index...") - call_command("document_index", "reindex") + self.stdout.write("Updating search index...") + call_command( + "document_index", + "reindex", + no_progress_bar=options["no_progress_bar"], + ) @staticmethod def _check_manifest_exists(path): @@ -132,7 +178,7 @@ class Command(BaseCommand): os.makedirs(settings.THUMBNAIL_DIR, exist_ok=True) os.makedirs(settings.ARCHIVE_DIR, exist_ok=True) - print("Copy files into paperless...") + self.stdout.write("Copy files into paperless...") manifest_documents = list( filter(lambda r: r["model"] == "documents.document", self.manifest),