mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-10-03 23:00:31 +00:00
documents/serialisers.py had grown into a single file with every serialiser for the app, making it hard to navigate and putting unrelated domains behind one import path. Moves serialisers into documents/serialisers/ as a package, grouping them into logical per-domain files instead. paperless_mail/serialisers.py is updated to import from the new submodules.
235 lines
8.7 KiB
Python
235 lines
8.7 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import re
|
|
|
|
from django.conf import settings
|
|
from rest_framework import serializers
|
|
|
|
from documents.models import CustomField
|
|
from documents.models import SavedView
|
|
from documents.models import SavedViewFilterRule
|
|
from documents.models import UiSettings
|
|
|
|
from .base import OwnedObjectSerializer
|
|
|
|
logger = logging.getLogger("paperless.serializers")
|
|
|
|
|
|
class SavedViewFilterRuleSerializer(serializers.ModelSerializer[SavedViewFilterRule]):
|
|
class Meta:
|
|
model = SavedViewFilterRule
|
|
fields = ["rule_type", "value"]
|
|
|
|
|
|
class SavedViewSerializer(OwnedObjectSerializer):
|
|
filter_rules = SavedViewFilterRuleSerializer(many=True)
|
|
|
|
class Meta:
|
|
model = SavedView
|
|
fields = [
|
|
"id",
|
|
"name",
|
|
"icon",
|
|
"sort_field",
|
|
"sort_reverse",
|
|
"filter_rules",
|
|
"page_size",
|
|
"display_mode",
|
|
"display_fields",
|
|
"owner",
|
|
"permissions",
|
|
"user_can_change",
|
|
"set_permissions",
|
|
]
|
|
|
|
def _get_api_version(self) -> int:
|
|
request = self.context.get("request")
|
|
return int(
|
|
request.version if request else settings.REST_FRAMEWORK["DEFAULT_VERSION"],
|
|
)
|
|
|
|
def _update_legacy_visibility_preferences(
|
|
self,
|
|
saved_view_id: int,
|
|
*,
|
|
show_on_dashboard: bool | None,
|
|
show_in_sidebar: bool | None,
|
|
) -> UiSettings | None:
|
|
if show_on_dashboard is None and show_in_sidebar is None:
|
|
return None
|
|
|
|
request = self.context.get("request")
|
|
user = request.user if request else self.user
|
|
if user is None:
|
|
return None
|
|
|
|
ui_settings, _ = UiSettings.objects.get_or_create(
|
|
user=user,
|
|
defaults={"settings": {}},
|
|
)
|
|
current_settings = (
|
|
ui_settings.settings if isinstance(ui_settings.settings, dict) else {}
|
|
)
|
|
current_settings = dict(current_settings)
|
|
|
|
saved_views_settings = current_settings.get("saved_views")
|
|
if isinstance(saved_views_settings, dict):
|
|
saved_views_settings = dict(saved_views_settings)
|
|
else:
|
|
saved_views_settings = {}
|
|
|
|
dashboard_ids = {
|
|
int(raw_id)
|
|
for raw_id in saved_views_settings.get("dashboard_views_visible_ids", [])
|
|
if str(raw_id).isdigit()
|
|
}
|
|
sidebar_ids = {
|
|
int(raw_id)
|
|
for raw_id in saved_views_settings.get("sidebar_views_visible_ids", [])
|
|
if str(raw_id).isdigit()
|
|
}
|
|
|
|
if show_on_dashboard is not None:
|
|
if show_on_dashboard:
|
|
dashboard_ids.add(saved_view_id)
|
|
else:
|
|
dashboard_ids.discard(saved_view_id)
|
|
if show_in_sidebar is not None:
|
|
if show_in_sidebar:
|
|
sidebar_ids.add(saved_view_id)
|
|
else:
|
|
sidebar_ids.discard(saved_view_id)
|
|
|
|
saved_views_settings["dashboard_views_visible_ids"] = sorted(dashboard_ids)
|
|
saved_views_settings["sidebar_views_visible_ids"] = sorted(sidebar_ids)
|
|
current_settings["saved_views"] = saved_views_settings
|
|
ui_settings.settings = current_settings
|
|
ui_settings.save(update_fields=["settings"])
|
|
return ui_settings
|
|
|
|
def to_representation(self, instance):
|
|
# TODO: remove this and related backwards compatibility code when API v9 is dropped
|
|
ret = super().to_representation(instance)
|
|
request = self.context.get("request")
|
|
api_version = self._get_api_version()
|
|
|
|
if api_version < 10:
|
|
dashboard_ids = set()
|
|
sidebar_ids = set()
|
|
user = request.user if request else None
|
|
if user is not None and hasattr(user, "ui_settings"):
|
|
ui_settings = user.ui_settings.settings or None
|
|
saved_views = None
|
|
if isinstance(ui_settings, dict):
|
|
saved_views = ui_settings.get("saved_views", {})
|
|
if isinstance(saved_views, dict):
|
|
dashboard_ids = set(
|
|
saved_views.get("dashboard_views_visible_ids", []),
|
|
)
|
|
sidebar_ids = set(
|
|
saved_views.get("sidebar_views_visible_ids", []),
|
|
)
|
|
ret["show_on_dashboard"] = instance.id in dashboard_ids
|
|
ret["show_in_sidebar"] = instance.id in sidebar_ids
|
|
|
|
return ret
|
|
|
|
def to_internal_value(self, data):
|
|
# TODO: remove this and related backwards compatibility code when API v9 is dropped
|
|
api_version = self._get_api_version()
|
|
if api_version >= 10:
|
|
return super().to_internal_value(data)
|
|
|
|
normalized_data = data.copy()
|
|
legacy_visibility_fields = {}
|
|
boolean_field = serializers.BooleanField()
|
|
|
|
for field_name in ("show_on_dashboard", "show_in_sidebar"):
|
|
if field_name in normalized_data:
|
|
try:
|
|
legacy_visibility_fields[field_name] = (
|
|
boolean_field.to_internal_value(
|
|
normalized_data.get(field_name),
|
|
)
|
|
)
|
|
except serializers.ValidationError as exc:
|
|
raise serializers.ValidationError({field_name: exc.detail})
|
|
del normalized_data[field_name]
|
|
|
|
ret = super().to_internal_value(normalized_data)
|
|
ret.update(legacy_visibility_fields)
|
|
return ret
|
|
|
|
def validate(self, attrs):
|
|
attrs = super().validate(attrs)
|
|
if "display_fields" in attrs and attrs["display_fields"] is not None:
|
|
for field in attrs["display_fields"]:
|
|
if (
|
|
SavedView.DisplayFields.CUSTOM_FIELD[:-2] in field
|
|
): # i.e. check for 'custom_field_' prefix
|
|
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):
|
|
request = self.context.get("request")
|
|
show_on_dashboard = validated_data.pop("show_on_dashboard", None)
|
|
show_in_sidebar = validated_data.pop("show_in_sidebar", None)
|
|
if "filter_rules" in validated_data:
|
|
rules_data = validated_data.pop("filter_rules")
|
|
else:
|
|
rules_data = None
|
|
if "user" in validated_data:
|
|
# backwards compatibility
|
|
validated_data["owner"] = validated_data.pop("user")
|
|
if (
|
|
"display_fields" in validated_data
|
|
and isinstance(
|
|
validated_data["display_fields"],
|
|
list,
|
|
)
|
|
and len(validated_data["display_fields"]) == 0
|
|
):
|
|
validated_data["display_fields"] = None
|
|
instance = super().update(instance, validated_data)
|
|
if rules_data is not None:
|
|
SavedViewFilterRule.objects.filter(saved_view=instance).delete()
|
|
for rule_data in rules_data:
|
|
SavedViewFilterRule.objects.create(saved_view=instance, **rule_data)
|
|
ui_settings = self._update_legacy_visibility_preferences(
|
|
instance.id,
|
|
show_on_dashboard=show_on_dashboard,
|
|
show_in_sidebar=show_in_sidebar,
|
|
)
|
|
if request is not None and ui_settings is not None:
|
|
request.user.ui_settings = ui_settings
|
|
return instance
|
|
|
|
def create(self, validated_data):
|
|
request = self.context.get("request")
|
|
show_on_dashboard = validated_data.pop("show_on_dashboard", None)
|
|
show_in_sidebar = validated_data.pop("show_in_sidebar", None)
|
|
rules_data = validated_data.pop("filter_rules")
|
|
if "user" in validated_data:
|
|
# backwards compatibility
|
|
validated_data["owner"] = validated_data.pop("user")
|
|
saved_view = super().create(validated_data)
|
|
for rule_data in rules_data:
|
|
SavedViewFilterRule.objects.create(saved_view=saved_view, **rule_data)
|
|
ui_settings = self._update_legacy_visibility_preferences(
|
|
saved_view.id,
|
|
show_on_dashboard=show_on_dashboard,
|
|
show_in_sidebar=show_in_sidebar,
|
|
)
|
|
if request is not None and ui_settings is not None:
|
|
request.user.ui_settings = ui_settings
|
|
return saved_view
|