ckanext-validation-schema-generator 2.0.2__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.
- bin/extract-id.py +5 -0
- ckanext/__init__.py +9 -0
- ckanext/validation_schema_generator/__init__.py +0 -0
- ckanext/validation_schema_generator/assets/css/validation_schema_generator.css +27 -0
- ckanext/validation_schema_generator/assets/js/validation_schema_generator.js +0 -0
- ckanext/validation_schema_generator/assets/webassets.yml +14 -0
- ckanext/validation_schema_generator/ckan_dataset.json +144 -0
- ckanext/validation_schema_generator/constants.py +30 -0
- ckanext/validation_schema_generator/controller.py +24 -0
- ckanext/validation_schema_generator/helpers.py +14 -0
- ckanext/validation_schema_generator/jobs.py +87 -0
- ckanext/validation_schema_generator/logic/__init__.py +0 -0
- ckanext/validation_schema_generator/logic/action.py +183 -0
- ckanext/validation_schema_generator/logic/auth.py +28 -0
- ckanext/validation_schema_generator/logic/schema.py +43 -0
- ckanext/validation_schema_generator/logic/validators.py +71 -0
- ckanext/validation_schema_generator/plugin.py +56 -0
- ckanext/validation_schema_generator/presets.json +16 -0
- ckanext/validation_schema_generator/templates/base.html +8 -0
- ckanext/validation_schema_generator/templates/package/resource_edit_base.html +9 -0
- ckanext/validation_schema_generator/templates/snippets/vsg_asset.html +2 -0
- ckanext/validation_schema_generator/templates/snippets/vsg_resource.html +2 -0
- ckanext/validation_schema_generator/templates/vsg/index.html +86 -0
- ckanext/validation_schema_generator/tests/__init__.py +0 -0
- ckanext/validation_schema_generator/tests/conftest.py +57 -0
- ckanext/validation_schema_generator/tests/test_action.py +264 -0
- ckanext/validation_schema_generator/tests/test_auth.py +34 -0
- ckanext/validation_schema_generator/tests/test_schema.json +117 -0
- ckanext/validation_schema_generator/utils.py +77 -0
- ckanext/validation_schema_generator/views/__init__.py +159 -0
- ckanext_validation_schema_generator-2.0.2.dist-info/METADATA +224 -0
- ckanext_validation_schema_generator-2.0.2.dist-info/RECORD +38 -0
- ckanext_validation_schema_generator-2.0.2.dist-info/WHEEL +5 -0
- ckanext_validation_schema_generator-2.0.2.dist-info/entry_points.txt +5 -0
- ckanext_validation_schema_generator-2.0.2.dist-info/licenses/LICENSE +661 -0
- ckanext_validation_schema_generator-2.0.2.dist-info/top_level.txt +3 -0
- test/features/environment.py +107 -0
- test/features/steps/steps.py +641 -0
bin/extract-id.py
ADDED
ckanext/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.schema-generate .alert {
|
|
2
|
+
margin-bottom: 20px;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.generate-schema-form .btn-generate {
|
|
6
|
+
margin-bottom: 20px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.generate-schema-form .table-schema {
|
|
10
|
+
margin-bottom: 30px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.generate-schema-form .with-textarea .form-group {
|
|
14
|
+
margin: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.generate-schema-form .with-textarea .form-group.control-select {
|
|
18
|
+
margin-bottom: 20px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.generate-schema-form .with-textarea .form-group textarea {
|
|
22
|
+
min-height: 300px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.generate-schema-form .with-textarea .control-label {
|
|
26
|
+
display: none;
|
|
27
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
vsg_js:
|
|
2
|
+
filter: rjsmin
|
|
3
|
+
output: ckanext-validation_schema_generator/%(version)s-validation_schema_generator.js
|
|
4
|
+
contents:
|
|
5
|
+
- js/validation_schema_generator.js
|
|
6
|
+
extra:
|
|
7
|
+
preload:
|
|
8
|
+
- base/main
|
|
9
|
+
|
|
10
|
+
vsg_css:
|
|
11
|
+
filter: cssrewrite
|
|
12
|
+
output: ckanext-validation_schema_generator/%(version)s-validation_schema_generator.css
|
|
13
|
+
contents:
|
|
14
|
+
- css/validation_schema_generator.css
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
{
|
|
2
|
+
"scheming_version": 1,
|
|
3
|
+
"dataset_type": "dataset",
|
|
4
|
+
"about": "A reimplementation of the default CKAN dataset schema",
|
|
5
|
+
"about_url": "http://github.com/ckan/ckanext-scheming",
|
|
6
|
+
"dataset_fields": [
|
|
7
|
+
{
|
|
8
|
+
"field_name": "title",
|
|
9
|
+
"label": "Title",
|
|
10
|
+
"preset": "title",
|
|
11
|
+
"form_placeholder": "eg. A descriptive title"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"field_name": "name",
|
|
15
|
+
"label": "URL",
|
|
16
|
+
"preset": "dataset_slug",
|
|
17
|
+
"form_placeholder": "eg. my-dataset"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"field_name": "notes",
|
|
21
|
+
"label": "Description",
|
|
22
|
+
"form_snippet": "markdown.html",
|
|
23
|
+
"form_placeholder": "eg. Some useful notes about the data",
|
|
24
|
+
"required": true
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"field_name": "tag_string",
|
|
28
|
+
"label": "Tags",
|
|
29
|
+
"preset": "tag_string_autocomplete",
|
|
30
|
+
"form_placeholder": "eg. economy, mental health, government"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"field_name": "license_id",
|
|
34
|
+
"label": "License",
|
|
35
|
+
"form_snippet": "license.html",
|
|
36
|
+
"help_text": "License definitions and additional information can be found at http://opendefinition.org/",
|
|
37
|
+
"required": true
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"field_name": "owner_org",
|
|
41
|
+
"label": "Organization",
|
|
42
|
+
"preset": "dataset_organization"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"field_name": "url",
|
|
46
|
+
"label": "Source",
|
|
47
|
+
"form_placeholder": "http://example.com/dataset.json",
|
|
48
|
+
"display_property": "foaf:homepage",
|
|
49
|
+
"display_snippet": "link.html"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"field_name": "version",
|
|
53
|
+
"label": "Version",
|
|
54
|
+
"validators": "scheming_required unicode_safe package_version_validator",
|
|
55
|
+
"form_placeholder": "1.0",
|
|
56
|
+
"required": true
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"field_name": "author",
|
|
60
|
+
"label": "Author",
|
|
61
|
+
"form_placeholder": "Joe Bloggs",
|
|
62
|
+
"display_property": "dc:creator",
|
|
63
|
+
"display_snippet": null,
|
|
64
|
+
"form_snippet": null
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"field_name": "author_email",
|
|
68
|
+
"label": "Email",
|
|
69
|
+
"form_placeholder": "joe@example.com",
|
|
70
|
+
"display_property": "dc:creator",
|
|
71
|
+
"display_snippet": "email.html",
|
|
72
|
+
"display_email_name_field": "author",
|
|
73
|
+
"required": true
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"field_name": "maintainer",
|
|
77
|
+
"label": "Maintainer",
|
|
78
|
+
"form_placeholder": "Joe Bloggs",
|
|
79
|
+
"display_property": "dc:contributor",
|
|
80
|
+
"display_snippet": null,
|
|
81
|
+
"form_snippet": null
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"field_name": "maintainer_email",
|
|
85
|
+
"label": "Maintainer Email",
|
|
86
|
+
"form_placeholder": "joe@example.com",
|
|
87
|
+
"display_property": "dc:contributor",
|
|
88
|
+
"display_email_name_field": "maintainer",
|
|
89
|
+
"display_snippet": null,
|
|
90
|
+
"form_snippet": null
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"field_name": "schema_upload",
|
|
94
|
+
"form_snippet": null,
|
|
95
|
+
"display_snippet": null,
|
|
96
|
+
"validators": "ignore_missing"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"field_name": "schema_json",
|
|
100
|
+
"form_snippet": null,
|
|
101
|
+
"display_snippet": null,
|
|
102
|
+
"validators": "ignore_missing"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"field_name": "schema_url",
|
|
106
|
+
"form_snippet": null,
|
|
107
|
+
"display_snippet": null,
|
|
108
|
+
"validators": "ignore_missing"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"resource_fields": [
|
|
112
|
+
{
|
|
113
|
+
"field_name": "url",
|
|
114
|
+
"label": "URL",
|
|
115
|
+
"preset": "resource_url_upload",
|
|
116
|
+
"required": true
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"field_name": "name",
|
|
120
|
+
"label": "Name",
|
|
121
|
+
"form_placeholder": "eg. January 2011 Gold Prices",
|
|
122
|
+
"required": true
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"field_name": "format",
|
|
126
|
+
"label": "Format",
|
|
127
|
+
"preset": "resource_format_autocomplete",
|
|
128
|
+
"required": true
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"field_name": "description",
|
|
132
|
+
"label": "Description",
|
|
133
|
+
"form_snippet": "markdown.html",
|
|
134
|
+
"form_placeholder": "Some useful notes about the data",
|
|
135
|
+
"required": true
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"field_name": "last_modified",
|
|
139
|
+
"display_snippet": null,
|
|
140
|
+
"form_snippet": null,
|
|
141
|
+
"validators": "ignore_empty"
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import ckan.plugins.toolkit as tk
|
|
2
|
+
|
|
3
|
+
TASK_TYPE = u"generate"
|
|
4
|
+
TASK_KEY = u"validation_schema_generator"
|
|
5
|
+
TASK_STATE_PENDING = u"Pending"
|
|
6
|
+
TASK_STATE_FINISHED = u"Complete"
|
|
7
|
+
TASK_STATE_NOT_GENERATED = u"Not generated"
|
|
8
|
+
TASK_STATE_ERROR = u"Failed"
|
|
9
|
+
VALID_STATES = [
|
|
10
|
+
TASK_STATE_ERROR,
|
|
11
|
+
TASK_STATE_FINISHED,
|
|
12
|
+
TASK_STATE_PENDING,
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
CF_JOB_TIMEOUT = u"ckanext.validation_schema_generator.job_timeout"
|
|
16
|
+
CF_JOB_TIMEOUT_DF = 3600
|
|
17
|
+
CF_API_KEY = u"ckanext.validation_schema_generator.api_key"
|
|
18
|
+
|
|
19
|
+
APPLY_FOR_OPTIONS = [u"dataset", u"resource"]
|
|
20
|
+
APPLY_FOR_DATASET = APPLY_FOR_OPTIONS[0]
|
|
21
|
+
APPLY_FOR_RESOURCE = APPLY_FOR_OPTIONS[1]
|
|
22
|
+
APPLY_FOR_FIELD = u"apply_for"
|
|
23
|
+
|
|
24
|
+
RES_SCHEMA_FIELD = tk.config.get(
|
|
25
|
+
u"ckanext.validation_schema_generator.resource_schema_field_name", u"schema")
|
|
26
|
+
PKG_SCHEMA_FIELD = tk.config.get(
|
|
27
|
+
u"ckanext.validation_schema_generator.package_schema_field_name", u"default_data_schema")
|
|
28
|
+
|
|
29
|
+
EMPTY_SCHEMA = u""
|
|
30
|
+
UNAPPLIED = u""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
from ckan.lib.base import BaseController
|
|
4
|
+
import ckan.plugins.toolkit as tk
|
|
5
|
+
|
|
6
|
+
from ckanext.validation_schema_generator.views import VSGIndexView
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class VSGController(BaseController):
|
|
10
|
+
def _check_access(self, resource_id):
|
|
11
|
+
try:
|
|
12
|
+
tk.check_access('vsg_generate', {}, {"id": resource_id})
|
|
13
|
+
except tk.NotAuthorized:
|
|
14
|
+
tk.abort(403, tk._(u'You are not allowed to generate resource schema'))
|
|
15
|
+
|
|
16
|
+
def index(self, dataset_id, resource_id):
|
|
17
|
+
self._check_access(resource_id)
|
|
18
|
+
|
|
19
|
+
view = VSGIndexView()
|
|
20
|
+
|
|
21
|
+
if tk.request.method == 'GET':
|
|
22
|
+
return view.get(dataset_id, resource_id)
|
|
23
|
+
|
|
24
|
+
return view.post(dataset_id, resource_id)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
def _get_helpers():
|
|
4
|
+
return {
|
|
5
|
+
"vsg_get_apply_for_options": vsg_get_apply_for_options
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def vsg_get_apply_for_options():
|
|
10
|
+
return [
|
|
11
|
+
{'value': '', 'text': ''},
|
|
12
|
+
{'value': 'dataset', 'text': 'Dataset default'},
|
|
13
|
+
{'value': 'resource', 'text': 'Resource'}
|
|
14
|
+
]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from frictionless import describe
|
|
6
|
+
from frictionless.exception import FrictionlessException
|
|
7
|
+
|
|
8
|
+
import ckan.model as model
|
|
9
|
+
import ckan.plugins.toolkit as tk
|
|
10
|
+
|
|
11
|
+
from ckanext.validation_schema_generator.constants import (
|
|
12
|
+
CF_API_KEY,
|
|
13
|
+
TASK_STATE_FINISHED,
|
|
14
|
+
TASK_STATE_ERROR,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def generate_schema_from_resource(input):
|
|
19
|
+
context = _make_context()
|
|
20
|
+
|
|
21
|
+
resource = tk.get_action(u'resource_show')(context, {
|
|
22
|
+
'id': input[u'resource_id']
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
errors = {}
|
|
26
|
+
options = {}
|
|
27
|
+
source = None
|
|
28
|
+
schema = None
|
|
29
|
+
|
|
30
|
+
if not source:
|
|
31
|
+
source = resource[u'url']
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
schema = describe(source, type='schema', **options)
|
|
35
|
+
except FrictionlessException as e:
|
|
36
|
+
errors[u'schema'] = str(e)
|
|
37
|
+
except Exception as e:
|
|
38
|
+
errors[u'undefined'] = str(e)
|
|
39
|
+
finally:
|
|
40
|
+
_update_task(input, errors, schema)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _make_context():
|
|
44
|
+
user = _get_site_user()
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
'model': model,
|
|
48
|
+
'session': model.Session,
|
|
49
|
+
'ignore_auth': True,
|
|
50
|
+
'user': user[u'name'],
|
|
51
|
+
'auth_user_obj': None
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _get_site_user():
|
|
56
|
+
return tk.get_action(u'get_site_user')({
|
|
57
|
+
'model': model,
|
|
58
|
+
'ignore_auth': True
|
|
59
|
+
}, {})
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _make_session():
|
|
63
|
+
s = requests.Session()
|
|
64
|
+
s.headers.update({u'Authorization': _get_api_key()})
|
|
65
|
+
return s
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _get_api_key():
|
|
69
|
+
return tk.config.get(CF_API_KEY, _get_site_user_api_key())
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_site_user_api_key():
|
|
73
|
+
user = _get_site_user()
|
|
74
|
+
return user['apikey']
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _update_task(input, errors, schema):
|
|
78
|
+
context = _make_context()
|
|
79
|
+
|
|
80
|
+
data_dict = {
|
|
81
|
+
'id': input[u'resource_id'],
|
|
82
|
+
'status': TASK_STATE_ERROR if errors else TASK_STATE_FINISHED,
|
|
83
|
+
'error': errors,
|
|
84
|
+
'schema': schema.to_json() if schema else ''
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
tk.get_action('vsg_update')(context, data_dict)
|
|
File without changes
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import ckan.plugins.toolkit as tk
|
|
7
|
+
from ckan.logic import validate
|
|
8
|
+
from ckan.lib.jobs import enqueue as enqueue_job
|
|
9
|
+
|
|
10
|
+
import ckanext.validation_schema_generator.logic.schema as vsg_schema
|
|
11
|
+
from ckanext.validation_schema_generator import jobs, utils as vsg_utils, constants as const
|
|
12
|
+
|
|
13
|
+
log = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _get_actions():
|
|
17
|
+
return {
|
|
18
|
+
'vsg_generate': vsg_generate,
|
|
19
|
+
'vsg_status': vsg_status,
|
|
20
|
+
'vsg_update': vsg_update,
|
|
21
|
+
'vsg_apply': vsg_apply,
|
|
22
|
+
'vsg_unapply': vsg_unapply
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@validate(vsg_schema.vsg_generate_schema)
|
|
27
|
+
def vsg_generate(context, data_dict):
|
|
28
|
+
"""Generate a schema for a dataset
|
|
29
|
+
|
|
30
|
+
:param id: resource ID
|
|
31
|
+
:type id: string
|
|
32
|
+
"""
|
|
33
|
+
tk.check_access('vsg_generate', context, data_dict)
|
|
34
|
+
|
|
35
|
+
task = vsg_utils.update_task(
|
|
36
|
+
context,
|
|
37
|
+
vsg_utils.generate_task_data(data_dict["id"],
|
|
38
|
+
const.TASK_STATE_PENDING))
|
|
39
|
+
|
|
40
|
+
data = {"resource_id": data_dict["id"], "task_id": task["id"]}
|
|
41
|
+
|
|
42
|
+
timeout = tk.asint(tk.config.get(
|
|
43
|
+
const.CF_JOB_TIMEOUT, const.CF_JOB_TIMEOUT_DF))
|
|
44
|
+
job = enqueue_job(jobs.generate_schema_from_resource, [data],
|
|
45
|
+
rq_kwargs={"timeout": timeout})
|
|
46
|
+
|
|
47
|
+
task['value'] = {'job_id': job.id}
|
|
48
|
+
task['error'] = {}
|
|
49
|
+
|
|
50
|
+
return vsg_utils.update_task(context, task)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@validate(vsg_schema.vsg_default_schema)
|
|
54
|
+
def vsg_status(context, data_dict):
|
|
55
|
+
"""Return a status of schema generation for a specific resource
|
|
56
|
+
|
|
57
|
+
:param id: resource ID
|
|
58
|
+
:type id: string
|
|
59
|
+
"""
|
|
60
|
+
tk.check_access('vsg_generate', context, data_dict)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
task = tk.get_action('task_status_show')(context, {
|
|
64
|
+
'entity_id': data_dict["id"],
|
|
65
|
+
'task_type': const.TASK_TYPE,
|
|
66
|
+
'key': const.TASK_KEY
|
|
67
|
+
})
|
|
68
|
+
except tk.ObjectNotFound:
|
|
69
|
+
return {
|
|
70
|
+
'state': const.TASK_STATE_NOT_GENERATED,
|
|
71
|
+
'last_updated': None,
|
|
72
|
+
'value': None,
|
|
73
|
+
'error': None
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
task['value'] = json.loads(task['value'])
|
|
77
|
+
task['error'] = json.loads(task['error'])
|
|
78
|
+
return task
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@validate(vsg_schema.vsg_hook_schema)
|
|
82
|
+
def vsg_update(context, data_dict):
|
|
83
|
+
"""Hook to update vsg task from job"""
|
|
84
|
+
id, error, status = tk.get_or_bust(data_dict, ['id', 'error', 'status'])
|
|
85
|
+
|
|
86
|
+
tk.check_access('vsg_generate', context, {'id': id})
|
|
87
|
+
task = tk.get_action('vsg_status')(context, {'id': id})
|
|
88
|
+
|
|
89
|
+
task['state'] = status
|
|
90
|
+
task['last_updated'] = vsg_utils.get_current_time()
|
|
91
|
+
task['error'] = error
|
|
92
|
+
|
|
93
|
+
if data_dict.get("schema"):
|
|
94
|
+
task['value']['schema'] = json.loads(data_dict["schema"])
|
|
95
|
+
|
|
96
|
+
return vsg_utils.update_task(context, task)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@validate(vsg_schema.vsg_apply_schema)
|
|
100
|
+
def vsg_apply(context, data_dict):
|
|
101
|
+
"""Apply a generated schema for a resource or dataset. The schema can be applied only
|
|
102
|
+
if the generation process is successfully completed
|
|
103
|
+
|
|
104
|
+
:param id: resource ID
|
|
105
|
+
:type id: string
|
|
106
|
+
:param apply_for: apply schema agaisnt the whole `dataset` or `resource` only.
|
|
107
|
+
:type apply_for: string
|
|
108
|
+
"""
|
|
109
|
+
tk.check_access('vsg_generate', context, data_dict)
|
|
110
|
+
|
|
111
|
+
apply_for = data_dict.get(const.APPLY_FOR_FIELD)
|
|
112
|
+
|
|
113
|
+
task = tk.get_action('vsg_status')(context, data_dict)
|
|
114
|
+
|
|
115
|
+
if task['state'] in (const.TASK_STATE_NOT_GENERATED,
|
|
116
|
+
const.TASK_STATE_PENDING):
|
|
117
|
+
raise tk.ValidationError(tk._(u"Schema is not generated yet."))
|
|
118
|
+
elif task['state'] == const.TASK_STATE_ERROR:
|
|
119
|
+
raise tk.ValidationError(
|
|
120
|
+
tk._(u"Schema generation failed. Check status."))
|
|
121
|
+
|
|
122
|
+
task['last_updated'] = vsg_utils.get_current_time()
|
|
123
|
+
task['value'][const.APPLY_FOR_FIELD] = apply_for
|
|
124
|
+
schema = vsg_utils.dump_schema(task['value']['schema'])
|
|
125
|
+
|
|
126
|
+
log.debug("Applying generated schema to %s: [%s]", apply_for, schema)
|
|
127
|
+
if apply_for == const.APPLY_FOR_DATASET:
|
|
128
|
+
_apply_pkg_schema(schema, data_dict['id'])
|
|
129
|
+
else:
|
|
130
|
+
_apply_res_schema(schema, data_dict['id'])
|
|
131
|
+
|
|
132
|
+
return vsg_utils.update_task(context, task)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _apply_res_schema(schema, resource_id):
|
|
136
|
+
"""Apply the generated schema for resource and unapply for package"""
|
|
137
|
+
context = {"user": "", "ignore_auth": True}
|
|
138
|
+
|
|
139
|
+
res = tk.get_action(u'resource_show')(context, {u'id': resource_id})
|
|
140
|
+
res[const.RES_SCHEMA_FIELD] = schema
|
|
141
|
+
|
|
142
|
+
tk.get_action(u'resource_update')(context, res)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _apply_pkg_schema(schema, resource_id):
|
|
146
|
+
"""Apply the generated schema for package and resource"""
|
|
147
|
+
context = {"user": "", "ignore_auth": True}
|
|
148
|
+
|
|
149
|
+
res = tk.get_action(u'resource_show')(context, {u'id': resource_id})
|
|
150
|
+
pkg = tk.get_action(u'package_show')(context, {u'id': res['package_id']})
|
|
151
|
+
|
|
152
|
+
pkg[const.PKG_SCHEMA_FIELD] = schema
|
|
153
|
+
|
|
154
|
+
if schema:
|
|
155
|
+
for resource in pkg.get('resources', []):
|
|
156
|
+
if resource['id'] == resource_id:
|
|
157
|
+
resource[const.RES_SCHEMA_FIELD] = schema
|
|
158
|
+
|
|
159
|
+
tk.get_action(u'package_update')(context, pkg)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@validate(vsg_schema.vsg_default_schema)
|
|
163
|
+
def vsg_unapply(context, data_dict):
|
|
164
|
+
"""Unapply the schema
|
|
165
|
+
"""
|
|
166
|
+
tk.check_access('vsg_generate', context, data_dict)
|
|
167
|
+
|
|
168
|
+
task = tk.get_action('vsg_status')(context, data_dict)
|
|
169
|
+
|
|
170
|
+
if task['state'] in (const.TASK_STATE_NOT_GENERATED,
|
|
171
|
+
const.TASK_STATE_PENDING):
|
|
172
|
+
raise tk.ValidationError(u"Schema is not generated yet")
|
|
173
|
+
elif task['state'] == const.TASK_STATE_ERROR:
|
|
174
|
+
raise tk.ValidationError(u"Schema generation failed. Check status.")
|
|
175
|
+
|
|
176
|
+
applied_for = task['value'].get(const.APPLY_FOR_FIELD)
|
|
177
|
+
|
|
178
|
+
if not applied_for:
|
|
179
|
+
raise tk.ValidationError(u"The schema is not applied yet")
|
|
180
|
+
|
|
181
|
+
task['value'][const.APPLY_FOR_FIELD] = ''
|
|
182
|
+
|
|
183
|
+
return vsg_utils.update_task(context, task)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
import ckan.plugins.toolkit as tk
|
|
4
|
+
import ckan.authz as authz
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _get_auth_functions():
|
|
8
|
+
return {
|
|
9
|
+
"vsg_generate": vsg_generate,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def vsg_generate(context, data_dict):
|
|
14
|
+
"""Check if user is allowed to generate schema
|
|
15
|
+
|
|
16
|
+
:param id: the id of the resource to generate schema for
|
|
17
|
+
:type id: string
|
|
18
|
+
"""
|
|
19
|
+
authorized = authz.is_authorized('resource_update', context,
|
|
20
|
+
data_dict).get('success')
|
|
21
|
+
|
|
22
|
+
if authorized:
|
|
23
|
+
return {'success': True}
|
|
24
|
+
|
|
25
|
+
msg = tk._('You are not authorized to generate schema for resource {}').format(
|
|
26
|
+
data_dict['id']
|
|
27
|
+
)
|
|
28
|
+
return {'success': False, 'msg': msg}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
from ckan.logic.schema import validator_args
|
|
4
|
+
|
|
5
|
+
import ckanext.validation_schema_generator.constants as const
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@validator_args
|
|
9
|
+
def vsg_default_schema(not_missing, vsg_resource_not_missing):
|
|
10
|
+
return {"id": [not_missing, vsg_resource_not_missing]}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@validator_args
|
|
14
|
+
def vsg_generate_schema(not_missing, vsg_is_resource_supportable,
|
|
15
|
+
vsg_resource_not_missing):
|
|
16
|
+
return {
|
|
17
|
+
"id":
|
|
18
|
+
[not_missing, vsg_resource_not_missing, vsg_is_resource_supportable]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@validator_args
|
|
23
|
+
def vsg_apply_schema(not_missing, resource_id_exists, one_of):
|
|
24
|
+
return {
|
|
25
|
+
"id": [not_missing, resource_id_exists],
|
|
26
|
+
"apply_for": [not_missing, one_of(const.APPLY_FOR_OPTIONS)]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@validator_args
|
|
31
|
+
def vsg_hook_schema(not_missing, resource_id_exists, one_of, ignore_missing,
|
|
32
|
+
vsg_validate_schema, unicode_safe, dict_only,
|
|
33
|
+
vsg_is_resource_supportable, vsg_generation_started):
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
"id": [
|
|
37
|
+
not_missing, resource_id_exists, vsg_is_resource_supportable,
|
|
38
|
+
vsg_generation_started
|
|
39
|
+
],
|
|
40
|
+
"error": [not_missing, dict_only],
|
|
41
|
+
"status": [not_missing, one_of(const.VALID_STATES)],
|
|
42
|
+
"schema": [ignore_missing, unicode_safe, vsg_validate_schema]
|
|
43
|
+
}
|