invenio-vocabularies 3.4.2__py2.py3-none-any.whl → 4.0.0__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/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/CustomAwardForm.js +2 -2
- invenio_vocabularies/cli.py +10 -39
- invenio_vocabularies/contrib/awards/datastreams.py +90 -3
- invenio_vocabularies/contrib/common/__init__.py +9 -0
- invenio_vocabularies/contrib/common/ror/__init__.py +9 -0
- invenio_vocabularies/contrib/common/ror/datastreams.py +66 -0
- invenio_vocabularies/contrib/funders/config.py +2 -0
- invenio_vocabularies/contrib/funders/datastreams.py +47 -12
- invenio_vocabularies/contrib/funders/jsonschemas/funders/funder-v1.0.0.json +33 -1
- invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v1.0.0.json +18 -0
- invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v1.0.0.json +18 -0
- invenio_vocabularies/contrib/funders/mappings/v7/funders/funder-v1.0.0.json +18 -0
- invenio_vocabularies/contrib/funders/schema.py +8 -0
- invenio_vocabularies/datastreams/readers.py +12 -3
- invenio_vocabularies/factories.py +78 -0
- invenio_vocabularies/services/schema.py +1 -10
- invenio_vocabularies/services/tasks.py +30 -0
- {invenio_vocabularies-3.4.2.dist-info → invenio_vocabularies-4.0.0.dist-info}/METADATA +10 -9
- {invenio_vocabularies-3.4.2.dist-info → invenio_vocabularies-4.0.0.dist-info}/RECORD +25 -21
- {invenio_vocabularies-3.4.2.dist-info → invenio_vocabularies-4.0.0.dist-info}/entry_points.txt +3 -0
- {invenio_vocabularies-3.4.2.dist-info → invenio_vocabularies-4.0.0.dist-info}/AUTHORS.rst +0 -0
- {invenio_vocabularies-3.4.2.dist-info → invenio_vocabularies-4.0.0.dist-info}/LICENSE +0 -0
- {invenio_vocabularies-3.4.2.dist-info → invenio_vocabularies-4.0.0.dist-info}/WHEEL +0 -0
- {invenio_vocabularies-3.4.2.dist-info → invenio_vocabularies-4.0.0.dist-info}/top_level.txt +0 -0
invenio_vocabularies/__init__.py
CHANGED
|
@@ -28,8 +28,8 @@ function CustomAwardForm({ deserializeFunder, selectedFunding }) {
|
|
|
28
28
|
funderPID = funderItem.id;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
if (funderItem.
|
|
32
|
-
funderCountry = funderItem.
|
|
31
|
+
if (funderItem.country_name) {
|
|
32
|
+
funderCountry = funderItem.country_name;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (!funderName && !funderPID) {
|
invenio_vocabularies/cli.py
CHANGED
|
@@ -9,46 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
"""Commands to create and manage vocabularies."""
|
|
11
11
|
|
|
12
|
-
from copy import deepcopy
|
|
13
12
|
|
|
14
13
|
import click
|
|
15
|
-
import yaml
|
|
16
14
|
from flask.cli import with_appcontext
|
|
17
15
|
from invenio_access.permissions import system_identity
|
|
18
16
|
from invenio_pidstore.errors import PIDDeletedError, PIDDoesNotExistError
|
|
19
|
-
from invenio_records_resources.proxies import current_service_registry
|
|
20
17
|
|
|
21
|
-
from .contrib.awards.datastreams import DATASTREAM_CONFIG as awards_ds_config
|
|
22
|
-
from .contrib.funders.datastreams import DATASTREAM_CONFIG as funders_ds_config
|
|
23
|
-
from .contrib.names.datastreams import DATASTREAM_CONFIG as names_ds_config
|
|
24
18
|
from .datastreams import DataStreamFactory
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def get_config_for_ds(vocabulary, filepath=None, origin=None):
|
|
28
|
-
"""Calculates the configuration for a Data Stream."""
|
|
29
|
-
config = None
|
|
30
|
-
if vocabulary == "names": # FIXME: turn into a proper factory
|
|
31
|
-
config = deepcopy(names_ds_config)
|
|
32
|
-
elif vocabulary == "funders":
|
|
33
|
-
config = deepcopy(funders_ds_config)
|
|
34
|
-
elif vocabulary == "awards":
|
|
35
|
-
config = deepcopy(awards_ds_config)
|
|
36
|
-
|
|
37
|
-
if config:
|
|
38
|
-
if filepath:
|
|
39
|
-
with open(filepath) as f:
|
|
40
|
-
config = yaml.safe_load(f).get(vocabulary)
|
|
41
|
-
if origin:
|
|
42
|
-
config["readers"][0].setdefault("args", {})
|
|
43
|
-
config["readers"][0]["args"]["origin"] = origin
|
|
44
|
-
|
|
45
|
-
return config
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def get_service_for_vocabulary(vocabulary):
|
|
49
|
-
"""Calculates the configuration for a Data Stream."""
|
|
50
|
-
if vocabulary == "names": # FIXME: turn into a proper factory
|
|
51
|
-
return current_service_registry.get("names")
|
|
19
|
+
from .factories import get_vocabulary_config
|
|
52
20
|
|
|
53
21
|
|
|
54
22
|
@click.group()
|
|
@@ -111,7 +79,9 @@ def import_vocab(vocabulary, filepath=None, origin=None, num_samples=None):
|
|
|
111
79
|
click.secho("One of --filepath or --origin must be present.", fg="red")
|
|
112
80
|
exit(1)
|
|
113
81
|
|
|
114
|
-
|
|
82
|
+
vc = get_vocabulary_config(vocabulary)
|
|
83
|
+
config = vc.get_config(filepath, origin)
|
|
84
|
+
|
|
115
85
|
success, errored, filtered = _process_vocab(config, num_samples)
|
|
116
86
|
|
|
117
87
|
_output_process(vocabulary, "imported", success, errored, filtered)
|
|
@@ -127,8 +97,8 @@ def update(vocabulary, filepath=None, origin=None):
|
|
|
127
97
|
if not filepath and not origin:
|
|
128
98
|
click.secho("One of --filepath or --origin must be present.", fg="red")
|
|
129
99
|
exit(1)
|
|
130
|
-
|
|
131
|
-
config =
|
|
100
|
+
vc = get_vocabulary_config(vocabulary)
|
|
101
|
+
config = vc.get_config(filepath, origin)
|
|
132
102
|
|
|
133
103
|
for w_conf in config["writers"]:
|
|
134
104
|
w_conf["args"]["update"] = True
|
|
@@ -153,7 +123,8 @@ def convert(vocabulary, filepath=None, origin=None, target=None, num_samples=Non
|
|
|
153
123
|
)
|
|
154
124
|
exit(1)
|
|
155
125
|
|
|
156
|
-
|
|
126
|
+
vc = get_vocabulary_config(vocabulary)
|
|
127
|
+
config = vc.get_config(filepath, origin)
|
|
157
128
|
if not filepath:
|
|
158
129
|
config["writers"] = [{"type": "yaml", "args": {"filepath": target}}]
|
|
159
130
|
|
|
@@ -176,8 +147,8 @@ def delete(vocabulary, identifier, all):
|
|
|
176
147
|
if not id and not all:
|
|
177
148
|
click.secho("An identifier or the --all flag must be present.", fg="red")
|
|
178
149
|
exit(1)
|
|
179
|
-
|
|
180
|
-
service =
|
|
150
|
+
vc = get_vocabulary_config(vocabulary)
|
|
151
|
+
service = vc.get_service()
|
|
181
152
|
if identifier:
|
|
182
153
|
try:
|
|
183
154
|
if service.delete(identifier, system_identity):
|
|
@@ -1,22 +1,79 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2022 CERN.
|
|
3
|
+
# Copyright (C) 2022-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
|
|
7
7
|
# details.
|
|
8
8
|
|
|
9
9
|
"""Awards datastreams, transformers, writers and readers."""
|
|
10
|
+
import io
|
|
10
11
|
|
|
12
|
+
import requests
|
|
11
13
|
from invenio_access.permissions import system_identity
|
|
12
14
|
from invenio_i18n import lazy_gettext as _
|
|
13
15
|
|
|
14
|
-
from ...datastreams.errors import TransformerError
|
|
16
|
+
from ...datastreams.errors import ReaderError, TransformerError
|
|
17
|
+
from ...datastreams.readers import BaseReader
|
|
15
18
|
from ...datastreams.transformers import BaseTransformer
|
|
16
19
|
from ...datastreams.writers import ServiceWriter
|
|
17
20
|
from .config import awards_ec_ror_id, awards_openaire_funders_mapping
|
|
18
21
|
|
|
19
22
|
|
|
23
|
+
class OpenAIREProjectHTTPReader(BaseReader):
|
|
24
|
+
"""OpenAIRE Project HTTP Reader returning an in-memory binary stream of the latest OpenAIRE Graph Dataset project tar file."""
|
|
25
|
+
|
|
26
|
+
def _iter(self, fp, *args, **kwargs):
|
|
27
|
+
raise NotImplementedError(
|
|
28
|
+
"OpenAIREProjectHTTPReader downloads one file and therefore does not iterate through items"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def read(self, item=None, *args, **kwargs):
|
|
32
|
+
"""Reads the latest OpenAIRE Graph Dataset project tar file from Zenodo and yields an in-memory binary stream of it."""
|
|
33
|
+
if item:
|
|
34
|
+
raise NotImplementedError(
|
|
35
|
+
"OpenAIREProjectHTTPReader does not support being chained after another reader"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if self._origin == "full":
|
|
39
|
+
# OpenAIRE Graph Dataset
|
|
40
|
+
api_url = "https://zenodo.org/api/records/3516917"
|
|
41
|
+
elif self._origin == "diff":
|
|
42
|
+
# OpenAIRE Graph dataset: new collected projects
|
|
43
|
+
api_url = "https://zenodo.org/api/records/6419021"
|
|
44
|
+
else:
|
|
45
|
+
raise ReaderError("The --origin option should be either 'full' or 'diff'")
|
|
46
|
+
|
|
47
|
+
# Call the signposting `linkset+json` endpoint for the Concept DOI (i.e. latest version) of the OpenAIRE Graph Dataset.
|
|
48
|
+
# See: https://github.com/inveniosoftware/rfcs/blob/master/rfcs/rdm-0071-signposting.md#provide-an-applicationlinksetjson-endpoint
|
|
49
|
+
headers = {"Accept": "application/linkset+json"}
|
|
50
|
+
api_resp = requests.get(api_url, headers=headers)
|
|
51
|
+
api_resp.raise_for_status()
|
|
52
|
+
|
|
53
|
+
# Extract the Landing page Link Set Object located as the first (index 0) item.
|
|
54
|
+
landing_page_linkset = api_resp.json()["linkset"][0]
|
|
55
|
+
|
|
56
|
+
# Extract the URL of the only project tar file linked to the record.
|
|
57
|
+
landing_page_project_tar_items = [
|
|
58
|
+
item
|
|
59
|
+
for item in landing_page_linkset["item"]
|
|
60
|
+
if item["type"] == "application/x-tar"
|
|
61
|
+
and item["href"].endswith("/project.tar")
|
|
62
|
+
]
|
|
63
|
+
if len(landing_page_project_tar_items) != 1:
|
|
64
|
+
raise ReaderError(
|
|
65
|
+
f"Expected 1 project tar item but got {len(landing_page_project_tar_items)}"
|
|
66
|
+
)
|
|
67
|
+
file_url = landing_page_project_tar_items[0]["href"]
|
|
68
|
+
|
|
69
|
+
# Download the project tar file and fully load the response bytes content in memory.
|
|
70
|
+
# The bytes content are then wrapped by a BytesIO to be file-like object (as required by `tarfile.open`).
|
|
71
|
+
# Using directly `file_resp.raw` is not possible since `tarfile.open` requires the file-like object to be seekable.
|
|
72
|
+
file_resp = requests.get(file_url)
|
|
73
|
+
file_resp.raise_for_status()
|
|
74
|
+
yield io.BytesIO(file_resp.content)
|
|
75
|
+
|
|
76
|
+
|
|
20
77
|
class AwardsServiceWriter(ServiceWriter):
|
|
21
78
|
"""Funders service writer."""
|
|
22
79
|
|
|
@@ -39,7 +96,20 @@ class OpenAIREProjectTransformer(BaseTransformer):
|
|
|
39
96
|
award = {}
|
|
40
97
|
|
|
41
98
|
code = record["code"]
|
|
42
|
-
|
|
99
|
+
|
|
100
|
+
# The `id` should follow the format `sourcePrefix::md5(localId)` where `sourcePrefix` is 12 characters long.
|
|
101
|
+
# See: https://graph.openaire.eu/docs/data-model/pids-and-identifiers#identifiers-in-the-graph
|
|
102
|
+
#
|
|
103
|
+
# The format of `id` in the full OpenAIRE Graph Dataset (https://doi.org/10.5281/zenodo.3516917)
|
|
104
|
+
# follows this format (e.g. 'abc_________::0123456789abcdef0123456789abcdef').
|
|
105
|
+
# However, the format of `id` in the new collected projects dataset (https://doi.org/10.5281/zenodo.6419021)
|
|
106
|
+
# does not follow this format, and has a `40|` prefix (e.g. '40|abc_________::0123456789abcdef0123456789abcdef').
|
|
107
|
+
#
|
|
108
|
+
# The number '40' corresponds to the entity types 'Project'.
|
|
109
|
+
# See: https://ec.europa.eu/research/participants/documents/downloadPublic?documentIds=080166e5a3a1a213&appId=PPGMS
|
|
110
|
+
# See: https://graph.openaire.eu/docs/5.0.0/data-model/entities/project#id
|
|
111
|
+
openaire_funder_prefix = record["id"].split("::", 1)[0].split("|", 1)[-1]
|
|
112
|
+
|
|
43
113
|
funder_id = awards_openaire_funders_mapping.get(openaire_funder_prefix)
|
|
44
114
|
if funder_id is None:
|
|
45
115
|
raise TransformerError(
|
|
@@ -78,7 +148,20 @@ class OpenAIREProjectTransformer(BaseTransformer):
|
|
|
78
148
|
award["identifiers"] = identifiers
|
|
79
149
|
|
|
80
150
|
award["number"] = code
|
|
151
|
+
|
|
152
|
+
# `title` is a mandatory attribute of the `Project` object in the OpenAIRE Graph Data Model.
|
|
153
|
+
# See: https://graph.openaire.eu/docs/data-model/entities/project#title
|
|
154
|
+
# However, 15'000+ awards for the FCT funder (and 1 record the NIH funder) are missing a title attribute.
|
|
155
|
+
if "title" not in record:
|
|
156
|
+
raise TransformerError(
|
|
157
|
+
_(
|
|
158
|
+
"Missing title attribute for award {award_id}".format(
|
|
159
|
+
award_id=award["id"]
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
)
|
|
81
163
|
award["title"] = {"en": record["title"]}
|
|
164
|
+
|
|
82
165
|
award["funder"] = {"id": funder_id}
|
|
83
166
|
acronym = record.get("acronym")
|
|
84
167
|
if acronym:
|
|
@@ -88,6 +171,10 @@ class OpenAIREProjectTransformer(BaseTransformer):
|
|
|
88
171
|
return stream_entry
|
|
89
172
|
|
|
90
173
|
|
|
174
|
+
VOCABULARIES_DATASTREAM_READERS = {
|
|
175
|
+
"openaire-project-http": OpenAIREProjectHTTPReader,
|
|
176
|
+
}
|
|
177
|
+
|
|
91
178
|
VOCABULARIES_DATASTREAM_TRANSFORMERS = {
|
|
92
179
|
"openaire-award": OpenAIREProjectTransformer,
|
|
93
180
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2024 CERN.
|
|
4
|
+
#
|
|
5
|
+
# Invenio-Vocabularies is free software; you can redistribute it and/or
|
|
6
|
+
# modify it under the terms of the MIT License; see LICENSE file for more
|
|
7
|
+
# details.
|
|
8
|
+
|
|
9
|
+
"""ROR-related Datastreams Readers/Writers/Transformers module."""
|
|
10
|
+
|
|
11
|
+
import io
|
|
12
|
+
|
|
13
|
+
import requests
|
|
14
|
+
|
|
15
|
+
from invenio_vocabularies.datastreams.errors import ReaderError
|
|
16
|
+
from invenio_vocabularies.datastreams.readers import BaseReader
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class RORHTTPReader(BaseReader):
|
|
20
|
+
"""ROR HTTP Reader returning an in-memory binary stream of the latest ROR data dump ZIP file."""
|
|
21
|
+
|
|
22
|
+
def _iter(self, fp, *args, **kwargs):
|
|
23
|
+
raise NotImplementedError(
|
|
24
|
+
"RORHTTPReader downloads one file and therefore does not iterate through items"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def read(self, item=None, *args, **kwargs):
|
|
28
|
+
"""Reads the latest ROR data dump ZIP file from Zenodo and yields an in-memory binary stream of it."""
|
|
29
|
+
if item:
|
|
30
|
+
raise NotImplementedError(
|
|
31
|
+
"RORHTTPReader does not support being chained after another reader"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Call the signposting `linkset+json` endpoint for the Concept DOI (i.e. latest version) of the ROR data dump.
|
|
35
|
+
# See: https://github.com/inveniosoftware/rfcs/blob/master/rfcs/rdm-0071-signposting.md#provide-an-applicationlinksetjson-endpoint
|
|
36
|
+
headers = {"Accept": "application/linkset+json"}
|
|
37
|
+
api_url = "https://zenodo.org/api/records/6347574"
|
|
38
|
+
api_resp = requests.get(api_url, headers=headers)
|
|
39
|
+
api_resp.raise_for_status()
|
|
40
|
+
|
|
41
|
+
# Extract the Landing page Link Set Object located as the first (index 0) item.
|
|
42
|
+
landing_page_linkset = api_resp.json()["linkset"][0]
|
|
43
|
+
|
|
44
|
+
# Extract the URL of the only ZIP file linked to the record.
|
|
45
|
+
landing_page_zip_items = [
|
|
46
|
+
item
|
|
47
|
+
for item in landing_page_linkset["item"]
|
|
48
|
+
if item["type"] == "application/zip"
|
|
49
|
+
]
|
|
50
|
+
if len(landing_page_zip_items) != 1:
|
|
51
|
+
raise ReaderError(
|
|
52
|
+
f"Expected 1 ZIP item but got {len(landing_page_zip_items)}"
|
|
53
|
+
)
|
|
54
|
+
file_url = landing_page_zip_items[0]["href"]
|
|
55
|
+
|
|
56
|
+
# Download the ZIP file and fully load the response bytes content in memory.
|
|
57
|
+
# The bytes content are then wrapped by a BytesIO to be file-like object (as required by `zipfile.ZipFile`).
|
|
58
|
+
# Using directly `file_resp.raw` is not possible since `zipfile.ZipFile` requires the file-like object to be seekable.
|
|
59
|
+
file_resp = requests.get(file_url)
|
|
60
|
+
file_resp.raise_for_status()
|
|
61
|
+
yield io.BytesIO(file_resp.content)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
VOCABULARIES_DATASTREAM_READERS = {
|
|
65
|
+
"ror-http": RORHTTPReader,
|
|
66
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
3
|
# Copyright (C) 2022 CERN.
|
|
4
|
+
# Copyright (C) 2024 California Institute of Technology.
|
|
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
|
|
@@ -38,36 +39,70 @@ class RORTransformer(BaseTransformer):
|
|
|
38
39
|
"""Applies the transformation to the stream entry."""
|
|
39
40
|
record = stream_entry.entry
|
|
40
41
|
funder = {}
|
|
42
|
+
funder["title"] = {}
|
|
41
43
|
|
|
42
44
|
funder["id"] = normalize_ror(record.get("id"))
|
|
43
45
|
if not funder["id"]:
|
|
44
46
|
raise TransformerError(_("Id not found in ROR entry."))
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
aliases = []
|
|
49
|
+
acronym = None
|
|
50
|
+
for name in record.get("names"):
|
|
51
|
+
# Some name entries have a `lang` key with a `None` value.
|
|
52
|
+
# Therefore, providing a default value to `name.get("lang")` is not enough,
|
|
53
|
+
# and we need instead to check if the result of `get` is None.
|
|
54
|
+
lang = name.get("lang")
|
|
55
|
+
if lang is None:
|
|
56
|
+
lang = "en"
|
|
57
|
+
|
|
58
|
+
if "ror_display" in name["types"]:
|
|
59
|
+
funder["name"] = name["value"]
|
|
60
|
+
if "label" in name["types"]:
|
|
61
|
+
funder["title"][lang] = name["value"]
|
|
62
|
+
if "alias" in name["types"]:
|
|
63
|
+
aliases.append(name["value"])
|
|
64
|
+
if "acronym" in name["types"]:
|
|
65
|
+
# The first acronyn goes in acronym field to maintain
|
|
66
|
+
# compatability with existing data structure
|
|
67
|
+
if not acronym:
|
|
68
|
+
acronym = name["value"]
|
|
69
|
+
else:
|
|
70
|
+
aliases.append(name["value"])
|
|
71
|
+
if acronym:
|
|
72
|
+
funder["acronym"] = acronym
|
|
73
|
+
if aliases:
|
|
74
|
+
funder["aliases"] = aliases
|
|
75
|
+
|
|
76
|
+
# ror_display is required and should be in every entry
|
|
47
77
|
if not funder["name"]:
|
|
48
|
-
raise TransformerError(
|
|
78
|
+
raise TransformerError(
|
|
79
|
+
_("Name with type ror_display not found in ROR entry.")
|
|
80
|
+
)
|
|
49
81
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
82
|
+
# This only gets the first location, to maintain compatability
|
|
83
|
+
# with existing data structure
|
|
84
|
+
location = record.get("locations", [{}])[0].get("geonames_details", {})
|
|
85
|
+
funder["country"] = location.get("country_code")
|
|
86
|
+
funder["country_name"] = location.get("country_name")
|
|
87
|
+
funder["location_name"] = location.get("name")
|
|
53
88
|
|
|
54
|
-
funder["
|
|
55
|
-
|
|
56
|
-
|
|
89
|
+
funder["types"] = record.get("types")
|
|
90
|
+
|
|
91
|
+
status = record.get("status")
|
|
92
|
+
funder["status"] = status
|
|
57
93
|
|
|
58
94
|
# The ROR is always listed in identifiers, expected by serialization
|
|
59
95
|
funder["identifiers"] = [{"identifier": funder["id"], "scheme": "ror"}]
|
|
60
96
|
valid_schemes = set(funder_schemes.keys())
|
|
61
97
|
fund_ref = "fundref"
|
|
62
98
|
valid_schemes.add(fund_ref)
|
|
63
|
-
for
|
|
64
|
-
scheme =
|
|
99
|
+
for identifier in record.get("external_ids"):
|
|
100
|
+
scheme = identifier["type"]
|
|
65
101
|
if scheme in valid_schemes:
|
|
66
102
|
value = identifier.get("preferred") or identifier.get("all")[0]
|
|
67
103
|
if scheme == fund_ref:
|
|
68
104
|
value = f"{funder_fundref_doi_prefix}/{value}"
|
|
69
105
|
scheme = "doi"
|
|
70
|
-
|
|
71
106
|
funder["identifiers"].append(
|
|
72
107
|
{
|
|
73
108
|
"identifier": value,
|
|
@@ -96,7 +131,7 @@ DATASTREAM_CONFIG = {
|
|
|
96
131
|
{
|
|
97
132
|
"type": "zip",
|
|
98
133
|
"args": {
|
|
99
|
-
"regex": "
|
|
134
|
+
"regex": "_schema_v2\\.json$",
|
|
100
135
|
},
|
|
101
136
|
},
|
|
102
137
|
{"type": "json"},
|
|
@@ -9,7 +9,15 @@
|
|
|
9
9
|
},
|
|
10
10
|
"country": {
|
|
11
11
|
"type": "string",
|
|
12
|
-
"description": "Represents a funder's origin country."
|
|
12
|
+
"description": "Represents a funder's origin country as a country code."
|
|
13
|
+
},
|
|
14
|
+
"country_name": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Represents a funder's origin country as a full name."
|
|
17
|
+
},
|
|
18
|
+
"location_name": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "Represents a funder's location name (usually a city)."
|
|
13
21
|
},
|
|
14
22
|
"identifiers": {
|
|
15
23
|
"description": "Alternate identifiers for the record.",
|
|
@@ -23,6 +31,30 @@
|
|
|
23
31
|
"type": "string",
|
|
24
32
|
"description": "Funders name."
|
|
25
33
|
},
|
|
34
|
+
"acronym": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Acronym for funders name."
|
|
37
|
+
},
|
|
38
|
+
"status": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Status of the funder."
|
|
41
|
+
},
|
|
42
|
+
"aliases": {
|
|
43
|
+
"description": "Alternate names for the funder.",
|
|
44
|
+
"type": "array",
|
|
45
|
+
"items": {
|
|
46
|
+
"type": "string"
|
|
47
|
+
},
|
|
48
|
+
"uniqueItems": true
|
|
49
|
+
},
|
|
50
|
+
"types": {
|
|
51
|
+
"description": "Types of funders.",
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": {
|
|
54
|
+
"type": "string"
|
|
55
|
+
},
|
|
56
|
+
"uniqueItems": true
|
|
57
|
+
},
|
|
26
58
|
"title": {
|
|
27
59
|
"$ref": "local://vocabularies/definitions-v1.0.0.json#/title"
|
|
28
60
|
}
|
|
@@ -57,6 +57,24 @@
|
|
|
57
57
|
"country": {
|
|
58
58
|
"type": "text"
|
|
59
59
|
},
|
|
60
|
+
"country_name": {
|
|
61
|
+
"type": "text"
|
|
62
|
+
},
|
|
63
|
+
"location_name": {
|
|
64
|
+
"type": "text"
|
|
65
|
+
},
|
|
66
|
+
"acronym": {
|
|
67
|
+
"type": "text"
|
|
68
|
+
},
|
|
69
|
+
"status": {
|
|
70
|
+
"type": "keyword"
|
|
71
|
+
},
|
|
72
|
+
"aliases": {
|
|
73
|
+
"type": "text"
|
|
74
|
+
},
|
|
75
|
+
"types": {
|
|
76
|
+
"type": "keyword"
|
|
77
|
+
},
|
|
60
78
|
"id": {
|
|
61
79
|
"type": "keyword"
|
|
62
80
|
},
|
|
@@ -57,6 +57,24 @@
|
|
|
57
57
|
"country": {
|
|
58
58
|
"type": "text"
|
|
59
59
|
},
|
|
60
|
+
"country_name": {
|
|
61
|
+
"type": "text"
|
|
62
|
+
},
|
|
63
|
+
"location_name": {
|
|
64
|
+
"type": "text"
|
|
65
|
+
},
|
|
66
|
+
"acronym": {
|
|
67
|
+
"type": "text"
|
|
68
|
+
},
|
|
69
|
+
"status": {
|
|
70
|
+
"type": "keyword"
|
|
71
|
+
},
|
|
72
|
+
"aliases": {
|
|
73
|
+
"type": "text"
|
|
74
|
+
},
|
|
75
|
+
"types": {
|
|
76
|
+
"type": "keyword"
|
|
77
|
+
},
|
|
60
78
|
"id": {
|
|
61
79
|
"type": "keyword"
|
|
62
80
|
},
|
|
@@ -57,6 +57,24 @@
|
|
|
57
57
|
"country": {
|
|
58
58
|
"type": "text"
|
|
59
59
|
},
|
|
60
|
+
"country_name": {
|
|
61
|
+
"type": "text"
|
|
62
|
+
},
|
|
63
|
+
"location_name": {
|
|
64
|
+
"type": "text"
|
|
65
|
+
},
|
|
66
|
+
"acronym": {
|
|
67
|
+
"type": "text"
|
|
68
|
+
},
|
|
69
|
+
"status": {
|
|
70
|
+
"type": "keyword"
|
|
71
|
+
},
|
|
72
|
+
"aliases": {
|
|
73
|
+
"type": "text"
|
|
74
|
+
},
|
|
75
|
+
"types": {
|
|
76
|
+
"type": "keyword"
|
|
77
|
+
},
|
|
60
78
|
"id": {
|
|
61
79
|
"type": "keyword"
|
|
62
80
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
3
|
# Copyright (C) 2021-2022 CERN.
|
|
4
|
+
# Copyright (C) 2024 California Institute of Technology.
|
|
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
|
|
@@ -43,6 +44,8 @@ class FunderSchema(BaseVocabularySchema):
|
|
|
43
44
|
required=True, validate=validate.Length(min=1, error=_("Name cannot be blank."))
|
|
44
45
|
)
|
|
45
46
|
country = SanitizedUnicode()
|
|
47
|
+
country_name = SanitizedUnicode()
|
|
48
|
+
location_name = SanitizedUnicode()
|
|
46
49
|
identifiers = IdentifierSet(
|
|
47
50
|
fields.Nested(
|
|
48
51
|
partial(
|
|
@@ -57,6 +60,11 @@ class FunderSchema(BaseVocabularySchema):
|
|
|
57
60
|
validate=validate.Length(min=1, error=_("PID cannot be blank."))
|
|
58
61
|
)
|
|
59
62
|
|
|
63
|
+
acronym = SanitizedUnicode()
|
|
64
|
+
aliases = fields.List(SanitizedUnicode())
|
|
65
|
+
status = SanitizedUnicode()
|
|
66
|
+
types = fields.List(SanitizedUnicode())
|
|
67
|
+
|
|
60
68
|
@validates_schema
|
|
61
69
|
def validate_id(self, data, **kwargs):
|
|
62
70
|
"""Validates ID."""
|
|
@@ -15,7 +15,6 @@ import re
|
|
|
15
15
|
import tarfile
|
|
16
16
|
import zipfile
|
|
17
17
|
from abc import ABC, abstractmethod
|
|
18
|
-
from collections import defaultdict
|
|
19
18
|
from json.decoder import JSONDecodeError
|
|
20
19
|
|
|
21
20
|
import requests
|
|
@@ -80,7 +79,12 @@ class TarReader(BaseReader):
|
|
|
80
79
|
def read(self, item=None, *args, **kwargs):
|
|
81
80
|
"""Opens a tar archive or uses the given file pointer."""
|
|
82
81
|
if item:
|
|
83
|
-
|
|
82
|
+
if isinstance(item, tarfile.TarFile):
|
|
83
|
+
yield from self._iter(fp=item, *args, **kwargs)
|
|
84
|
+
else:
|
|
85
|
+
# If the item is not already a TarFile (e.g. if it is a BytesIO), try to create a TarFile from the item.
|
|
86
|
+
with tarfile.open(mode=self._mode, fileobj=item) as archive:
|
|
87
|
+
yield from self._iter(fp=archive, *args, **kwargs)
|
|
84
88
|
else:
|
|
85
89
|
with tarfile.open(self._origin, self._mode) as archive:
|
|
86
90
|
yield from self._iter(fp=archive, *args, **kwargs)
|
|
@@ -136,7 +140,12 @@ class ZipReader(BaseReader):
|
|
|
136
140
|
"""Opens a Zip archive or uses the given file pointer."""
|
|
137
141
|
# https://docs.python.org/3/library/zipfile.html
|
|
138
142
|
if item:
|
|
139
|
-
|
|
143
|
+
if isinstance(item, zipfile.ZipFile):
|
|
144
|
+
yield from self._iter(fp=item, *args, **kwargs)
|
|
145
|
+
else:
|
|
146
|
+
# If the item is not already a ZipFile (e.g. if it is a BytesIO), try to create a ZipFile from the item.
|
|
147
|
+
with zipfile.ZipFile(item, **self._options) as archive:
|
|
148
|
+
yield from self._iter(fp=archive, *args, **kwargs)
|
|
140
149
|
else:
|
|
141
150
|
with zipfile.ZipFile(self._origin, **self._options) as archive:
|
|
142
151
|
yield from self._iter(fp=archive, *args, **kwargs)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2024 CERN.
|
|
4
|
+
# Copyright (C) 2024 KTH Royal Institute of Technology.
|
|
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
|
+
"""Generate Vocabulary Config."""
|
|
10
|
+
from copy import deepcopy
|
|
11
|
+
|
|
12
|
+
import yaml
|
|
13
|
+
from invenio_records_resources.proxies import current_service_registry
|
|
14
|
+
|
|
15
|
+
from .contrib.awards.datastreams import DATASTREAM_CONFIG as awards_ds_config
|
|
16
|
+
from .contrib.funders.datastreams import DATASTREAM_CONFIG as funders_ds_config
|
|
17
|
+
from .contrib.names.datastreams import DATASTREAM_CONFIG as names_ds_config
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class VocabularyConfig:
|
|
21
|
+
"""Vocabulary Config Factory."""
|
|
22
|
+
|
|
23
|
+
config = None
|
|
24
|
+
vocabulary_name = None
|
|
25
|
+
|
|
26
|
+
def get_config(self, filepath=None, origin=None):
|
|
27
|
+
"""Get the configuration for the vocabulary."""
|
|
28
|
+
config = deepcopy(self.config)
|
|
29
|
+
if filepath:
|
|
30
|
+
with open(filepath, encoding="utf-8") as f:
|
|
31
|
+
config = yaml.safe_load(f).get(self.vocabulary_name)
|
|
32
|
+
if origin:
|
|
33
|
+
config["readers"][0].setdefault("args", {})
|
|
34
|
+
config["readers"][0]["args"]["origin"] = origin
|
|
35
|
+
return config
|
|
36
|
+
|
|
37
|
+
def get_service(self):
|
|
38
|
+
"""Get the service for the vocabulary."""
|
|
39
|
+
return current_service_registry.get(self.vocabulary_name)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class NamesVocabularyConfig(VocabularyConfig):
|
|
43
|
+
"""Names Vocabulary Config."""
|
|
44
|
+
|
|
45
|
+
config = names_ds_config
|
|
46
|
+
vocabulary_name = "names"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class FundersVocabularyConfig(VocabularyConfig):
|
|
50
|
+
"""Funders Vocabulary Config."""
|
|
51
|
+
|
|
52
|
+
config = funders_ds_config
|
|
53
|
+
vocabulary_name = "funders"
|
|
54
|
+
|
|
55
|
+
def get_service(self):
|
|
56
|
+
"""Get the service for the vocabulary."""
|
|
57
|
+
raise NotImplementedError("Service not implemented for Funders")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class AwardsVocabularyConfig(VocabularyConfig):
|
|
61
|
+
"""Awards Vocabulary Config."""
|
|
62
|
+
|
|
63
|
+
config = awards_ds_config
|
|
64
|
+
vocabulary_name = "awards"
|
|
65
|
+
|
|
66
|
+
def get_service(self):
|
|
67
|
+
"""Get the service for the vocabulary."""
|
|
68
|
+
raise NotImplementedError("Service not implemented for Awards")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_vocabulary_config(vocabulary):
|
|
72
|
+
"""Factory function to get the appropriate Vocabulary Config."""
|
|
73
|
+
vocab_config = {
|
|
74
|
+
"names": NamesVocabularyConfig,
|
|
75
|
+
"funders": FundersVocabularyConfig,
|
|
76
|
+
"awards": AwardsVocabularyConfig,
|
|
77
|
+
}
|
|
78
|
+
return vocab_config.get(vocabulary, VocabularyConfig)()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
#
|
|
3
|
-
# Copyright (C) 2020-
|
|
3
|
+
# Copyright (C) 2020-2022 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,9 +35,6 @@ 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
|
-
|
|
41
38
|
|
|
42
39
|
class VocabularyRelationSchema(BaseVocabularyRelationSchema):
|
|
43
40
|
"""Vocabulary relation schema."""
|
|
@@ -66,9 +63,6 @@ class ContribVocabularyRelationSchema(Schema):
|
|
|
66
63
|
ftf_name = None # free text field name
|
|
67
64
|
parent_field_name = None
|
|
68
65
|
|
|
69
|
-
# Nested field type for administration UI form generation
|
|
70
|
-
administration_schema_type = "vocabulary"
|
|
71
|
-
|
|
72
66
|
@validates_schema
|
|
73
67
|
def validate_relation_schema(self, data, **kwargs):
|
|
74
68
|
"""Validates that either id either the free text field are present."""
|
|
@@ -97,9 +91,6 @@ class BaseVocabularySchema(BaseRecordSchema):
|
|
|
97
91
|
description = i18n_strings
|
|
98
92
|
icon = fields.Str(allow_none=False)
|
|
99
93
|
|
|
100
|
-
# Nested field type for administration UI form generation
|
|
101
|
-
administration_schema_type = "vocabulary"
|
|
102
|
-
|
|
103
94
|
|
|
104
95
|
class VocabularySchema(BaseVocabularySchema):
|
|
105
96
|
"""Service schema for vocabulary records."""
|
|
@@ -11,6 +11,7 @@ from celery import shared_task
|
|
|
11
11
|
from flask import current_app
|
|
12
12
|
|
|
13
13
|
from ..datastreams.factories import DataStreamFactory
|
|
14
|
+
from ..factories import get_vocabulary_config
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
@shared_task(ignore_result=True)
|
|
@@ -26,3 +27,32 @@ def process_datastream(config):
|
|
|
26
27
|
if result.errors:
|
|
27
28
|
for err in result.errors:
|
|
28
29
|
current_app.logger.error(err)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@shared_task()
|
|
33
|
+
def import_funders():
|
|
34
|
+
"""Import the funders vocabulary.
|
|
35
|
+
|
|
36
|
+
Only new records are imported.
|
|
37
|
+
Existing records are not updated.
|
|
38
|
+
"""
|
|
39
|
+
vc = get_vocabulary_config("funders")
|
|
40
|
+
config = vc.get_config()
|
|
41
|
+
|
|
42
|
+
# When importing funders via a Celery task, make sure that we are automatically downloading the ROR file,
|
|
43
|
+
# instead of relying on a local file on the file system.
|
|
44
|
+
if config["readers"][0]["type"] == "ror-http":
|
|
45
|
+
readers_config_with_ror_http = config["readers"]
|
|
46
|
+
else:
|
|
47
|
+
readers_config_with_ror_http = [{"type": "ror-http"}] + config["readers"]
|
|
48
|
+
|
|
49
|
+
ds = DataStreamFactory.create(
|
|
50
|
+
readers_config=readers_config_with_ror_http,
|
|
51
|
+
transformers_config=config.get("transformers"),
|
|
52
|
+
writers_config=config["writers"],
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
for result in ds.process():
|
|
56
|
+
if result.errors:
|
|
57
|
+
for err in result.errors:
|
|
58
|
+
current_app.logger.error(err)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: invenio-vocabularies
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: "Invenio module for managing vocabularies."
|
|
5
5
|
Home-page: https://github.com/inveniosoftware/invenio-vocabularies
|
|
6
6
|
Author: CERN
|
|
@@ -11,7 +11,7 @@ Platform: any
|
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Requires-Python: >=3.7
|
|
13
13
|
Requires-Dist: invenio-i18n <3.0.0,>=2.0.0
|
|
14
|
-
Requires-Dist: invenio-records-resources <
|
|
14
|
+
Requires-Dist: invenio-records-resources <7.0.0,>=6.0.0
|
|
15
15
|
Requires-Dist: lxml >=4.5.0
|
|
16
16
|
Requires-Dist: PyYAML >=5.4.1
|
|
17
17
|
Provides-Extra: elasticsearch7
|
|
@@ -74,14 +74,15 @@ https://invenio-vocabularies.readthedocs.io/
|
|
|
74
74
|
Changes
|
|
75
75
|
=======
|
|
76
76
|
|
|
77
|
-
Version
|
|
78
|
-
|
|
79
|
-
- schema: add administration UI attributes
|
|
80
|
-
|
|
81
|
-
Version 3.4.1 (released 2024-04-19)
|
|
82
|
-
|
|
83
|
-
- funders: exclude ROR schema v2 json (#309)
|
|
77
|
+
Version 4.0.0 (released 2024-06-04)
|
|
84
78
|
|
|
79
|
+
- datastreams: implement factories for generating vocabulary configurations
|
|
80
|
+
- datastreams: added ROR HTTP reader
|
|
81
|
+
- funders: use ROR v2 dump instead of v1
|
|
82
|
+
- datastreams: added celery task for funders using ROR HTTP reader
|
|
83
|
+
- datastreams: add OpenAIRE Project HTTP Reader
|
|
84
|
+
- datastreams: fix OpenAIRE graph dataset parsing
|
|
85
|
+
- installation: upgrade invenio-records-resources
|
|
85
86
|
|
|
86
87
|
Version 3.4.0 (released 2024-04-19)
|
|
87
88
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
invenio_vocabularies/__init__.py,sha256=
|
|
2
|
-
invenio_vocabularies/cli.py,sha256=
|
|
1
|
+
invenio_vocabularies/__init__.py,sha256=V5z9SuZ3KH-yIAm6O_EOrw27GQcnW3S76XWEknRVwBU,377
|
|
2
|
+
invenio_vocabularies/cli.py,sha256=9Gfm3jjNDX3oQnYXupxSuqwXvWnJmsQ9GmyekJ1h5Kg,5239
|
|
3
3
|
invenio_vocabularies/config.py,sha256=mLypkeVrPKZPtokvHSF-_Q7YcV4sCVONiyhGhu-34hI,3772
|
|
4
4
|
invenio_vocabularies/ext.py,sha256=ukuvkhkLPBy2AITFLojLYTIUlP2qcbHNkt6ES8i1TwY,5310
|
|
5
|
+
invenio_vocabularies/factories.py,sha256=iz1MzCwzZoj0cSxlYmr_RWSOL6gRXKHqJnFznshpcvQ,2421
|
|
5
6
|
invenio_vocabularies/fixtures.py,sha256=nNWwH04HFASjfj1oy5kMdcQGKmVjzUuA5wSw-ER1QAg,1585
|
|
6
7
|
invenio_vocabularies/proxies.py,sha256=H_bcJXPTwGbErx-pF89ChayOZcXJXJGZW7O0xkJkYQ4,693
|
|
7
8
|
invenio_vocabularies/views.py,sha256=JCUA6yzF_160U1oAFUIgGt4MigsLQ9MiHOInHBQw98M,1381
|
|
@@ -23,7 +24,7 @@ invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/index.js,sha
|
|
|
23
24
|
invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/index.js,sha256=iSx-bdQkKj6XA9NAam31bdcQmFygljQnjLcFjjK3lwU,245
|
|
24
25
|
invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/index.js,sha256=7sSg482yJODQHU4jkP-hWJjpBOw7ubFr5nPZl5D_1gQ,262
|
|
25
26
|
invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/AwardResults.js,sha256=AgqJg9GEcJvKZR4plZsH0j7cm9C3yjT9YCPI6uvmOyc,3499
|
|
26
|
-
invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/CustomAwardForm.js,sha256=
|
|
27
|
+
invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/CustomAwardForm.js,sha256=DTEt6SV56bc7LC1KczUixHkdBUF3lGQs8rCc-YLii4Q,4804
|
|
27
28
|
invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FunderDropdown.js,sha256=pPMB9Hirc8z3efquecrO_CKlfXE1ws6OqDB7fB30l5k,2431
|
|
28
29
|
invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.js,sha256=aqQ92Z0WJ4rfXz0E8KIIN4-qjORdQYxXPa5eS5b_g8A,6912
|
|
29
30
|
invenio_vocabularies/assets/semantic-ui/js/invenio_vocabularies/src/contrib/forms/Funding/FundingField.test.js,sha256=z_4lWkf3h9Uin8OzSBSKY1wpUxtbnjE2kRUjW80jAhA,35
|
|
@@ -60,7 +61,7 @@ invenio_vocabularies/contrib/awards/__init__.py,sha256=KwCmwFalz-3pDs9iTa5TKUidB
|
|
|
60
61
|
invenio_vocabularies/contrib/awards/api.py,sha256=OXukE7PLXs45BTtqVrhvGBNqLmQaI-CgXmHTCi36LZk,303
|
|
61
62
|
invenio_vocabularies/contrib/awards/awards.py,sha256=N-BNE2ffUu-IlfKFBjMjfYqxL0vmpO8YMPe4q4e-rTo,2695
|
|
62
63
|
invenio_vocabularies/contrib/awards/config.py,sha256=PlDHabkWDUzwa1Fvk_U2hG83kQYBqM1IyChg8Yg_VlY,1630
|
|
63
|
-
invenio_vocabularies/contrib/awards/datastreams.py,sha256=
|
|
64
|
+
invenio_vocabularies/contrib/awards/datastreams.py,sha256=4BRifAmmEw0fUxo9JK5ZUXfSrSv1wllPvuGVOfick54,8244
|
|
64
65
|
invenio_vocabularies/contrib/awards/models.py,sha256=mM-kSNf7kDH3oIbV8epxxbUi7muYqi4JreXxgWXlVzw,318
|
|
65
66
|
invenio_vocabularies/contrib/awards/resources.py,sha256=_9YTqbhz8axFXGhG5y4WyjE27p9n-7e3c6HoBRditPA,411
|
|
66
67
|
invenio_vocabularies/contrib/awards/schema.py,sha256=bg-09nvhEn-ipzaztK1iNbKc59saAM8v8hAW9zpKDAc,2786
|
|
@@ -75,26 +76,29 @@ invenio_vocabularies/contrib/awards/mappings/os-v2/__init__.py,sha256=9gRLFRtjhN
|
|
|
75
76
|
invenio_vocabularies/contrib/awards/mappings/os-v2/awards/award-v1.0.0.json,sha256=KBAVvfuZDe_K8YISgbKCcpCZVVh-vkQIbt00OP1MPZA,1477
|
|
76
77
|
invenio_vocabularies/contrib/awards/mappings/v7/__init__.py,sha256=fERdPp7K7ajaoUu_4_HVLfF-WD_qcl7QgST6yGb68s4,253
|
|
77
78
|
invenio_vocabularies/contrib/awards/mappings/v7/awards/award-v1.0.0.json,sha256=KBAVvfuZDe_K8YISgbKCcpCZVVh-vkQIbt00OP1MPZA,1477
|
|
79
|
+
invenio_vocabularies/contrib/common/__init__.py,sha256=DdbGYRthEpQtObhY_YK4vOT9Zf7FFagQ6qtUjJOYw-M,247
|
|
80
|
+
invenio_vocabularies/contrib/common/ror/__init__.py,sha256=3u2-fre1SQ-4nz3Ay0nxj3ntmMZ8Ujh_4eV-fyxfmtc,239
|
|
81
|
+
invenio_vocabularies/contrib/common/ror/datastreams.py,sha256=osd_-QarJkzsPQAAAK4LEs7BJLNtWUCGGuq1yztnnIU,2665
|
|
78
82
|
invenio_vocabularies/contrib/funders/__init__.py,sha256=YxFXBDnT7NM8rFwxT_Ge3xXR2n17EM0alknQq7r_Bt8,478
|
|
79
83
|
invenio_vocabularies/contrib/funders/api.py,sha256=QKGGeSnPHSoBfucvpaVruXT_txYidofZ080G3IxFkIo,306
|
|
80
|
-
invenio_vocabularies/contrib/funders/config.py,sha256
|
|
81
|
-
invenio_vocabularies/contrib/funders/datastreams.py,sha256=
|
|
84
|
+
invenio_vocabularies/contrib/funders/config.py,sha256=BbzCRIcPxSwDllhjnGmdec5A126Zs1I4ZyVUCGi3jRA,1756
|
|
85
|
+
invenio_vocabularies/contrib/funders/datastreams.py,sha256=ezBtXebBmUmlPSLD8hA8BuR55kREtGShWeKTVXa3QUk,4995
|
|
82
86
|
invenio_vocabularies/contrib/funders/facets.py,sha256=a068TVtt74Ncu0latb177LFK8EdnpbMOWecAKozA04M,1245
|
|
83
87
|
invenio_vocabularies/contrib/funders/funders.py,sha256=nHqUoyRRW9oruN-ghmwzb9QIz_jyBgxNUpk41yvcso8,2292
|
|
84
88
|
invenio_vocabularies/contrib/funders/models.py,sha256=RAU-_YVOUNVCn03_XGJ2czcVwXTaZPk5w7X_bMAgMOk,314
|
|
85
89
|
invenio_vocabularies/contrib/funders/resources.py,sha256=He4gXd737ovdrHL-HB9dX7AGxp1BVJ9QteIO7JWUVSE,415
|
|
86
|
-
invenio_vocabularies/contrib/funders/schema.py,sha256=
|
|
90
|
+
invenio_vocabularies/contrib/funders/schema.py,sha256=tEOdMU2i0z_3PkXL1yD1qIJeoAy_8n22xxv8V7iltz0,2480
|
|
87
91
|
invenio_vocabularies/contrib/funders/serializer.py,sha256=UwFsPnQiMUWx2lMQgh3Bv1uosubTUF5GFIAxwECNoAk,927
|
|
88
92
|
invenio_vocabularies/contrib/funders/services.py,sha256=M-kQwtVOGygHe44vXr1L4E5Vvnpoco4KO0LRYwzhvMc,375
|
|
89
93
|
invenio_vocabularies/contrib/funders/jsonschemas/__init__.py,sha256=O4XwUXCk-gx_K2LDVJOLkq07-ibsUkpT1vPaebgd0Gk,247
|
|
90
|
-
invenio_vocabularies/contrib/funders/jsonschemas/funders/funder-v1.0.0.json,sha256=
|
|
94
|
+
invenio_vocabularies/contrib/funders/jsonschemas/funders/funder-v1.0.0.json,sha256=5Pyqf5ig16D2OnrFf1I3ws9PIShUPtZBrT8O-bfIMbw,1598
|
|
91
95
|
invenio_vocabularies/contrib/funders/mappings/__init__.py,sha256=aSr-tZd9rsjet6leeS336gdSdZHXwZKdaPStNtVNQVk,244
|
|
92
96
|
invenio_vocabularies/contrib/funders/mappings/os-v1/__init__.py,sha256=xXEX3tacmXp0I1KFtDw7ohIahozd2oIGp1UN40IhFic,251
|
|
93
|
-
invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v1.0.0.json,sha256=
|
|
97
|
+
invenio_vocabularies/contrib/funders/mappings/os-v1/funders/funder-v1.0.0.json,sha256=HtYnmonywSGEov-1twath81ugIUFj-kV2oIsWB0JG5w,1595
|
|
94
98
|
invenio_vocabularies/contrib/funders/mappings/os-v2/__init__.py,sha256=YvMRlKYTnEmyTzI9smZp_lO3w-zcK-8IpqT-jGUXEEY,251
|
|
95
|
-
invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v1.0.0.json,sha256=
|
|
99
|
+
invenio_vocabularies/contrib/funders/mappings/os-v2/funders/funder-v1.0.0.json,sha256=HtYnmonywSGEov-1twath81ugIUFj-kV2oIsWB0JG5w,1595
|
|
96
100
|
invenio_vocabularies/contrib/funders/mappings/v7/__init__.py,sha256=yFHmi3QYD65YKzLU5vMEtwAZn0gwkFYa6Db_tSbHjKE,254
|
|
97
|
-
invenio_vocabularies/contrib/funders/mappings/v7/funders/funder-v1.0.0.json,sha256=
|
|
101
|
+
invenio_vocabularies/contrib/funders/mappings/v7/funders/funder-v1.0.0.json,sha256=HtYnmonywSGEov-1twath81ugIUFj-kV2oIsWB0JG5w,1595
|
|
98
102
|
invenio_vocabularies/contrib/names/__init__.py,sha256=DBfsM7JMETZGaV5QmXEwE7zhCaAXvc2SZN6uXnW_V-c,451
|
|
99
103
|
invenio_vocabularies/contrib/names/api.py,sha256=sEPn_jFX3gyoxgbdEUSIvOoPCUI8pocI6qCZO6mzCgQ,300
|
|
100
104
|
invenio_vocabularies/contrib/names/config.py,sha256=hKDTEEBYGYOY6sMOArZjjkq2HJ6MJtRZp1geGLAFgRg,1735
|
|
@@ -135,7 +139,7 @@ invenio_vocabularies/datastreams/__init__.py,sha256=VPefh6k4Q3eYxKIW8I5zXUGucntp
|
|
|
135
139
|
invenio_vocabularies/datastreams/datastreams.py,sha256=_jSXv2yAvSjt8btMoLJlqXOkqBzYb3Xe9m2GH50Nwag,3987
|
|
136
140
|
invenio_vocabularies/datastreams/errors.py,sha256=IDUZ3gNtYGrhcOgApHCms1gNNJTyJzoMPmG5JtIeYNU,678
|
|
137
141
|
invenio_vocabularies/datastreams/factories.py,sha256=js76i-cjcEzslgemsrwSzXQMy3SXtY3iFcsO-OlrMIM,2182
|
|
138
|
-
invenio_vocabularies/datastreams/readers.py,sha256=
|
|
142
|
+
invenio_vocabularies/datastreams/readers.py,sha256=M4CRKyj7GH4iMernMqNRWgPlXjgVZTWD2X-Tp4spxEA,7409
|
|
139
143
|
invenio_vocabularies/datastreams/transformers.py,sha256=wspny-kazYMRHjkkyFfRVNIYzJxbjAqokRCBQ_-gXcY,1357
|
|
140
144
|
invenio_vocabularies/datastreams/writers.py,sha256=a85RYE6FAQqNF7obmlqHLW_ztJ1a6biS_i9a_yaKJw4,3615
|
|
141
145
|
invenio_vocabularies/datastreams/xml.py,sha256=HFa-lfxj7kFrr2IjeN1jxSLDfcvpBwO9nZLZF2-BryE,997
|
|
@@ -165,9 +169,9 @@ invenio_vocabularies/services/components.py,sha256=d9C-24dEDM63gFm75nU-dXrrjS2zZ
|
|
|
165
169
|
invenio_vocabularies/services/facets.py,sha256=qvdHoGSJJr90dZHSVe0-hlO1r0LtTnFVSjrt9PNuNAg,3872
|
|
166
170
|
invenio_vocabularies/services/permissions.py,sha256=one3NvNFYq-q15e6xxf85OkH1bWZ5OsvJqMnNbm3Qms,696
|
|
167
171
|
invenio_vocabularies/services/querystr.py,sha256=X3JHVF9B0O0iLWrnW3ok_bf_8jA-Cs_oAcYYkGOm3Uw,1829
|
|
168
|
-
invenio_vocabularies/services/schema.py,sha256
|
|
172
|
+
invenio_vocabularies/services/schema.py,sha256=ShnnH_ILHZGxE546J6Jsdwdeix6jLubSRomzf472DK8,4307
|
|
169
173
|
invenio_vocabularies/services/service.py,sha256=W3wtKOttQjOr8Nkaus6m3KRuCMBqBsWUCAVv7Dj8bvM,7392
|
|
170
|
-
invenio_vocabularies/services/tasks.py,sha256=
|
|
174
|
+
invenio_vocabularies/services/tasks.py,sha256=rk-foPEhLL8kKoi8EPuUIJSjLX07NM3Fx8KjuY6Kqyk,1767
|
|
171
175
|
invenio_vocabularies/services/custom_fields/__init__.py,sha256=QgvSsn-S1xLzbZ57pjjGTt5oI3HqzXHVjwGTtuPgzN8,421
|
|
172
176
|
invenio_vocabularies/services/custom_fields/subject.py,sha256=k-qXbCjXPTk-Xo_W5sojFpQbkntv50odROiNyJHMzL8,2234
|
|
173
177
|
invenio_vocabularies/services/custom_fields/vocabulary.py,sha256=eCvqrNloMMCCvqR49IQwzk2p4xapx5_bmQhd6ByJZFM,3019
|
|
@@ -267,10 +271,10 @@ invenio_vocabularies/translations/zh_CN/LC_MESSAGES/messages.mo,sha256=g1I5aNO8r
|
|
|
267
271
|
invenio_vocabularies/translations/zh_CN/LC_MESSAGES/messages.po,sha256=vg8qC8ofpAdJ3mQz7mWM1ylKDpiNWXFs7rlMdSPkgKk,4629
|
|
268
272
|
invenio_vocabularies/translations/zh_TW/LC_MESSAGES/messages.mo,sha256=cqSm8NtMAwrP9O6qbmtkDtRT1e9D93qpsJN5X9_PPVw,600
|
|
269
273
|
invenio_vocabularies/translations/zh_TW/LC_MESSAGES/messages.po,sha256=9ACePz_EpB-LfcIJajZ2kp8Q04tcdrQLOtug162ZUss,4115
|
|
270
|
-
invenio_vocabularies-
|
|
271
|
-
invenio_vocabularies-
|
|
272
|
-
invenio_vocabularies-
|
|
273
|
-
invenio_vocabularies-
|
|
274
|
-
invenio_vocabularies-
|
|
275
|
-
invenio_vocabularies-
|
|
276
|
-
invenio_vocabularies-
|
|
274
|
+
invenio_vocabularies-4.0.0.dist-info/AUTHORS.rst,sha256=8d0p_WWE1r9DavvzMDi2D4YIGBHiMYcN3LYxqQOj8sY,291
|
|
275
|
+
invenio_vocabularies-4.0.0.dist-info/LICENSE,sha256=UvI8pR8jGWqe0sTkb_hRG6eIrozzWwWzyCGEpuXX4KE,1062
|
|
276
|
+
invenio_vocabularies-4.0.0.dist-info/METADATA,sha256=qTKnTHUD2J1jWkNiPAHYhDGDBLQaVvhT_j02lM1KtrE,6950
|
|
277
|
+
invenio_vocabularies-4.0.0.dist-info/WHEEL,sha256=-G_t0oGuE7UD0DrSpVZnq1hHMBV9DD2XkS5v7XpmTnk,110
|
|
278
|
+
invenio_vocabularies-4.0.0.dist-info/entry_points.txt,sha256=wQd9reAcv3btbEyMD9FHdkMbM7GvYDJiGugCuqc14sA,2589
|
|
279
|
+
invenio_vocabularies-4.0.0.dist-info/top_level.txt,sha256=x1gRNbaODF_bCD0SBLM3nVOFPGi06cmGX5X94WKrFKk,21
|
|
280
|
+
invenio_vocabularies-4.0.0.dist-info/RECORD,,
|
{invenio_vocabularies-3.4.2.dist-info → invenio_vocabularies-4.0.0.dist-info}/entry_points.txt
RENAMED
|
@@ -28,6 +28,9 @@ invenio_vocabularies_ext = invenio_vocabularies.views:blueprint
|
|
|
28
28
|
[invenio_base.finalize_app]
|
|
29
29
|
invenio_vocabularies = invenio_vocabularies.ext:finalize_app
|
|
30
30
|
|
|
31
|
+
[invenio_celery.tasks]
|
|
32
|
+
invenio_vocabularies_services = invenio_vocabularies.services.tasks
|
|
33
|
+
|
|
31
34
|
[invenio_db.alembic]
|
|
32
35
|
invenio_vocabularies = invenio_vocabularies:alembic
|
|
33
36
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|