invenio-vocabularies 4.0.0__py2.py3-none-any.whl → 4.1.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of invenio-vocabularies might be problematic. Click here for more details.
- invenio_vocabularies/__init__.py +1 -1
- invenio_vocabularies/administration/__init__.py +10 -0
- invenio_vocabularies/administration/views/__init__.py +10 -0
- invenio_vocabularies/administration/views/vocabularies.py +44 -0
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/CustomAwardForm.js +8 -20
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.js +2 -2
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingModal.js +5 -7
- invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/NoAwardResults.js +3 -3
- invenio_vocabularies/config.py +33 -3
- invenio_vocabularies/contrib/affiliations/config.py +2 -2
- invenio_vocabularies/contrib/affiliations/datastreams.py +67 -0
- invenio_vocabularies/contrib/affiliations/jsonschemas/affiliations/affiliation-v1.0.0.json +38 -1
- invenio_vocabularies/contrib/affiliations/mappings/os-v1/affiliations/affiliation-v1.0.0.json +21 -0
- invenio_vocabularies/contrib/affiliations/mappings/os-v2/affiliations/affiliation-v1.0.0.json +21 -0
- invenio_vocabularies/contrib/affiliations/mappings/v7/affiliations/affiliation-v1.0.0.json +21 -0
- invenio_vocabularies/contrib/affiliations/schema.py +17 -3
- invenio_vocabularies/contrib/awards/jsonschemas/awards/award-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/awards/mappings/os-v1/awards/award-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/awards/mappings/os-v2/awards/award-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/awards/mappings/v7/awards/award-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/common/ror/datastreams.py +101 -1
- invenio_vocabularies/contrib/funders/datastreams.py +8 -92
- invenio_vocabularies/contrib/funders/jsonschemas/funders/funder-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/funders/mappings/v7/funders/funder-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/funders/serializer.py +2 -1
- invenio_vocabularies/contrib/names/jsonschemas/names/name-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/names/mappings/os-v1/names/name-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/names/mappings/os-v2/names/name-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/names/mappings/v7/names/name-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/subjects/jsonschemas/subjects/subject-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/subjects/mappings/os-v1/subjects/subject-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/subjects/mappings/os-v2/subjects/subject-v1.0.0.json +3 -0
- invenio_vocabularies/contrib/subjects/mappings/v7/subjects/subject-v1.0.0.json +3 -0
- invenio_vocabularies/datastreams/factories.py +1 -2
- invenio_vocabularies/datastreams/readers.py +84 -0
- invenio_vocabularies/datastreams/writers.py +2 -2
- invenio_vocabularies/ext.py +22 -7
- invenio_vocabularies/factories.py +15 -0
- invenio_vocabularies/proxies.py +2 -2
- invenio_vocabularies/records/jsonschemas/vocabularies/definitions-v1.0.0.json +7 -0
- invenio_vocabularies/records/jsonschemas/vocabularies/vocabulary-v1.0.0.json +1 -4
- invenio_vocabularies/resources/__init__.py +8 -1
- invenio_vocabularies/resources/config.py +105 -0
- invenio_vocabularies/resources/resource.py +31 -41
- invenio_vocabularies/services/__init__.py +5 -2
- invenio_vocabularies/services/config.py +179 -0
- invenio_vocabularies/services/permissions.py +3 -1
- invenio_vocabularies/services/results.py +110 -0
- invenio_vocabularies/services/schema.py +11 -2
- invenio_vocabularies/services/service.py +41 -86
- invenio_vocabularies/services/tasks.py +2 -2
- invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabularies-list.html +12 -0
- invenio_vocabularies/templates/semantic-ui/invenio_vocabularies/vocabulary-details.html +71 -0
- invenio_vocabularies/views.py +7 -0
- {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.1.1.dist-info}/METADATA +25 -1
- {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.1.1.dist-info}/RECORD +63 -54
- {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.1.1.dist-info}/entry_points.txt +4 -0
- {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.1.1.dist-info}/AUTHORS.rst +0 -0
- {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.1.1.dist-info}/LICENSE +0 -0
- {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.1.1.dist-info}/WHEEL +0 -0
- {invenio_vocabularies-4.0.0.dist-info → invenio_vocabularies-4.1.1.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2020-
|
|
3
|
+
# Copyright (C) 2020-2024 CERN.
|
|
4
4
|
#
|
|
5
5
|
# Invenio-Vocabularies is free software; you can redistribute it and/or
|
|
6
6
|
# modify it under the terms of the MIT License; see LICENSE file for more
|
|
@@ -9,7 +9,14 @@
|
|
|
9
9
|
"""Resources module."""
|
|
10
10
|
from invenio_vocabularies.resources.schema import L10NString, VocabularyL10Schema
|
|
11
11
|
|
|
12
|
+
from .config import VocabulariesResourceConfig, VocabularyTypeResourceConfig
|
|
13
|
+
from .resource import VocabulariesAdminResource, VocabulariesResource
|
|
14
|
+
|
|
12
15
|
__all__ = (
|
|
13
16
|
"VocabularyL10Schema",
|
|
14
17
|
"L10NString",
|
|
18
|
+
"VocabulariesResourceConfig",
|
|
19
|
+
"VocabularyTypeResourceConfig",
|
|
20
|
+
"VocabulariesAdminResource",
|
|
21
|
+
"VocabulariesResource",
|
|
15
22
|
)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2024 CERN.
|
|
4
|
+
# Copyright (C) 2024 University of Münster.
|
|
5
|
+
#
|
|
6
|
+
# Invenio-Vocabularies is free software; you can redistringibute it and/or modify it
|
|
7
|
+
# under the terms of the MIT License; see LICENSE file for more details.
|
|
8
|
+
|
|
9
|
+
"""Resources config."""
|
|
10
|
+
|
|
11
|
+
import marshmallow as ma
|
|
12
|
+
from flask_resources import (
|
|
13
|
+
BaseListSchema,
|
|
14
|
+
HTTPJSONException,
|
|
15
|
+
JSONSerializer,
|
|
16
|
+
MarshmallowSerializer,
|
|
17
|
+
ResourceConfig,
|
|
18
|
+
ResponseHandler,
|
|
19
|
+
create_error_handler,
|
|
20
|
+
)
|
|
21
|
+
from invenio_records_resources.resources import (
|
|
22
|
+
RecordResource,
|
|
23
|
+
RecordResourceConfig,
|
|
24
|
+
SearchRequestArgsSchema,
|
|
25
|
+
)
|
|
26
|
+
from invenio_records_resources.resources.errors import ErrorHandlersMixin
|
|
27
|
+
from invenio_records_resources.resources.records.args import SearchRequestArgsSchema
|
|
28
|
+
from invenio_records_resources.resources.records.headers import etag_headers
|
|
29
|
+
from invenio_records_resources.services.base.config import ConfiguratorMixin
|
|
30
|
+
|
|
31
|
+
from .serializer import VocabularyL10NItemSchema
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class VocabularySearchRequestArgsSchema(SearchRequestArgsSchema):
|
|
35
|
+
"""Vocabularies search request parameters."""
|
|
36
|
+
|
|
37
|
+
tags = ma.fields.Str()
|
|
38
|
+
active = ma.fields.Boolean()
|
|
39
|
+
status = ma.fields.Boolean()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class VocabulariesResourceConfig(RecordResourceConfig):
|
|
43
|
+
"""Vocabulary resource configuration."""
|
|
44
|
+
|
|
45
|
+
blueprint_name = "vocabularies"
|
|
46
|
+
url_prefix = "/vocabularies"
|
|
47
|
+
routes = {
|
|
48
|
+
"list": "/<type>",
|
|
49
|
+
"item": "/<type>/<pid_value>",
|
|
50
|
+
"tasks": "/tasks",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
request_view_args = {
|
|
54
|
+
"pid_value": ma.fields.Str(),
|
|
55
|
+
"type": ma.fields.Str(required=True),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
request_search_args = VocabularySearchRequestArgsSchema
|
|
59
|
+
|
|
60
|
+
response_handlers = {
|
|
61
|
+
"application/json": ResponseHandler(JSONSerializer(), headers=etag_headers),
|
|
62
|
+
"application/vnd.inveniordm.v1+json": ResponseHandler(
|
|
63
|
+
MarshmallowSerializer(
|
|
64
|
+
format_serializer_cls=JSONSerializer,
|
|
65
|
+
object_schema_cls=VocabularyL10NItemSchema,
|
|
66
|
+
list_schema_cls=BaseListSchema,
|
|
67
|
+
),
|
|
68
|
+
headers=etag_headers,
|
|
69
|
+
),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class VocabularyTypeResourceConfig(ResourceConfig, ConfiguratorMixin):
|
|
74
|
+
"""Vocabulary list resource config."""
|
|
75
|
+
|
|
76
|
+
# /vocabulary/vocabulary_id
|
|
77
|
+
# Blueprint configuration
|
|
78
|
+
blueprint_name = "vocabulary_types"
|
|
79
|
+
url_prefix = "/vocabularies"
|
|
80
|
+
|
|
81
|
+
routes = {"list": "/"}
|
|
82
|
+
|
|
83
|
+
# Request parsing
|
|
84
|
+
request_read_args = {}
|
|
85
|
+
request_view_args = {
|
|
86
|
+
"pid_value": ma.fields.String,
|
|
87
|
+
"type": ma.fields.String,
|
|
88
|
+
}
|
|
89
|
+
request_search_args = VocabularySearchRequestArgsSchema
|
|
90
|
+
|
|
91
|
+
error_handlers = {
|
|
92
|
+
**ErrorHandlersMixin.error_handlers,
|
|
93
|
+
# TODO: Add custom error handlers here
|
|
94
|
+
}
|
|
95
|
+
response_handlers = {
|
|
96
|
+
"application/json": ResponseHandler(JSONSerializer(), headers=etag_headers),
|
|
97
|
+
"application/vnd.inveniordm.v1+json": ResponseHandler(
|
|
98
|
+
MarshmallowSerializer(
|
|
99
|
+
format_serializer_cls=JSONSerializer,
|
|
100
|
+
object_schema_cls=VocabularyL10NItemSchema,
|
|
101
|
+
list_schema_cls=BaseListSchema,
|
|
102
|
+
),
|
|
103
|
+
headers=etag_headers,
|
|
104
|
+
),
|
|
105
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2020-
|
|
3
|
+
# Copyright (C) 2020-2024 CERN.
|
|
4
|
+
# Copyright (C) 2024 Uni Münster.
|
|
4
5
|
#
|
|
5
6
|
# Invenio-Vocabularies is free software; you can redistribute it and/or
|
|
6
7
|
# modify it under the terms of the MIT License; see LICENSE file for more
|
|
@@ -8,6 +9,8 @@
|
|
|
8
9
|
|
|
9
10
|
"""Vocabulary resource."""
|
|
10
11
|
|
|
12
|
+
import json
|
|
13
|
+
|
|
11
14
|
import marshmallow as ma
|
|
12
15
|
from flask import g
|
|
13
16
|
from flask_resources import (
|
|
@@ -18,6 +21,7 @@ from flask_resources import (
|
|
|
18
21
|
resource_requestctx,
|
|
19
22
|
response_handler,
|
|
20
23
|
)
|
|
24
|
+
from invenio_access.permissions import system_identity
|
|
21
25
|
from invenio_records_resources.resources import (
|
|
22
26
|
RecordResource,
|
|
23
27
|
RecordResourceConfig,
|
|
@@ -37,55 +41,20 @@ from marshmallow import fields
|
|
|
37
41
|
from .serializer import VocabularyL10NItemSchema
|
|
38
42
|
|
|
39
43
|
|
|
40
|
-
#
|
|
41
|
-
# Request args
|
|
42
|
-
#
|
|
43
|
-
class VocabularySearchRequestArgsSchema(SearchRequestArgsSchema):
|
|
44
|
-
"""Add parameter to parse tags."""
|
|
45
|
-
|
|
46
|
-
tags = fields.Str()
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
#
|
|
50
|
-
# Resource config
|
|
51
|
-
#
|
|
52
|
-
class VocabulariesResourceConfig(RecordResourceConfig):
|
|
53
|
-
"""Vocabulary resource configuration."""
|
|
54
|
-
|
|
55
|
-
blueprint_name = "vocabularies"
|
|
56
|
-
url_prefix = "/vocabularies"
|
|
57
|
-
routes = {"list": "/<type>", "item": "/<type>/<pid_value>", "tasks": "/tasks"}
|
|
58
|
-
|
|
59
|
-
request_view_args = {
|
|
60
|
-
"pid_value": ma.fields.Str(),
|
|
61
|
-
"type": ma.fields.Str(required=True),
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
request_search_args = VocabularySearchRequestArgsSchema
|
|
65
|
-
|
|
66
|
-
response_handlers = {
|
|
67
|
-
"application/json": ResponseHandler(JSONSerializer(), headers=etag_headers),
|
|
68
|
-
"application/vnd.inveniordm.v1+json": ResponseHandler(
|
|
69
|
-
MarshmallowSerializer(
|
|
70
|
-
format_serializer_cls=JSONSerializer,
|
|
71
|
-
object_schema_cls=VocabularyL10NItemSchema,
|
|
72
|
-
list_schema_cls=BaseListSchema,
|
|
73
|
-
),
|
|
74
|
-
headers=etag_headers,
|
|
75
|
-
),
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
44
|
#
|
|
80
45
|
# Resource
|
|
81
46
|
#
|
|
82
47
|
class VocabulariesResource(RecordResource):
|
|
83
|
-
"""Resource for generic vocabularies.
|
|
48
|
+
"""Resource for generic vocabularies.
|
|
49
|
+
|
|
50
|
+
Provide the API /api/vocabularies/
|
|
51
|
+
"""
|
|
84
52
|
|
|
85
53
|
def create_url_rules(self):
|
|
86
54
|
"""Create the URL rules for the record resource."""
|
|
87
55
|
routes = self.config.routes
|
|
88
56
|
rules = super().create_url_rules()
|
|
57
|
+
|
|
89
58
|
rules.append(
|
|
90
59
|
route("POST", routes["tasks"], self.launch),
|
|
91
60
|
)
|
|
@@ -164,3 +133,24 @@ class VocabulariesResource(RecordResource):
|
|
|
164
133
|
"""Create a task."""
|
|
165
134
|
self.service.launch(g.identity, resource_requestctx.data or {})
|
|
166
135
|
return "", 202
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class VocabulariesAdminResource(RecordResource):
|
|
139
|
+
"""Resource for vocabularies admin interface."""
|
|
140
|
+
|
|
141
|
+
def create_url_rules(self):
|
|
142
|
+
"""Create the URL rules for the record resource."""
|
|
143
|
+
routes = self.config.routes
|
|
144
|
+
|
|
145
|
+
rules = [route("GET", routes["list"], self.search)]
|
|
146
|
+
|
|
147
|
+
return rules
|
|
148
|
+
|
|
149
|
+
@request_search_args
|
|
150
|
+
@response_handler(many=True)
|
|
151
|
+
def search(self):
|
|
152
|
+
"""Return information about _all_ vocabularies."""
|
|
153
|
+
identity = g.identity
|
|
154
|
+
hits = self.service.search(identity, params=resource_requestctx.args)
|
|
155
|
+
|
|
156
|
+
return hits.to_dict(), 200
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2020-
|
|
3
|
+
# Copyright (C) 2020-2024 CERN.
|
|
4
4
|
#
|
|
5
5
|
# Invenio-Vocabularies is free software; you can redistribute it and/or
|
|
6
6
|
# modify it under the terms of the MIT License; see LICENSE file for more
|
|
@@ -8,9 +8,12 @@
|
|
|
8
8
|
|
|
9
9
|
"""Services module."""
|
|
10
10
|
|
|
11
|
-
from .
|
|
11
|
+
from .config import VocabulariesServiceConfig, VocabularyTypesServiceConfig
|
|
12
|
+
from .service import VocabulariesService, VocabularyTypeService
|
|
12
13
|
|
|
13
14
|
__all__ = (
|
|
14
15
|
"VocabulariesService",
|
|
16
|
+
"VocabularyTypeService",
|
|
15
17
|
"VocabulariesServiceConfig",
|
|
18
|
+
"VocabularyTypesServiceConfig",
|
|
16
19
|
)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2024 CERN.
|
|
4
|
+
# Copyright (C) 2024 Uni Münster.
|
|
5
|
+
#
|
|
6
|
+
# Invenio-Vocabularies is free software; you can redistribute it and/or
|
|
7
|
+
# modify it under the terms of the MIT License; see LICENSE file for more
|
|
8
|
+
# details.
|
|
9
|
+
|
|
10
|
+
"""Vocabulary services configs."""
|
|
11
|
+
|
|
12
|
+
import sqlalchemy as sa
|
|
13
|
+
from flask import current_app
|
|
14
|
+
from invenio_i18n import lazy_gettext as _
|
|
15
|
+
from invenio_records_resources.services import (
|
|
16
|
+
Link,
|
|
17
|
+
LinksTemplate,
|
|
18
|
+
RecordService,
|
|
19
|
+
RecordServiceConfig,
|
|
20
|
+
SearchOptions,
|
|
21
|
+
pagination_links,
|
|
22
|
+
)
|
|
23
|
+
from invenio_records_resources.services.base import (
|
|
24
|
+
ConditionalLink,
|
|
25
|
+
Service,
|
|
26
|
+
ServiceListResult,
|
|
27
|
+
)
|
|
28
|
+
from invenio_records_resources.services.records.components import DataComponent
|
|
29
|
+
from invenio_records_resources.services.records.params import (
|
|
30
|
+
FilterParam,
|
|
31
|
+
SuggestQueryParser,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from ..records.api import Vocabulary
|
|
35
|
+
from ..records.models import VocabularyType
|
|
36
|
+
from . import results
|
|
37
|
+
from .components import PIDComponent, VocabularyTypeComponent
|
|
38
|
+
from .permissions import PermissionPolicy
|
|
39
|
+
from .schema import TaskSchema, VocabularySchema
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def is_custom_vocabulary_type(vocabulary_type, context):
|
|
43
|
+
"""Check if the vocabulary type is a custom vocabulary type."""
|
|
44
|
+
return vocabulary_type["id"] in current_app.config.get(
|
|
45
|
+
"VOCABULARIES_CUSTOM_VOCABULARY_TYPES", []
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class VocabularySearchOptions(SearchOptions):
|
|
50
|
+
"""Search options for vocabularies."""
|
|
51
|
+
|
|
52
|
+
params_interpreters_cls = [
|
|
53
|
+
FilterParam.factory(param="tags", field="tags"),
|
|
54
|
+
] + SearchOptions.params_interpreters_cls
|
|
55
|
+
|
|
56
|
+
suggest_parser_cls = SuggestQueryParser.factory(
|
|
57
|
+
fields=[
|
|
58
|
+
"id.text^100",
|
|
59
|
+
"id.text._2gram",
|
|
60
|
+
"id.text._3gram",
|
|
61
|
+
"title.en^5",
|
|
62
|
+
"title.en._2gram",
|
|
63
|
+
"title.en._3gram",
|
|
64
|
+
],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
sort_default = "bestmatch"
|
|
68
|
+
|
|
69
|
+
sort_default_no_query = "title"
|
|
70
|
+
|
|
71
|
+
sort_options = {
|
|
72
|
+
"bestmatch": dict(
|
|
73
|
+
title=_("Best match"),
|
|
74
|
+
fields=["_score"], # ES defaults to desc on `_score` field
|
|
75
|
+
),
|
|
76
|
+
"title": dict(
|
|
77
|
+
title=_("Title"),
|
|
78
|
+
fields=["title_sort"],
|
|
79
|
+
),
|
|
80
|
+
"newest": dict(
|
|
81
|
+
title=_("Newest"),
|
|
82
|
+
fields=["-created"],
|
|
83
|
+
),
|
|
84
|
+
"oldest": dict(
|
|
85
|
+
title=_("Oldest"),
|
|
86
|
+
fields=["created"],
|
|
87
|
+
),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class VocabularyTypeSearchOptions(SearchOptions):
|
|
92
|
+
"""Search options for vocabulary types."""
|
|
93
|
+
|
|
94
|
+
sort_options = {
|
|
95
|
+
"id": dict(
|
|
96
|
+
title=_("ID"),
|
|
97
|
+
fields=["id"],
|
|
98
|
+
),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
sort_default = "id"
|
|
102
|
+
|
|
103
|
+
sort_default_no_query = "id"
|
|
104
|
+
|
|
105
|
+
sort_direction_options = {
|
|
106
|
+
"asc": dict(title=_("Ascending"), fn=sa.asc),
|
|
107
|
+
"desc": dict(title=_("Descending"), fn=sa.desc),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
sort_direction_default = "asc"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class VocabulariesServiceConfig(RecordServiceConfig):
|
|
114
|
+
"""Vocabulary service configuration."""
|
|
115
|
+
|
|
116
|
+
service_id = "vocabularies"
|
|
117
|
+
indexer_queue_name = "vocabularies"
|
|
118
|
+
permission_policy_cls = PermissionPolicy
|
|
119
|
+
record_cls = Vocabulary
|
|
120
|
+
schema = VocabularySchema
|
|
121
|
+
task_schema = TaskSchema
|
|
122
|
+
|
|
123
|
+
search = VocabularySearchOptions
|
|
124
|
+
|
|
125
|
+
components = [
|
|
126
|
+
# Order of components are important!
|
|
127
|
+
VocabularyTypeComponent,
|
|
128
|
+
DataComponent,
|
|
129
|
+
PIDComponent,
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
links_item = {
|
|
133
|
+
"self": Link(
|
|
134
|
+
"{+api}/vocabularies/{type}/{id}",
|
|
135
|
+
vars=lambda record, vars: vars.update(
|
|
136
|
+
{
|
|
137
|
+
"id": record.pid.pid_value,
|
|
138
|
+
"type": record.type.id,
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
links_search = pagination_links("{+api}/vocabularies/{type}{?args*}")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class VocabularyTypesServiceConfig(RecordServiceConfig):
|
|
148
|
+
"""Vocabulary types service configuration."""
|
|
149
|
+
|
|
150
|
+
service_id = "vocabulary_types"
|
|
151
|
+
permission_policy_cls = PermissionPolicy
|
|
152
|
+
record_cls = VocabularyType
|
|
153
|
+
schema = VocabularySchema # Works but should be VocabularyTypeSchema if this is defined at some point
|
|
154
|
+
result_list_cls = results.VocabularyTypeList
|
|
155
|
+
|
|
156
|
+
links_item = {
|
|
157
|
+
"self": ConditionalLink(
|
|
158
|
+
cond=is_custom_vocabulary_type,
|
|
159
|
+
if_=Link(
|
|
160
|
+
"{+api}/{id}",
|
|
161
|
+
vars=lambda vocab_type, vars: vars.update({"id": vocab_type["id"]}),
|
|
162
|
+
),
|
|
163
|
+
else_=Link(
|
|
164
|
+
"{+api}/vocabularies/{id}",
|
|
165
|
+
vars=lambda vocab_type, vars: vars.update({"id": vocab_type["id"]}),
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
search = VocabularyTypeSearchOptions
|
|
171
|
+
|
|
172
|
+
components = [
|
|
173
|
+
# Order of components are important!
|
|
174
|
+
VocabularyTypeComponent,
|
|
175
|
+
DataComponent,
|
|
176
|
+
PIDComponent,
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
links_search = pagination_links("{+api}/vocabularies/{type}{?args*}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2020-
|
|
3
|
+
# Copyright (C) 2020-2024 CERN.
|
|
4
4
|
#
|
|
5
5
|
# Invenio-Vocabularies is free software; you can redistribute it and/or
|
|
6
6
|
# modify it under the terms of the MIT License; see LICENSE file for more
|
|
@@ -21,3 +21,5 @@ class PermissionPolicy(RecordPermissionPolicy):
|
|
|
21
21
|
can_update = [SystemProcess()]
|
|
22
22
|
can_delete = [SystemProcess()]
|
|
23
23
|
can_manage = [SystemProcess()]
|
|
24
|
+
# this permission is needed for the /api/vocabularies/ endpoint
|
|
25
|
+
can_list_vocabularies = [SystemProcess(), AnyUser()]
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2024 CERN.
|
|
4
|
+
# Copyright (C) 2024 Uni Münster.
|
|
5
|
+
#
|
|
6
|
+
# Invenio-Vocabularies is free software; you can redistribute it and/or
|
|
7
|
+
# modify it under the terms of the MIT License; see LICENSE file for more
|
|
8
|
+
# details.
|
|
9
|
+
|
|
10
|
+
"""Vocabulary results."""
|
|
11
|
+
|
|
12
|
+
from flask import current_app
|
|
13
|
+
from invenio_records_resources.proxies import current_service_registry
|
|
14
|
+
from invenio_records_resources.services import RecordServiceConfig
|
|
15
|
+
from invenio_records_resources.services.records.results import RecordList
|
|
16
|
+
from invenio_search import current_search_client
|
|
17
|
+
|
|
18
|
+
from ..proxies import current_service
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class VocabularyTypeList(RecordList):
|
|
22
|
+
"""Ensures that vocabulary type metadata is returned in the proper format."""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def total(self):
|
|
26
|
+
"""Get total number of hits."""
|
|
27
|
+
return self._results.total
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def custom_vocabulary_names(self):
|
|
31
|
+
"""Checks whether vocabulary is a custom vocabulary."""
|
|
32
|
+
return current_app.config.get("VOCABULARIES_CUSTOM_VOCABULARY_TYPES", [])
|
|
33
|
+
|
|
34
|
+
def to_dict(self):
|
|
35
|
+
"""Formats result to a dict of hits."""
|
|
36
|
+
config_vocab_types = current_app.config.get(
|
|
37
|
+
"INVENIO_VOCABULARY_TYPE_METADATA", {}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
count_terms_agg = {}
|
|
41
|
+
generic_stats = self._generic_vocabulary_statistics()
|
|
42
|
+
custom_stats = self._custom_vocabulary_statistics()
|
|
43
|
+
|
|
44
|
+
for k in generic_stats.keys() | custom_stats.keys():
|
|
45
|
+
count_terms_agg[k] = generic_stats.get(k, 0) + custom_stats.get(k, 0)
|
|
46
|
+
|
|
47
|
+
hits = self._results.items
|
|
48
|
+
|
|
49
|
+
# Extend database data with configuration & aggregation data.
|
|
50
|
+
results = []
|
|
51
|
+
for db_vocab_type in hits:
|
|
52
|
+
result = {
|
|
53
|
+
"id": db_vocab_type.id,
|
|
54
|
+
"pid_type": db_vocab_type.pid_type,
|
|
55
|
+
"count": count_terms_agg.get(db_vocab_type.id, 0),
|
|
56
|
+
"is_custom_vocabulary": db_vocab_type.id
|
|
57
|
+
in self.custom_vocabulary_names,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if db_vocab_type.id in config_vocab_types:
|
|
61
|
+
for k, v in config_vocab_types[db_vocab_type.id].items():
|
|
62
|
+
result[k] = v
|
|
63
|
+
|
|
64
|
+
results.append(result)
|
|
65
|
+
|
|
66
|
+
for hit in results:
|
|
67
|
+
if self._links_item_tpl:
|
|
68
|
+
hit["links"] = self._links_item_tpl.expand(self._identity, hit)
|
|
69
|
+
|
|
70
|
+
res = {
|
|
71
|
+
"hits": {
|
|
72
|
+
"hits": results,
|
|
73
|
+
"total": self.total,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if self._params:
|
|
78
|
+
if self._links_tpl:
|
|
79
|
+
res["links"] = self._links_tpl.expand(self._identity, self.pagination)
|
|
80
|
+
|
|
81
|
+
return res
|
|
82
|
+
|
|
83
|
+
def _custom_vocabulary_statistics(self):
|
|
84
|
+
# query database for count of terms in custom vocabularies
|
|
85
|
+
returndict = {}
|
|
86
|
+
for vocab_type in self.custom_vocabulary_names:
|
|
87
|
+
custom_service = current_service_registry.get(vocab_type)
|
|
88
|
+
record_cls = custom_service.config.record_cls
|
|
89
|
+
returndict[vocab_type] = record_cls.model_cls.query.count()
|
|
90
|
+
|
|
91
|
+
return returndict
|
|
92
|
+
|
|
93
|
+
def _generic_vocabulary_statistics(self):
|
|
94
|
+
# Opensearch query for generic vocabularies
|
|
95
|
+
config: RecordServiceConfig = (
|
|
96
|
+
current_service.config
|
|
97
|
+
) # TODO: Where to get the config from here? current_service is None
|
|
98
|
+
search_opts = config.search
|
|
99
|
+
|
|
100
|
+
search = search_opts.search_cls(
|
|
101
|
+
using=current_search_client,
|
|
102
|
+
index=config.record_cls.index.search_alias,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
search.aggs.bucket("vocabularies", {"terms": {"field": "type.id", "size": 100}})
|
|
106
|
+
|
|
107
|
+
search_result = search.execute()
|
|
108
|
+
buckets = search_result.aggs.to_dict()["vocabularies"]["buckets"]
|
|
109
|
+
|
|
110
|
+
return {bucket["key"]: bucket["doc_count"] for bucket in buckets}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2020-
|
|
3
|
+
# Copyright (C) 2020-2024 CERN.
|
|
4
4
|
#
|
|
5
5
|
# Invenio-Vocabularies is free software; you can redistribute it and/or
|
|
6
6
|
# modify it under the terms of the MIT License; see LICENSE file for more
|
|
@@ -35,6 +35,9 @@ class BaseVocabularyRelationSchema(Schema):
|
|
|
35
35
|
|
|
36
36
|
id = SanitizedUnicode(required=True)
|
|
37
37
|
|
|
38
|
+
# Nested field type for administration UI form generation
|
|
39
|
+
administration_schema_type = "vocabulary"
|
|
40
|
+
|
|
38
41
|
|
|
39
42
|
class VocabularyRelationSchema(BaseVocabularyRelationSchema):
|
|
40
43
|
"""Vocabulary relation schema."""
|
|
@@ -63,6 +66,9 @@ class ContribVocabularyRelationSchema(Schema):
|
|
|
63
66
|
ftf_name = None # free text field name
|
|
64
67
|
parent_field_name = None
|
|
65
68
|
|
|
69
|
+
# Nested field type for administration UI form generation
|
|
70
|
+
administration_schema_type = "vocabulary"
|
|
71
|
+
|
|
66
72
|
@validates_schema
|
|
67
73
|
def validate_relation_schema(self, data, **kwargs):
|
|
68
74
|
"""Validates that either id either the free text field are present."""
|
|
@@ -90,13 +96,16 @@ class BaseVocabularySchema(BaseRecordSchema):
|
|
|
90
96
|
title = i18n_strings
|
|
91
97
|
description = i18n_strings
|
|
92
98
|
icon = fields.Str(allow_none=False)
|
|
99
|
+
tags = fields.List(SanitizedUnicode())
|
|
100
|
+
|
|
101
|
+
# Nested field type for administration UI form generation
|
|
102
|
+
administration_schema_type = "vocabulary"
|
|
93
103
|
|
|
94
104
|
|
|
95
105
|
class VocabularySchema(BaseVocabularySchema):
|
|
96
106
|
"""Service schema for vocabulary records."""
|
|
97
107
|
|
|
98
108
|
props = fields.Dict(allow_none=False, keys=fields.Str(), values=fields.Str())
|
|
99
|
-
tags = fields.List(SanitizedUnicode())
|
|
100
109
|
type = fields.Str(attribute="type.id", required=True)
|
|
101
110
|
|
|
102
111
|
|