Skip to content

Commit cf49931

Browse files
Complete Vocabularies async port.
- Moved to `vocabularies_async` and `types` to be consistent with `dev_async`. - Set `internal_resource` to the old resource. - Added `ResourceConfig`. - Added module to `default_settings`. - Addressed feedbacks.
1 parent 27e72bf commit cf49931

File tree

7 files changed

+128
-61
lines changed

7 files changed

+128
-61
lines changed

superdesk/default_settings.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ def local_to_utc_hour(hour):
418418
#:
419419
#: ..versionadded: 3.0.0
420420
#:
421-
MODULES = ["superdesk.users", "apps.desks_async"]
421+
MODULES = ["superdesk.users", "apps.desks_async", "superdesk.vocabularies_async"]
422422

423423
ASYNC_AUTH_CLASS = "superdesk.core.auth.token_auth:TokenAuthorization"
424424

superdesk/types/__init__.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from typing import TypedDict, Dict, Any, List
1+
from typing import Any, Dict, List, TypedDict
2+
23
from .desks import DesksResourceModel
34
from .users import UsersResourceModel
5+
from .vocabularies import VocabulariesResourceModel
46

5-
__all__ = ["UsersResourceModel", "DesksResourceModel"]
7+
__all__ = ["UsersResourceModel", "DesksResourceModel", "VocabulariesResourceModel"]
68

79

810
class WebsocketMessageFilterConditions(TypedDict, total=False):

superdesk/types/vocabularies.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# -*- coding: utf-8; -*-
2+
#
3+
# This file is part of Superdesk.
4+
#
5+
# Copyright 2013, 2025 Sourcefabric z.u. and contributors.
6+
#
7+
# For the full copyright and license information, please see the
8+
# AUTHORS and LICENSE files distributed with this source code, or
9+
# at https://www.sourcefabric.org/superdesk/license
10+
11+
12+
from enum import Enum, unique
13+
import logging
14+
from typing import Annotated, Any
15+
16+
from pydantic import BaseModel, ConfigDict, Field
17+
from quart_babel import gettext as _
18+
19+
from superdesk.core.resources import ResourceModel
20+
from superdesk.core.resources.model import Dataclass, ResourceModel
21+
from superdesk.core.resources.validators import validate_maxlength
22+
23+
logger = logging.getLogger(__name__)
24+
25+
26+
class Tag(Dataclass):
27+
text: str
28+
29+
30+
class Item(BaseModel):
31+
name: str
32+
qcode: str
33+
is_active: bool = True
34+
35+
36+
class DateShortcut(Dataclass):
37+
value: int
38+
term: str
39+
label: str
40+
41+
42+
class CustomFieldConfig(Dataclass):
43+
increment_steps: list[int]
44+
initial_offset_minutes: int
45+
46+
47+
@unique
48+
class CVAccessType(str, Enum):
49+
MANAGEABLE = "manageable"
50+
UNMANAGEABLE = "unmanageable"
51+
52+
53+
@unique
54+
class SelectionType(str, Enum):
55+
SINGLE_SELECTION = "single selection"
56+
MULTI_SELECTION = "multi selection"
57+
DO_NOT_SHOW = "do not show"
58+
59+
60+
class VocabulariesResourceModel(ResourceModel):
61+
display_name: str
62+
description: str | None = None
63+
helper_text: Annotated[str | None, validate_maxlength(120)] = None
64+
tags: list[Tag] | None = None
65+
popup_width: int | None = None
66+
management_type: Annotated[CVAccessType, Field(alias="type")]
67+
items: list[Item]
68+
selection_type: SelectionType | None = None
69+
read_only: bool | None = None
70+
schema_field: str | None = None
71+
dependent: bool = False
72+
service: dict[str, int] = Field(default_factory=dict)
73+
priority: int = 0
74+
unique_field: str | None = None
75+
schema_: dict[str, dict]
76+
field_type_: str | None = None
77+
field_options_: dict[str, Any] = Field(default_factory=dict)
78+
init_version: int = 0
79+
preffered_items: bool = False
80+
disable_entire_category_selection: bool = False
81+
date_shortcuts: list[DateShortcut] | None = None
82+
custom_field_type: str | None = None
83+
custom_field_config: CustomFieldConfig | None = None
84+
translations: dict[str, dict[str, Any]] = Field(default_factory=dict)

superdesk/vocabularies/vocabularies.py

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151

5252

5353
class VocabulariesResource(Resource):
54+
internal_resource = True
5455
schema = {
5556
"_id": {
5657
"type": "string",
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from superdesk.core.module import Module
2+
from .service import VocabulariesService
3+
from .module import desks_resource_config
4+
5+
__all__ = ["VocabulariesService"]
6+
7+
module = Module(name="apps.desks_async", resources=[desks_resource_config])
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# -*- coding: utf-8; -*-
2+
#
3+
# This file is part of Superdesk.
4+
#
5+
# Copyright 2013, 2025 Sourcefabric z.u. and contributors.
6+
#
7+
# For the full copyright and license information, please see the
8+
# AUTHORS and LICENSE files distributed with this source code, or
9+
# at https://www.sourcefabric.org/superdesk/license
10+
11+
from superdesk.core.resources import ResourceConfig, MongoResourceConfig, MongoIndexOptions
12+
from superdesk.types import VocabulariesResourceModel
13+
from .service import VocabulariesService
14+
15+
16+
desks_resource_config = ResourceConfig(
17+
name="vocabularies",
18+
data_class=VocabulariesResourceModel,
19+
service=VocabulariesService,
20+
mongo=MongoResourceConfig(
21+
indexes=[
22+
MongoIndexOptions(
23+
name="field_type",
24+
keys=[("field_type", 1)],
25+
),
26+
],
27+
),
28+
)

superdesk/vocabularies/async_vocabularies.py superdesk/vocabularies_async/service.py

+3-58
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,24 @@
1111

1212
import json
1313
import logging
14-
from typing import Annotated, Any, Dict, List, Optional
14+
from typing import Any, Dict, List, Optional
1515
from typing import List
1616

1717
from eve.methods.common import serialize_value
18-
from pydantic import BaseModel, ConfigDict, Field
1918
from quart_babel import gettext as _, lazy_gettext
2019

2120
from superdesk import privilege
2221
from superdesk.cache import cache
23-
from superdesk.core.resources import ResourceModel, dataclass
22+
from superdesk.core.resources import ResourceModel
2423
from superdesk.core.resources.model import ResourceModel
2524
from superdesk.core.resources.service import AsyncResourceService
26-
from superdesk.core.resources.validators import validate_maxlength
2725
from superdesk.core.types import SearchRequest
2826
from superdesk.default_schema import DEFAULT_EDITOR, DEFAULT_SCHEMA
2927
from superdesk.errors import SuperdeskApiError
3028
from superdesk.flask import request
3129
from superdesk.notification import push_notification
3230
from superdesk.resource_fields import ID_FIELD
31+
from superdesk.types.vocabularies import VocabulariesResourceModel
3332
from superdesk.users import get_user_from_request
3433
from superdesk.utc import utcnow
3534

@@ -54,58 +53,6 @@
5453
}
5554

5655

57-
@dataclass
58-
class Tag:
59-
text: str
60-
61-
62-
class Item(BaseModel):
63-
model_config = ConfigDict(extra="allow")
64-
name: str
65-
qcode: str
66-
is_active: bool = True
67-
68-
69-
@dataclass
70-
class DateShortcut:
71-
value: int
72-
term: str
73-
label: str
74-
75-
76-
@dataclass
77-
class CustomFieldConfig:
78-
increment_steps: list[int]
79-
initial_offset_minutes: int
80-
81-
82-
class VocabulariesResourceModel(ResourceModel):
83-
display_name: str
84-
description: str | None = None
85-
helper_text: Annotated[str | None, validate_maxlength(120)] = None
86-
tags: list[Tag] | None = None
87-
popup_width: int | None = None
88-
type_: str # Use type_ instead of type due to reserved keyword conflict
89-
items: list[Item]
90-
selection_type: str | None = None
91-
read_only: bool | None = None
92-
schema_field: str | None = None
93-
dependent: bool = False
94-
service: dict[str, int] = Field(default_factory=dict)
95-
priority: int = 0
96-
unique_field: str | None = None
97-
schema_: dict[str, dict]
98-
field_type_: str | None = None
99-
field_options_: dict[str, Any] = Field(default_factory=dict)
100-
init_version: int = 0
101-
preffered_items: bool = False
102-
disable_entire_category_selection: bool = False
103-
date_shortcuts: list[DateShortcut] | None = None
104-
custom_field_type: str | None = None
105-
custom_field_config: CustomFieldConfig | None = None
106-
translations: dict[str, dict[str, Any]] = Field(default_factory=dict)
107-
108-
10956
class VocabulariesService(AsyncResourceService[VocabulariesResourceModel]):
11057
system_keys = set(DEFAULT_SCHEMA.keys()).union(set(DEFAULT_EDITOR.keys()))
11158

@@ -184,7 +131,6 @@ async def on_fetched_item(self, doc: VocabulariesResourceModel):
184131
async def on_update(self, updates: dict[str, Any], original: VocabulariesResourceModel) -> None:
185132
"""Checks the duplicates if a unique field is defined"""
186133
if "items" in updates:
187-
# FIXME: `model_copy` doesn't validate updates, is it fine here?
188134
updated = original.model_copy(deep=True, update=updates)
189135
await self._validate_items(updated)
190136
if original.unique_field:
@@ -249,7 +195,6 @@ def _cast_items(self, vocab: VocabulariesResourceModel) -> None:
249195
for item in vocab.items:
250196
for field, field_schema in schema.items():
251197
if hasattr(item, field):
252-
# FIXME: not sure about this one either.
253198
setattr(item, field, serialize_value(field_schema["type"], getattr(item, field)))
254199

255200
def _send_notification(self, updated_vocabulary: VocabulariesResourceModel, event="vocabularies:updated") -> None:

0 commit comments

Comments
 (0)