mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-07-06 03:55:10 +00:00
Try to automatically migrate user's DB settings values
This commit is contained in:
@@ -146,6 +146,10 @@ The new settings are independent:
|
||||
- [`PAPERLESS_OCR_MODE`](configuration.md#PAPERLESS_OCR_MODE) controls OCR: `auto` (default), `force`, `redo`, `off`.
|
||||
- [`PAPERLESS_ARCHIVE_FILE_GENERATION`](configuration.md#PAPERLESS_ARCHIVE_FILE_GENERATION) controls archive production: `auto` (default), `always`, `never`.
|
||||
|
||||
### Database configuration
|
||||
|
||||
If you changed OCR settings via the admin UI (ApplicationConfiguration), the database values are **migrated automatically** during the upgrade. `mode` values (`skip` / `skip_noarchive`) are mapped to their new equivalents and `skip_archive_file` values are converted to the new `archive_file_generation` field. After upgrading, review the OCR settings in the admin UI to confirm the migrated values match your intent.
|
||||
|
||||
### Action Required
|
||||
|
||||
Remove any `PAPERLESS_OCR_SKIP_ARCHIVE_FILE` variable from your environment. If you relied on `OCR_MODE=skip` or `OCR_MODE=skip_noarchive`, update accordingly:
|
||||
|
||||
@@ -3,6 +3,44 @@
|
||||
from django.db import migrations
|
||||
from django.db import models
|
||||
|
||||
_MODE_MAP = {
|
||||
"skip": "auto",
|
||||
"redo": "redo",
|
||||
"force": "force",
|
||||
"skip_noarchive": "auto",
|
||||
}
|
||||
|
||||
_ARCHIVE_MAP = {
|
||||
# never skip -> always generate
|
||||
"never": "always",
|
||||
# skip when text present -> auto
|
||||
"with_text": "auto",
|
||||
# always skip -> never generate
|
||||
"always": "never",
|
||||
}
|
||||
|
||||
|
||||
def migrate_old_values(apps, schema_editor):
|
||||
ApplicationConfiguration = apps.get_model("paperless", "ApplicationConfiguration")
|
||||
for config in ApplicationConfiguration.objects.all():
|
||||
old_mode = config.mode
|
||||
old_skip = config.skip_archive_file
|
||||
|
||||
# Map the old mode value
|
||||
if old_mode in _MODE_MAP:
|
||||
config.mode = _MODE_MAP[old_mode]
|
||||
|
||||
# Map skip_archive_file -> archive_file_generation
|
||||
if old_skip in _ARCHIVE_MAP:
|
||||
config.archive_file_generation = _ARCHIVE_MAP[old_skip]
|
||||
|
||||
# skip_noarchive implied no archive file; set that if the user
|
||||
# didn't already have an explicit skip_archive_file preference
|
||||
if old_mode == "skip_noarchive" and old_skip is None:
|
||||
config.archive_file_generation = "never"
|
||||
|
||||
config.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
@@ -10,21 +48,7 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="applicationconfiguration",
|
||||
name="skip_archive_file",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="applicationconfiguration",
|
||||
name="archive_file_generation",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[("auto", "auto"), ("always", "always"), ("never", "never")],
|
||||
max_length=8,
|
||||
null=True,
|
||||
verbose_name="Controls archive file generation",
|
||||
),
|
||||
),
|
||||
# 1. Update mode choices in-place (old values still in the column)
|
||||
migrations.AlterField(
|
||||
model_name="applicationconfiguration",
|
||||
name="mode",
|
||||
@@ -41,4 +65,26 @@ class Migration(migrations.Migration):
|
||||
verbose_name="Sets the OCR mode",
|
||||
),
|
||||
),
|
||||
# 2. Add the new field
|
||||
migrations.AddField(
|
||||
model_name="applicationconfiguration",
|
||||
name="archive_file_generation",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[("auto", "auto"), ("always", "always"), ("never", "never")],
|
||||
max_length=8,
|
||||
null=True,
|
||||
verbose_name="Controls archive file generation",
|
||||
),
|
||||
),
|
||||
# 3. Migrate data from old values to new
|
||||
migrations.RunPython(
|
||||
migrate_old_values,
|
||||
migrations.RunPython.noop,
|
||||
),
|
||||
# 4. Drop the old field
|
||||
migrations.RemoveField(
|
||||
model_name="applicationconfiguration",
|
||||
name="skip_archive_file",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
from documents.tests.utils import TestMigrations
|
||||
|
||||
|
||||
class TestMigrateSkipArchiveFile(TestMigrations):
|
||||
migrate_from = "0007_optimize_integer_field_sizes"
|
||||
migrate_to = "0008_replace_skip_archive_file"
|
||||
|
||||
def setUpBeforeMigration(self, apps):
|
||||
ApplicationConfiguration = apps.get_model(
|
||||
"paperless",
|
||||
"ApplicationConfiguration",
|
||||
)
|
||||
ApplicationConfiguration.objects.all().delete()
|
||||
ApplicationConfiguration.objects.create(
|
||||
pk=1,
|
||||
mode="skip",
|
||||
skip_archive_file="always",
|
||||
)
|
||||
ApplicationConfiguration.objects.create(
|
||||
pk=2,
|
||||
mode="redo",
|
||||
skip_archive_file="with_text",
|
||||
)
|
||||
ApplicationConfiguration.objects.create(
|
||||
pk=3,
|
||||
mode="force",
|
||||
skip_archive_file="never",
|
||||
)
|
||||
ApplicationConfiguration.objects.create(
|
||||
pk=4,
|
||||
mode="skip_noarchive",
|
||||
skip_archive_file=None,
|
||||
)
|
||||
ApplicationConfiguration.objects.create(
|
||||
pk=5,
|
||||
mode="skip_noarchive",
|
||||
skip_archive_file="never",
|
||||
)
|
||||
ApplicationConfiguration.objects.create(pk=6, mode=None, skip_archive_file=None)
|
||||
|
||||
def _get_config(self, pk):
|
||||
ApplicationConfiguration = self.apps.get_model(
|
||||
"paperless",
|
||||
"ApplicationConfiguration",
|
||||
)
|
||||
return ApplicationConfiguration.objects.get(pk=pk)
|
||||
|
||||
def test_skip_mapped_to_auto(self):
|
||||
config = self._get_config(1)
|
||||
assert config.mode == "auto"
|
||||
|
||||
def test_skip_archive_always_mapped_to_never(self):
|
||||
config = self._get_config(1)
|
||||
assert config.archive_file_generation == "never"
|
||||
|
||||
def test_redo_unchanged(self):
|
||||
config = self._get_config(2)
|
||||
assert config.mode == "redo"
|
||||
|
||||
def test_skip_archive_with_text_mapped_to_auto(self):
|
||||
config = self._get_config(2)
|
||||
assert config.archive_file_generation == "auto"
|
||||
|
||||
def test_force_unchanged(self):
|
||||
config = self._get_config(3)
|
||||
assert config.mode == "force"
|
||||
|
||||
def test_skip_archive_never_mapped_to_always(self):
|
||||
config = self._get_config(3)
|
||||
assert config.archive_file_generation == "always"
|
||||
|
||||
def test_skip_noarchive_mapped_to_auto(self):
|
||||
config = self._get_config(4)
|
||||
assert config.mode == "auto"
|
||||
|
||||
def test_skip_noarchive_implies_archive_never(self):
|
||||
config = self._get_config(4)
|
||||
assert config.archive_file_generation == "never"
|
||||
|
||||
def test_skip_noarchive_explicit_skip_archive_takes_precedence(self):
|
||||
"""skip_archive_file=never maps to always, not overridden by skip_noarchive."""
|
||||
config = self._get_config(5)
|
||||
assert config.mode == "auto"
|
||||
assert config.archive_file_generation == "always"
|
||||
|
||||
def test_null_values_remain_null(self):
|
||||
config = self._get_config(6)
|
||||
assert config.mode is None
|
||||
assert config.archive_file_generation is None
|
||||
Reference in New Issue
Block a user