Use simple JSON field for display_fields

This commit is contained in:
shamoon 2024-04-20 22:11:50 -07:00
parent 6f546b9cba
commit c07e3f8379
5 changed files with 369 additions and 434 deletions

View File

@ -1,7 +1,6 @@
# Generated by Django 4.2.11 on 2024-04-16 18:35 # Generated by Django 4.2.11 on 2024-04-16 18:35
import django.core.validators import django.core.validators
import multiselectfield.db.fields
from django.db import migrations from django.db import migrations
from django.db import models from django.db import models
@ -41,18 +40,8 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="savedview", model_name="savedview",
name="display_fields", name="display_fields",
field=multiselectfield.db.fields.MultiSelectField( field=models.JSONField(
blank=True, blank=True,
choices=[
("title", "Title"),
("created", "Created"),
("added", "Added"),
("tag", "Tags"),
("documenttype", "Document Type"),
("correspondent", "Correspondent"),
("storagepath", "Storage Path"),
],
max_length=128,
null=True, null=True,
verbose_name="Document display fields", verbose_name="Document display fields",
), ),

View File

@ -393,164 +393,6 @@ class Log(models.Model):
return self.message return self.message
class CustomField(models.Model):
"""
Defines the name and type of a custom field
"""
class FieldDataType(models.TextChoices):
STRING = ("string", _("String"))
URL = ("url", _("URL"))
DATE = ("date", _("Date"))
BOOL = ("boolean"), _("Boolean")
INT = ("integer", _("Integer"))
FLOAT = ("float", _("Float"))
MONETARY = ("monetary", _("Monetary"))
DOCUMENTLINK = ("documentlink", _("Document Link"))
created = models.DateTimeField(
_("created"),
default=timezone.now,
db_index=True,
editable=False,
)
name = models.CharField(max_length=128)
data_type = models.CharField(
_("data type"),
max_length=50,
choices=FieldDataType.choices,
editable=False,
)
class Meta:
ordering = ("created",)
verbose_name = _("custom field")
verbose_name_plural = _("custom fields")
constraints = [
models.UniqueConstraint(
fields=["name"],
name="%(app_label)s_%(class)s_unique_name",
),
]
def __str__(self) -> str:
return f"{self.name} : {self.data_type}"
class CustomFieldInstance(models.Model):
"""
A single instance of a field, attached to a CustomField for the name and type
and attached to a single Document to be metadata for it
"""
created = models.DateTimeField(
_("created"),
default=timezone.now,
db_index=True,
editable=False,
)
document = models.ForeignKey(
Document,
blank=False,
null=False,
on_delete=models.CASCADE,
related_name="custom_fields",
editable=False,
)
field = models.ForeignKey(
CustomField,
blank=False,
null=False,
on_delete=models.CASCADE,
related_name="fields",
editable=False,
)
# Actual data storage
value_text = models.CharField(max_length=128, null=True)
value_bool = models.BooleanField(null=True)
value_url = models.URLField(null=True)
value_date = models.DateField(null=True)
value_int = models.IntegerField(null=True)
value_float = models.FloatField(null=True)
value_monetary = models.CharField(null=True, max_length=128)
value_document_ids = models.JSONField(null=True)
class Meta:
ordering = ("created",)
verbose_name = _("custom field instance")
verbose_name_plural = _("custom field instances")
constraints = [
models.UniqueConstraint(
fields=["document", "field"],
name="%(app_label)s_%(class)s_unique_document_field",
),
]
def __str__(self) -> str:
return str(self.field.name) + f" : {self.value}"
@property
def value(self):
"""
Based on the data type, access the actual value the instance stores
A little shorthand/quick way to get what is actually here
"""
if self.field.data_type == CustomField.FieldDataType.STRING:
return self.value_text
elif self.field.data_type == CustomField.FieldDataType.URL:
return self.value_url
elif self.field.data_type == CustomField.FieldDataType.DATE:
return self.value_date
elif self.field.data_type == CustomField.FieldDataType.BOOL:
return self.value_bool
elif self.field.data_type == CustomField.FieldDataType.INT:
return self.value_int
elif self.field.data_type == CustomField.FieldDataType.FLOAT:
return self.value_float
elif self.field.data_type == CustomField.FieldDataType.MONETARY:
return self.value_monetary
elif self.field.data_type == CustomField.FieldDataType.DOCUMENTLINK:
return self.value_document_ids
raise NotImplementedError(self.field.data_type) # pragma: no cover
class DynamicMultiSelectField(MultiSelectField):
"""
A MultiSelectField that can have dynamic choices from a model
"""
def __init__(self, *args, **kwargs):
self.dynamic_choices = kwargs.pop("dyanmic_choices", None)
super().__init__(*args, **kwargs)
def _get_choices(self):
return self._choices
def _set_choices(self, value):
if self.dynamic_choices:
for key, model in self.dynamic_choices:
try:
for obj in model.objects.all(): # pragma: no cover
value.append((key % obj.pk, obj.name))
except Exception:
pass
self._choices = value
choices = property(_get_choices, _set_choices)
class SavedView(ModelWithOwner): class SavedView(ModelWithOwner):
class DisplayMode(models.TextChoices): class DisplayMode(models.TextChoices):
TABLE = ("table", _("Table")) TABLE = ("table", _("Table"))
@ -565,9 +407,7 @@ class SavedView(ModelWithOwner):
DOCUMENT_TYPE = ("documenttype", _("Document Type")) DOCUMENT_TYPE = ("documenttype", _("Document Type"))
CORRESPONDENT = ("correspondent", _("Correspondent")) CORRESPONDENT = ("correspondent", _("Correspondent"))
STORAGE_PATH = ("storagepath", _("Storage Path")) STORAGE_PATH = ("storagepath", _("Storage Path"))
CUSTOM_FIELD = ("custom_field_%d", ("Custom Field"))
class DynamicDisplayFields:
CUSTOM_FIELD = ("custom_field_%d", CustomField)
name = models.CharField(_("name"), max_length=128) name = models.CharField(_("name"), max_length=128)
@ -601,11 +441,8 @@ class SavedView(ModelWithOwner):
blank=True, blank=True,
) )
display_fields = DynamicMultiSelectField( display_fields = models.JSONField(
max_length=128,
verbose_name=_("Document display fields"), verbose_name=_("Document display fields"),
choices=DisplayFields.choices,
dyanmic_choices=[DynamicDisplayFields.CUSTOM_FIELD],
null=True, null=True,
blank=True, blank=True,
) )
@ -947,6 +784,139 @@ class ShareLink(models.Model):
return f"Share Link for {self.document.title}" return f"Share Link for {self.document.title}"
class CustomField(models.Model):
"""
Defines the name and type of a custom field
"""
class FieldDataType(models.TextChoices):
STRING = ("string", _("String"))
URL = ("url", _("URL"))
DATE = ("date", _("Date"))
BOOL = ("boolean"), _("Boolean")
INT = ("integer", _("Integer"))
FLOAT = ("float", _("Float"))
MONETARY = ("monetary", _("Monetary"))
DOCUMENTLINK = ("documentlink", _("Document Link"))
created = models.DateTimeField(
_("created"),
default=timezone.now,
db_index=True,
editable=False,
)
name = models.CharField(max_length=128)
data_type = models.CharField(
_("data type"),
max_length=50,
choices=FieldDataType.choices,
editable=False,
)
class Meta:
ordering = ("created",)
verbose_name = _("custom field")
verbose_name_plural = _("custom fields")
constraints = [
models.UniqueConstraint(
fields=["name"],
name="%(app_label)s_%(class)s_unique_name",
),
]
def __str__(self) -> str:
return f"{self.name} : {self.data_type}"
class CustomFieldInstance(models.Model):
"""
A single instance of a field, attached to a CustomField for the name and type
and attached to a single Document to be metadata for it
"""
created = models.DateTimeField(
_("created"),
default=timezone.now,
db_index=True,
editable=False,
)
document = models.ForeignKey(
Document,
blank=False,
null=False,
on_delete=models.CASCADE,
related_name="custom_fields",
editable=False,
)
field = models.ForeignKey(
CustomField,
blank=False,
null=False,
on_delete=models.CASCADE,
related_name="fields",
editable=False,
)
# Actual data storage
value_text = models.CharField(max_length=128, null=True)
value_bool = models.BooleanField(null=True)
value_url = models.URLField(null=True)
value_date = models.DateField(null=True)
value_int = models.IntegerField(null=True)
value_float = models.FloatField(null=True)
value_monetary = models.CharField(null=True, max_length=128)
value_document_ids = models.JSONField(null=True)
class Meta:
ordering = ("created",)
verbose_name = _("custom field instance")
verbose_name_plural = _("custom field instances")
constraints = [
models.UniqueConstraint(
fields=["document", "field"],
name="%(app_label)s_%(class)s_unique_document_field",
),
]
def __str__(self) -> str:
return str(self.field.name) + f" : {self.value}"
@property
def value(self):
"""
Based on the data type, access the actual value the instance stores
A little shorthand/quick way to get what is actually here
"""
if self.field.data_type == CustomField.FieldDataType.STRING:
return self.value_text
elif self.field.data_type == CustomField.FieldDataType.URL:
return self.value_url
elif self.field.data_type == CustomField.FieldDataType.DATE:
return self.value_date
elif self.field.data_type == CustomField.FieldDataType.BOOL:
return self.value_bool
elif self.field.data_type == CustomField.FieldDataType.INT:
return self.value_int
elif self.field.data_type == CustomField.FieldDataType.FLOAT:
return self.value_float
elif self.field.data_type == CustomField.FieldDataType.MONETARY:
return self.value_monetary
elif self.field.data_type == CustomField.FieldDataType.DOCUMENTLINK:
return self.value_document_ids
raise NotImplementedError(self.field.data_type)
if settings.AUDIT_LOG_ENABLED: if settings.AUDIT_LOG_ENABLED:
auditlog.register( auditlog.register(
Document, Document,

View File

@ -802,48 +802,8 @@ class SavedViewFilterRuleSerializer(serializers.ModelSerializer):
fields = ["rule_type", "value"] fields = ["rule_type", "value"]
class DynamicOrderedMultipleChoiceField(fields.MultipleChoiceField):
"""
A MultipleChoiceField that allows for dynamic choices from a model
and preserves the order of the choices.
"""
def __init__(self, **kwargs):
self.dyanmic_choices = kwargs.pop("dyanmic_choices", None)
super().__init__(**kwargs)
def _get_choices(self):
return super()._get_choices()
def _set_choices(self, choices):
if self.dyanmic_choices is not None:
for key, Model in self.dyanmic_choices:
try:
for obj in Model.objects.all():
choices.append((key % obj.pk, obj.name))
except Exception:
pass
return super()._set_choices(choices)
choices = property(_get_choices, _set_choices)
def to_internal_value(self, data):
super().to_internal_value(data)
# MultipleChoiceField doesn't preserve order, so we use an array
return [fields.ChoiceField.to_internal_value(self, item) for item in data]
def to_representation(self, value):
# MultipleChoiceField doesn't preserve order, so we return as array to match the original order
return [self.choice_strings_to_values.get(str(item), item) for item in value]
class SavedViewSerializer(OwnedObjectSerializer): class SavedViewSerializer(OwnedObjectSerializer):
filter_rules = SavedViewFilterRuleSerializer(many=True) filter_rules = SavedViewFilterRuleSerializer(many=True)
display_fields = DynamicOrderedMultipleChoiceField(
choices=SavedView.DisplayFields.choices,
dyanmic_choices=[("custom_field_%d", CustomField)],
required=False,
)
class Meta: class Meta:
model = SavedView model = SavedView
@ -864,6 +824,22 @@ class SavedViewSerializer(OwnedObjectSerializer):
"set_permissions", "set_permissions",
] ]
def validate(self, attrs):
attrs = super().validate(attrs)
if "display_fields" in attrs:
for field in attrs["display_fields"]:
if re.sub(r"\d+", "%d", field) == SavedView.DisplayFields.CUSTOM_FIELD:
field_id = int(re.search(r"\d+", field)[0])
if not CustomField.objects.filter(id=field_id).exists():
raise serializers.ValidationError(
f"Invalid field: {field}",
)
elif field not in SavedView.DisplayFields.values:
raise serializers.ValidationError(
f"Invalid field: {field}",
)
return attrs
def update(self, instance, validated_data): def update(self, instance, validated_data):
if "filter_rules" in validated_data: if "filter_rules" in validated_data:
rules_data = validated_data.pop("filter_rules") rules_data = validated_data.pop("filter_rules")

View File

@ -1749,7 +1749,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
"display_fields": [ "display_fields": [
SavedView.DisplayFields.TITLE, SavedView.DisplayFields.TITLE,
SavedView.DisplayFields.CREATED, SavedView.DisplayFields.CREATED,
SavedView.DynamicDisplayFields.CUSTOM_FIELD[0] % custom_field.id, SavedView.DisplayFields.CUSTOM_FIELD % custom_field.id,
], ],
}, },
format="json", format="json",
@ -1762,7 +1762,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
[ [
str(SavedView.DisplayFields.TITLE), str(SavedView.DisplayFields.TITLE),
str(SavedView.DisplayFields.CREATED), str(SavedView.DisplayFields.CREATED),
SavedView.DynamicDisplayFields.CUSTOM_FIELD[0] % custom_field.id, SavedView.DisplayFields.CUSTOM_FIELD % custom_field.id,
], ],
) )
@ -1773,7 +1773,7 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
"display_fields": [ "display_fields": [
SavedView.DisplayFields.TITLE, SavedView.DisplayFields.TITLE,
SavedView.DisplayFields.CREATED, SavedView.DisplayFields.CREATED,
SavedView.DynamicDisplayFields.CUSTOM_FIELD[0] % 99, SavedView.DisplayFields.CUSTOM_FIELD % 99,
], ],
}, },
format="json", format="json",

View File

@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-19 01:14-0700\n" "POT-Creation-Date: 2024-04-20 22:11-0700\n"
"PO-Revision-Date: 2022-02-17 04:17\n" "PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: English\n" "Language-Team: English\n"
@ -21,31 +21,31 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "" msgstr ""
#: documents/models.py:36 documents/models.py:938 #: documents/models.py:36 documents/models.py:775
msgid "owner" msgid "owner"
msgstr "" msgstr ""
#: documents/models.py:53 documents/models.py:963 #: documents/models.py:53 documents/models.py:933
msgid "None" msgid "None"
msgstr "" msgstr ""
#: documents/models.py:54 documents/models.py:964 #: documents/models.py:54 documents/models.py:934
msgid "Any word" msgid "Any word"
msgstr "" msgstr ""
#: documents/models.py:55 documents/models.py:965 #: documents/models.py:55 documents/models.py:935
msgid "All words" msgid "All words"
msgstr "" msgstr ""
#: documents/models.py:56 documents/models.py:966 #: documents/models.py:56 documents/models.py:936
msgid "Exact match" msgid "Exact match"
msgstr "" msgstr ""
#: documents/models.py:57 documents/models.py:967 #: documents/models.py:57 documents/models.py:937
msgid "Regular expression" msgid "Regular expression"
msgstr "" msgstr ""
#: documents/models.py:58 documents/models.py:968 #: documents/models.py:58 documents/models.py:938
msgid "Fuzzy word" msgid "Fuzzy word"
msgstr "" msgstr ""
@ -53,20 +53,20 @@ msgstr ""
msgid "Automatic" msgid "Automatic"
msgstr "" msgstr ""
#: documents/models.py:62 documents/models.py:572 documents/models.py:1284 #: documents/models.py:62 documents/models.py:412 documents/models.py:1254
#: paperless_mail/models.py:18 paperless_mail/models.py:93 #: paperless_mail/models.py:18 paperless_mail/models.py:93
msgid "name" msgid "name"
msgstr "" msgstr ""
#: documents/models.py:64 documents/models.py:1024 #: documents/models.py:64 documents/models.py:994
msgid "match" msgid "match"
msgstr "" msgstr ""
#: documents/models.py:67 documents/models.py:1027 #: documents/models.py:67 documents/models.py:997
msgid "matching algorithm" msgid "matching algorithm"
msgstr "" msgstr ""
#: documents/models.py:72 documents/models.py:1032 #: documents/models.py:72 documents/models.py:1002
msgid "is insensitive" msgid "is insensitive"
msgstr "" msgstr ""
@ -132,7 +132,7 @@ msgstr ""
msgid "title" msgid "title"
msgstr "" msgstr ""
#: documents/models.py:171 documents/models.py:852 #: documents/models.py:171 documents/models.py:689
msgid "content" msgid "content"
msgstr "" msgstr ""
@ -162,8 +162,8 @@ msgstr ""
msgid "The checksum of the archived document." msgid "The checksum of the archived document."
msgstr "" msgstr ""
#: documents/models.py:205 documents/models.py:385 documents/models.py:412 #: documents/models.py:205 documents/models.py:385 documents/models.py:695
#: documents/models.py:449 documents/models.py:858 documents/models.py:896 #: documents/models.py:733 documents/models.py:803 documents/models.py:840
msgid "created" msgid "created"
msgstr "" msgstr ""
@ -211,7 +211,7 @@ msgstr ""
msgid "The position of this document in your physical document archive." msgid "The position of this document in your physical document archive."
msgstr "" msgstr ""
#: documents/models.py:279 documents/models.py:869 documents/models.py:923 #: documents/models.py:279 documents/models.py:706 documents/models.py:760
msgid "document" msgid "document"
msgstr "" msgstr ""
@ -259,636 +259,636 @@ msgstr ""
msgid "logs" msgid "logs"
msgstr "" msgstr ""
#: documents/models.py:402 #: documents/models.py:398
msgid "String"
msgstr ""
#: documents/models.py:403
msgid "URL"
msgstr ""
#: documents/models.py:404
msgid "Date"
msgstr ""
#: documents/models.py:405
msgid "Boolean"
msgstr ""
#: documents/models.py:406
msgid "Integer"
msgstr ""
#: documents/models.py:407
msgid "Float"
msgstr ""
#: documents/models.py:408
msgid "Monetary"
msgstr ""
#: documents/models.py:409
msgid "Document Link"
msgstr ""
#: documents/models.py:421
msgid "data type"
msgstr ""
#: documents/models.py:429
msgid "custom field"
msgstr ""
#: documents/models.py:430
msgid "custom fields"
msgstr ""
#: documents/models.py:492
msgid "custom field instance"
msgstr ""
#: documents/models.py:493
msgid "custom field instances"
msgstr ""
#: documents/models.py:556
msgid "Table" msgid "Table"
msgstr "" msgstr ""
#: documents/models.py:557 #: documents/models.py:399
msgid "Small Cards" msgid "Small Cards"
msgstr "" msgstr ""
#: documents/models.py:558 #: documents/models.py:400
msgid "Large Cards" msgid "Large Cards"
msgstr "" msgstr ""
#: documents/models.py:561 #: documents/models.py:403
msgid "Title" msgid "Title"
msgstr "" msgstr ""
#: documents/models.py:562 #: documents/models.py:404
msgid "Created" msgid "Created"
msgstr "" msgstr ""
#: documents/models.py:563 #: documents/models.py:405
msgid "Added" msgid "Added"
msgstr "" msgstr ""
#: documents/models.py:564 #: documents/models.py:406
msgid "Tags" msgid "Tags"
msgstr "" msgstr ""
#: documents/models.py:565 #: documents/models.py:407
msgid "Document Type" msgid "Document Type"
msgstr "" msgstr ""
#: documents/models.py:566 #: documents/models.py:408
msgid "Correspondent" msgid "Correspondent"
msgstr "" msgstr ""
#: documents/models.py:567 #: documents/models.py:409
msgid "Storage Path" msgid "Storage Path"
msgstr "" msgstr ""
#: documents/models.py:575 #: documents/models.py:415
msgid "show on dashboard" msgid "show on dashboard"
msgstr "" msgstr ""
#: documents/models.py:578 #: documents/models.py:418
msgid "show in sidebar" msgid "show in sidebar"
msgstr "" msgstr ""
#: documents/models.py:582 #: documents/models.py:422
msgid "sort field" msgid "sort field"
msgstr "" msgstr ""
#: documents/models.py:587 #: documents/models.py:427
msgid "sort reverse" msgid "sort reverse"
msgstr "" msgstr ""
#: documents/models.py:590 #: documents/models.py:430
msgid "View page size" msgid "View page size"
msgstr "" msgstr ""
#: documents/models.py:598 #: documents/models.py:438
msgid "View display mode" msgid "View display mode"
msgstr "" msgstr ""
#: documents/models.py:606 #: documents/models.py:445
msgid "Document display fields" msgid "Document display fields"
msgstr "" msgstr ""
#: documents/models.py:615 documents/models.py:668 #: documents/models.py:452 documents/models.py:505
msgid "saved view" msgid "saved view"
msgstr "" msgstr ""
#: documents/models.py:616 #: documents/models.py:453
msgid "saved views" msgid "saved views"
msgstr "" msgstr ""
#: documents/models.py:624 #: documents/models.py:461
msgid "title contains" msgid "title contains"
msgstr "" msgstr ""
#: documents/models.py:625 #: documents/models.py:462
msgid "content contains" msgid "content contains"
msgstr "" msgstr ""
#: documents/models.py:626 #: documents/models.py:463
msgid "ASN is" msgid "ASN is"
msgstr "" msgstr ""
#: documents/models.py:627 #: documents/models.py:464
msgid "correspondent is" msgid "correspondent is"
msgstr "" msgstr ""
#: documents/models.py:628 #: documents/models.py:465
msgid "document type is" msgid "document type is"
msgstr "" msgstr ""
#: documents/models.py:629 #: documents/models.py:466
msgid "is in inbox" msgid "is in inbox"
msgstr "" msgstr ""
#: documents/models.py:630 #: documents/models.py:467
msgid "has tag" msgid "has tag"
msgstr "" msgstr ""
#: documents/models.py:631 #: documents/models.py:468
msgid "has any tag" msgid "has any tag"
msgstr "" msgstr ""
#: documents/models.py:632 #: documents/models.py:469
msgid "created before" msgid "created before"
msgstr "" msgstr ""
#: documents/models.py:633 #: documents/models.py:470
msgid "created after" msgid "created after"
msgstr "" msgstr ""
#: documents/models.py:634 #: documents/models.py:471
msgid "created year is" msgid "created year is"
msgstr "" msgstr ""
#: documents/models.py:635 #: documents/models.py:472
msgid "created month is" msgid "created month is"
msgstr "" msgstr ""
#: documents/models.py:636 #: documents/models.py:473
msgid "created day is" msgid "created day is"
msgstr "" msgstr ""
#: documents/models.py:637 #: documents/models.py:474
msgid "added before" msgid "added before"
msgstr "" msgstr ""
#: documents/models.py:638 #: documents/models.py:475
msgid "added after" msgid "added after"
msgstr "" msgstr ""
#: documents/models.py:639 #: documents/models.py:476
msgid "modified before" msgid "modified before"
msgstr "" msgstr ""
#: documents/models.py:640 #: documents/models.py:477
msgid "modified after" msgid "modified after"
msgstr "" msgstr ""
#: documents/models.py:641 #: documents/models.py:478
msgid "does not have tag" msgid "does not have tag"
msgstr "" msgstr ""
#: documents/models.py:642 #: documents/models.py:479
msgid "does not have ASN" msgid "does not have ASN"
msgstr "" msgstr ""
#: documents/models.py:643 #: documents/models.py:480
msgid "title or content contains" msgid "title or content contains"
msgstr "" msgstr ""
#: documents/models.py:644 #: documents/models.py:481
msgid "fulltext query" msgid "fulltext query"
msgstr "" msgstr ""
#: documents/models.py:645 #: documents/models.py:482
msgid "more like this" msgid "more like this"
msgstr "" msgstr ""
#: documents/models.py:646 #: documents/models.py:483
msgid "has tags in" msgid "has tags in"
msgstr "" msgstr ""
#: documents/models.py:647 #: documents/models.py:484
msgid "ASN greater than" msgid "ASN greater than"
msgstr "" msgstr ""
#: documents/models.py:648 #: documents/models.py:485
msgid "ASN less than" msgid "ASN less than"
msgstr "" msgstr ""
#: documents/models.py:649 #: documents/models.py:486
msgid "storage path is" msgid "storage path is"
msgstr "" msgstr ""
#: documents/models.py:650 #: documents/models.py:487
msgid "has correspondent in" msgid "has correspondent in"
msgstr "" msgstr ""
#: documents/models.py:651 #: documents/models.py:488
msgid "does not have correspondent in" msgid "does not have correspondent in"
msgstr "" msgstr ""
#: documents/models.py:652 #: documents/models.py:489
msgid "has document type in" msgid "has document type in"
msgstr "" msgstr ""
#: documents/models.py:653 #: documents/models.py:490
msgid "does not have document type in" msgid "does not have document type in"
msgstr "" msgstr ""
#: documents/models.py:654 #: documents/models.py:491
msgid "has storage path in" msgid "has storage path in"
msgstr "" msgstr ""
#: documents/models.py:655 #: documents/models.py:492
msgid "does not have storage path in" msgid "does not have storage path in"
msgstr "" msgstr ""
#: documents/models.py:656 #: documents/models.py:493
msgid "owner is" msgid "owner is"
msgstr "" msgstr ""
#: documents/models.py:657 #: documents/models.py:494
msgid "has owner in" msgid "has owner in"
msgstr "" msgstr ""
#: documents/models.py:658 #: documents/models.py:495
msgid "does not have owner" msgid "does not have owner"
msgstr "" msgstr ""
#: documents/models.py:659 #: documents/models.py:496
msgid "does not have owner in" msgid "does not have owner in"
msgstr "" msgstr ""
#: documents/models.py:660 #: documents/models.py:497
msgid "has custom field value" msgid "has custom field value"
msgstr "" msgstr ""
#: documents/models.py:661 #: documents/models.py:498
msgid "is shared by me" msgid "is shared by me"
msgstr "" msgstr ""
#: documents/models.py:671 #: documents/models.py:508
msgid "rule type" msgid "rule type"
msgstr "" msgstr ""
#: documents/models.py:673 #: documents/models.py:510
msgid "value" msgid "value"
msgstr "" msgstr ""
#: documents/models.py:676 #: documents/models.py:513
msgid "filter rule" msgid "filter rule"
msgstr "" msgstr ""
#: documents/models.py:677 #: documents/models.py:514
msgid "filter rules" msgid "filter rules"
msgstr "" msgstr ""
#: documents/models.py:788 #: documents/models.py:625
msgid "Task ID" msgid "Task ID"
msgstr "" msgstr ""
#: documents/models.py:789 #: documents/models.py:626
msgid "Celery ID for the Task that was run" msgid "Celery ID for the Task that was run"
msgstr "" msgstr ""
#: documents/models.py:794 #: documents/models.py:631
msgid "Acknowledged" msgid "Acknowledged"
msgstr "" msgstr ""
#: documents/models.py:795 #: documents/models.py:632
msgid "If the task is acknowledged via the frontend or API" msgid "If the task is acknowledged via the frontend or API"
msgstr "" msgstr ""
#: documents/models.py:801 #: documents/models.py:638
msgid "Task Filename" msgid "Task Filename"
msgstr "" msgstr ""
#: documents/models.py:802 #: documents/models.py:639
msgid "Name of the file which the Task was run for" msgid "Name of the file which the Task was run for"
msgstr "" msgstr ""
#: documents/models.py:808 #: documents/models.py:645
msgid "Task Name" msgid "Task Name"
msgstr "" msgstr ""
#: documents/models.py:809 #: documents/models.py:646
msgid "Name of the Task which was run" msgid "Name of the Task which was run"
msgstr "" msgstr ""
#: documents/models.py:816 #: documents/models.py:653
msgid "Task State" msgid "Task State"
msgstr "" msgstr ""
#: documents/models.py:817 #: documents/models.py:654
msgid "Current state of the task being run" msgid "Current state of the task being run"
msgstr "" msgstr ""
#: documents/models.py:822 #: documents/models.py:659
msgid "Created DateTime" msgid "Created DateTime"
msgstr "" msgstr ""
#: documents/models.py:823 #: documents/models.py:660
msgid "Datetime field when the task result was created in UTC" msgid "Datetime field when the task result was created in UTC"
msgstr "" msgstr ""
#: documents/models.py:828 #: documents/models.py:665
msgid "Started DateTime" msgid "Started DateTime"
msgstr "" msgstr ""
#: documents/models.py:829 #: documents/models.py:666
msgid "Datetime field when the task was started in UTC" msgid "Datetime field when the task was started in UTC"
msgstr "" msgstr ""
#: documents/models.py:834 #: documents/models.py:671
msgid "Completed DateTime" msgid "Completed DateTime"
msgstr "" msgstr ""
#: documents/models.py:835 #: documents/models.py:672
msgid "Datetime field when the task was completed in UTC" msgid "Datetime field when the task was completed in UTC"
msgstr "" msgstr ""
#: documents/models.py:840 #: documents/models.py:677
msgid "Result Data" msgid "Result Data"
msgstr "" msgstr ""
#: documents/models.py:842 #: documents/models.py:679
msgid "The data returned by the task" msgid "The data returned by the task"
msgstr "" msgstr ""
#: documents/models.py:854 #: documents/models.py:691
msgid "Note for the document" msgid "Note for the document"
msgstr "" msgstr ""
#: documents/models.py:878 #: documents/models.py:715
msgid "user" msgid "user"
msgstr "" msgstr ""
#: documents/models.py:883 #: documents/models.py:720
msgid "note" msgid "note"
msgstr "" msgstr ""
#: documents/models.py:884 #: documents/models.py:721
msgid "notes" msgid "notes"
msgstr "" msgstr ""
#: documents/models.py:892 #: documents/models.py:729
msgid "Archive" msgid "Archive"
msgstr "" msgstr ""
#: documents/models.py:893 #: documents/models.py:730
msgid "Original" msgid "Original"
msgstr "" msgstr ""
#: documents/models.py:904 #: documents/models.py:741
msgid "expiration" msgid "expiration"
msgstr "" msgstr ""
#: documents/models.py:911 #: documents/models.py:748
msgid "slug" msgid "slug"
msgstr "" msgstr ""
#: documents/models.py:943 #: documents/models.py:780
msgid "share link" msgid "share link"
msgstr "" msgstr ""
#: documents/models.py:944 #: documents/models.py:781
msgid "share links" msgid "share links"
msgstr "" msgstr ""
#: documents/models.py:971 #: documents/models.py:793
msgid "String"
msgstr ""
#: documents/models.py:794
msgid "URL"
msgstr ""
#: documents/models.py:795
msgid "Date"
msgstr ""
#: documents/models.py:796
msgid "Boolean"
msgstr ""
#: documents/models.py:797
msgid "Integer"
msgstr ""
#: documents/models.py:798
msgid "Float"
msgstr ""
#: documents/models.py:799
msgid "Monetary"
msgstr ""
#: documents/models.py:800
msgid "Document Link"
msgstr ""
#: documents/models.py:812
msgid "data type"
msgstr ""
#: documents/models.py:820
msgid "custom field"
msgstr ""
#: documents/models.py:821
msgid "custom fields"
msgstr ""
#: documents/models.py:883
msgid "custom field instance"
msgstr ""
#: documents/models.py:884
msgid "custom field instances"
msgstr ""
#: documents/models.py:941
msgid "Consumption Started" msgid "Consumption Started"
msgstr "" msgstr ""
#: documents/models.py:972 #: documents/models.py:942
msgid "Document Added" msgid "Document Added"
msgstr "" msgstr ""
#: documents/models.py:973 #: documents/models.py:943
msgid "Document Updated" msgid "Document Updated"
msgstr "" msgstr ""
#: documents/models.py:976 #: documents/models.py:946
msgid "Consume Folder" msgid "Consume Folder"
msgstr "" msgstr ""
#: documents/models.py:977 #: documents/models.py:947
msgid "Api Upload" msgid "Api Upload"
msgstr "" msgstr ""
#: documents/models.py:978 #: documents/models.py:948
msgid "Mail Fetch" msgid "Mail Fetch"
msgstr "" msgstr ""
#: documents/models.py:981 #: documents/models.py:951
msgid "Workflow Trigger Type" msgid "Workflow Trigger Type"
msgstr "" msgstr ""
#: documents/models.py:993 #: documents/models.py:963
msgid "filter path" msgid "filter path"
msgstr "" msgstr ""
#: documents/models.py:998 #: documents/models.py:968
msgid "" msgid ""
"Only consume documents with a path that matches this if specified. Wildcards " "Only consume documents with a path that matches this if specified. Wildcards "
"specified as * are allowed. Case insensitive." "specified as * are allowed. Case insensitive."
msgstr "" msgstr ""
#: documents/models.py:1005 #: documents/models.py:975
msgid "filter filename" msgid "filter filename"
msgstr "" msgstr ""
#: documents/models.py:1010 paperless_mail/models.py:148 #: documents/models.py:980 paperless_mail/models.py:148
msgid "" msgid ""
"Only consume documents which entirely match this filename if specified. " "Only consume documents which entirely match this filename if specified. "
"Wildcards such as *.pdf or *invoice* are allowed. Case insensitive." "Wildcards such as *.pdf or *invoice* are allowed. Case insensitive."
msgstr "" msgstr ""
#: documents/models.py:1021 #: documents/models.py:991
msgid "filter documents from this mail rule" msgid "filter documents from this mail rule"
msgstr "" msgstr ""
#: documents/models.py:1037 #: documents/models.py:1007
msgid "has these tag(s)" msgid "has these tag(s)"
msgstr "" msgstr ""
#: documents/models.py:1045 #: documents/models.py:1015
msgid "has this document type" msgid "has this document type"
msgstr "" msgstr ""
#: documents/models.py:1053 #: documents/models.py:1023
msgid "has this correspondent" msgid "has this correspondent"
msgstr "" msgstr ""
#: documents/models.py:1057 #: documents/models.py:1027
msgid "workflow trigger" msgid "workflow trigger"
msgstr "" msgstr ""
#: documents/models.py:1058 #: documents/models.py:1028
msgid "workflow triggers" msgid "workflow triggers"
msgstr "" msgstr ""
#: documents/models.py:1068 #: documents/models.py:1038
msgid "Assignment" msgid "Assignment"
msgstr "" msgstr ""
#: documents/models.py:1072 #: documents/models.py:1042
msgid "Removal" msgid "Removal"
msgstr "" msgstr ""
#: documents/models.py:1076 #: documents/models.py:1046
msgid "Workflow Action Type" msgid "Workflow Action Type"
msgstr "" msgstr ""
#: documents/models.py:1082 #: documents/models.py:1052
msgid "assign title" msgid "assign title"
msgstr "" msgstr ""
#: documents/models.py:1087 #: documents/models.py:1057
msgid "" msgid ""
"Assign a document title, can include some placeholders, see documentation." "Assign a document title, can include some placeholders, see documentation."
msgstr "" msgstr ""
#: documents/models.py:1096 paperless_mail/models.py:216 #: documents/models.py:1066 paperless_mail/models.py:216
msgid "assign this tag" msgid "assign this tag"
msgstr "" msgstr ""
#: documents/models.py:1105 paperless_mail/models.py:224 #: documents/models.py:1075 paperless_mail/models.py:224
msgid "assign this document type" msgid "assign this document type"
msgstr "" msgstr ""
#: documents/models.py:1114 paperless_mail/models.py:238 #: documents/models.py:1084 paperless_mail/models.py:238
msgid "assign this correspondent" msgid "assign this correspondent"
msgstr "" msgstr ""
#: documents/models.py:1123 #: documents/models.py:1093
msgid "assign this storage path" msgid "assign this storage path"
msgstr "" msgstr ""
#: documents/models.py:1132 #: documents/models.py:1102
msgid "assign this owner" msgid "assign this owner"
msgstr "" msgstr ""
#: documents/models.py:1139 #: documents/models.py:1109
msgid "grant view permissions to these users" msgid "grant view permissions to these users"
msgstr "" msgstr ""
#: documents/models.py:1146 #: documents/models.py:1116
msgid "grant view permissions to these groups" msgid "grant view permissions to these groups"
msgstr "" msgstr ""
#: documents/models.py:1153 #: documents/models.py:1123
msgid "grant change permissions to these users" msgid "grant change permissions to these users"
msgstr "" msgstr ""
#: documents/models.py:1160 #: documents/models.py:1130
msgid "grant change permissions to these groups" msgid "grant change permissions to these groups"
msgstr "" msgstr ""
#: documents/models.py:1167 #: documents/models.py:1137
msgid "assign these custom fields" msgid "assign these custom fields"
msgstr "" msgstr ""
#: documents/models.py:1174 #: documents/models.py:1144
msgid "remove these tag(s)" msgid "remove these tag(s)"
msgstr "" msgstr ""
#: documents/models.py:1179 #: documents/models.py:1149
msgid "remove all tags" msgid "remove all tags"
msgstr "" msgstr ""
#: documents/models.py:1186 #: documents/models.py:1156
msgid "remove these document type(s)" msgid "remove these document type(s)"
msgstr "" msgstr ""
#: documents/models.py:1191 #: documents/models.py:1161
msgid "remove all document types" msgid "remove all document types"
msgstr "" msgstr ""
#: documents/models.py:1198 #: documents/models.py:1168
msgid "remove these correspondent(s)" msgid "remove these correspondent(s)"
msgstr "" msgstr ""
#: documents/models.py:1203 #: documents/models.py:1173
msgid "remove all correspondents" msgid "remove all correspondents"
msgstr "" msgstr ""
#: documents/models.py:1210 #: documents/models.py:1180
msgid "remove these storage path(s)" msgid "remove these storage path(s)"
msgstr "" msgstr ""
#: documents/models.py:1215 #: documents/models.py:1185
msgid "remove all storage paths" msgid "remove all storage paths"
msgstr "" msgstr ""
#: documents/models.py:1222 #: documents/models.py:1192
msgid "remove these owner(s)" msgid "remove these owner(s)"
msgstr "" msgstr ""
#: documents/models.py:1227 #: documents/models.py:1197
msgid "remove all owners" msgid "remove all owners"
msgstr "" msgstr ""
#: documents/models.py:1234 #: documents/models.py:1204
msgid "remove view permissions for these users" msgid "remove view permissions for these users"
msgstr "" msgstr ""
#: documents/models.py:1241 #: documents/models.py:1211
msgid "remove view permissions for these groups" msgid "remove view permissions for these groups"
msgstr "" msgstr ""
#: documents/models.py:1248 #: documents/models.py:1218
msgid "remove change permissions for these users" msgid "remove change permissions for these users"
msgstr "" msgstr ""
#: documents/models.py:1255 #: documents/models.py:1225
msgid "remove change permissions for these groups" msgid "remove change permissions for these groups"
msgstr "" msgstr ""
#: documents/models.py:1260 #: documents/models.py:1230
msgid "remove all permissions" msgid "remove all permissions"
msgstr "" msgstr ""
#: documents/models.py:1267 #: documents/models.py:1237
msgid "remove these custom fields" msgid "remove these custom fields"
msgstr "" msgstr ""
#: documents/models.py:1272 #: documents/models.py:1242
msgid "remove all custom fields" msgid "remove all custom fields"
msgstr "" msgstr ""
#: documents/models.py:1276 #: documents/models.py:1246
msgid "workflow action" msgid "workflow action"
msgstr "" msgstr ""
#: documents/models.py:1277 #: documents/models.py:1247
msgid "workflow actions" msgid "workflow actions"
msgstr "" msgstr ""
#: documents/models.py:1286 paperless_mail/models.py:95 #: documents/models.py:1256 paperless_mail/models.py:95
msgid "order" msgid "order"
msgstr "" msgstr ""
#: documents/models.py:1292 #: documents/models.py:1262
msgid "triggers" msgid "triggers"
msgstr "" msgstr ""
#: documents/models.py:1299 #: documents/models.py:1269
msgid "actions" msgid "actions"
msgstr "" msgstr ""
#: documents/models.py:1302 #: documents/models.py:1272
msgid "enabled" msgid "enabled"
msgstr "" msgstr ""
@ -901,12 +901,12 @@ msgstr ""
msgid "Invalid color." msgid "Invalid color."
msgstr "" msgstr ""
#: documents/serialisers.py:1186 #: documents/serialisers.py:1162
#, python-format #, python-format
msgid "File type %(type)s not supported" msgid "File type %(type)s not supported"
msgstr "" msgstr ""
#: documents/serialisers.py:1295 #: documents/serialisers.py:1271
msgid "Invalid variable detected." msgid "Invalid variable detected."
msgstr "" msgstr ""