Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#2750] Add example_idatasetform example extension
- Loading branch information
Sean Hammond
committed
Feb 25, 2013
1 parent
deea895
commit 4331816
Showing
9 changed files
with
363 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import logging | ||
|
||
import ckan.plugins as plugins | ||
import ckan.plugins.toolkit as toolkit | ||
import ckan.lib.plugins as lib_plugins | ||
import ckan.lib.navl.validators as validators | ||
import ckan.logic as logic | ||
import ckan.logic.converters as converters | ||
|
||
|
||
class ExampleIDatasetFormPlugin(plugins.SingletonPlugin, | ||
lib_plugins.DefaultDatasetForm): | ||
'''An example IDatasetForm CKAN plugin. | ||
Uses a tag vocabulary to add a custom metadata field to datasets. | ||
''' | ||
plugins.implements(plugins.IConfigurer, inherit=False) | ||
plugins.implements(plugins.IDatasetForm, inherit=False) | ||
|
||
# These record how many times methods that this plugin's methods are | ||
# called, for testing purposes. | ||
num_times_check_data_dict_called = 0 | ||
num_times_new_template_called = 0 | ||
num_times_comments_template_called = 0 | ||
num_times_search_template_called = 0 | ||
num_times_read_template_called = 0 | ||
num_times_history_template_called = 0 | ||
num_times_package_form_called = 0 | ||
|
||
def create_country_codes(self): | ||
'''Create country_codes vocab and tags, if they don't exist already. | ||
Note that you could also create the vocab and tags using CKAN's API, | ||
and once they are created you can edit them (e.g. to add and remove | ||
possible dataset country code values) using the API. | ||
''' | ||
user = logic.get_action('get_site_user')({'ignore_auth': True}, {}) | ||
context = {'user': user['name']} | ||
try: | ||
data = {'id': 'country_codes'} | ||
logic.get_action('vocabulary_show')(context, data) | ||
logging.info("Example genre vocabulary already exists, skipping.") | ||
except logic.NotFound: | ||
logging.info("Creating vocab 'country_codes'") | ||
data = {'name': 'country_codes'} | ||
vocab = logic.get_action('vocabulary_create')(context, data) | ||
for tag in (u'uk', u'ie', u'de', u'fr', u'es'): | ||
logging.info( | ||
"Adding tag {0} to vocab 'country_codes'".format(tag)) | ||
data = {'name': tag, 'vocabulary_id': vocab['id']} | ||
logic.get_action('tag_create')(context, data) | ||
|
||
def update_config(self, config): | ||
# Add this plugin's templates dir to CKAN's extra_template_paths, so | ||
# that CKAN will use this plugin's custom templates. | ||
toolkit.add_template_directory(config, 'templates') | ||
|
||
def is_fallback(self): | ||
# Return True to register this plugin as the default handler for | ||
# package types not handled by any other IDatasetForm plugin. | ||
return True | ||
|
||
def package_types(self): | ||
# This plugin doesn't handle any special package types, it just | ||
# registers itself as the default (above). | ||
return [] | ||
|
||
def form_to_db_schema(self): | ||
schema = super(ExampleIDatasetFormPlugin, self).form_to_db_schema() | ||
|
||
# Add our custom country_code metadata field to the schema. | ||
schema.update({ | ||
'country_code': [validators.ignore_missing, | ||
converters.convert_to_tags('country_codes')] | ||
}) | ||
|
||
return schema | ||
|
||
def db_to_form_schema(self): | ||
schema = super(ExampleIDatasetFormPlugin, self).db_to_form_schema() | ||
|
||
# Don't show vocab tags mixed in with normal 'free' tags | ||
# (e.g. on dataset pages, or on the search page) | ||
schema['tags']['__extras'].append(logic.converters.free_tags_only) | ||
|
||
# Add our custom country_code metadata field to the schema. | ||
schema.update({ | ||
'country_code': [ | ||
converters.convert_from_tags('country_codes'), | ||
validators.ignore_missing] | ||
}) | ||
|
||
return schema | ||
|
||
def setup_template_variables(self, context, data_dict=None): | ||
super(ExampleIDatasetFormPlugin, self).setup_template_variables( | ||
context, data_dict) | ||
|
||
# Create the country_codes vocab and tags, if they don't already exist. | ||
self.create_country_codes() | ||
|
||
# Add the list of available country codes, from the country_codes | ||
# vocab, to the template context. | ||
try: | ||
toolkit.c.country_codes = logic.get_action('tag_list')( | ||
context, {'vocabulary_id': 'country_codes'}) | ||
except logic.NotFound: | ||
toolkit.c.country_codes = None | ||
|
||
# These methods just record how many times they're called, for testing | ||
# purposes. | ||
# TODO: It might be better to test that custom templates returned by | ||
# these methods are actually used, not just that the methods get | ||
# called. | ||
|
||
def new_template(self): | ||
ExampleIDatasetFormPlugin.num_times_new_template_called += 1 | ||
return lib_plugins.DefaultDatasetForm.new_template(self) | ||
|
||
def comments_template(self): | ||
ExampleIDatasetFormPlugin.num_times_comments_template_called += 1 | ||
return lib_plugins.DefaultDatasetForm.comments_template(self) | ||
|
||
def search_template(self): | ||
ExampleIDatasetFormPlugin.num_times_search_template_called += 1 | ||
return lib_plugins.DefaultDatasetForm.search_template(self) | ||
|
||
def read_template(self): | ||
ExampleIDatasetFormPlugin.num_times_read_template_called += 1 | ||
return lib_plugins.DefaultDatasetForm.read_template(self) | ||
|
||
def history_template(self): | ||
ExampleIDatasetFormPlugin.num_times_history_template_called += 1 | ||
return lib_plugins.DefaultDatasetForm.history_template(self) | ||
|
||
def package_form(self): | ||
ExampleIDatasetFormPlugin.num_times_package_form_called += 1 | ||
return lib_plugins.DefaultDatasetForm.package_form(self) | ||
|
||
def check_data_dict(self, data_dict, schema=None): | ||
ExampleIDatasetFormPlugin.num_times_check_data_dict_called += 1 | ||
return lib_plugins.DefaultDatasetForm.check_data_dict(self, data_dict, | ||
schema) |
6 changes: 6 additions & 0 deletions
6
ckanext/example_idatasetform/templates/package/new_package_metadata.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{% ckan_extends %} | ||
|
||
<!-- Pass c.country_codes to package_metadata_form.html. | ||
FIXME: If ckan passed c to package_metadata_form.html anyway, we wouldn't need | ||
to override this block here. --> | ||
{% block form %}{{ h.snippet('package/snippets/package_metadata_form.html', data=data, errors=errors, include_metadata=false, pkg_name=pkg_name, country_codes=c.country_codes) }}{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{% ckan_extends %} | ||
|
||
{% block package_description %} | ||
{{ super() }} | ||
|
||
<!-- Add our custom country_code field to the dataset read page. --> | ||
{% if pkg.get('country_code') %} | ||
<section id="dataset-country_code" class="resources module-content"> | ||
<p><strong>Country Code</strong>: {{ pkg.country_code[0] }}</p> | ||
</section> | ||
{% endif %} | ||
|
||
{% endblock %} |
8 changes: 8 additions & 0 deletions
8
ckanext/example_idatasetform/templates/package/snippets/package_form.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{% ckan_extends %} | ||
|
||
<!-- Pass c.country_codes to package_metadata_fields.html. | ||
FIXME: If ckan passed c to package_metadata_fields.html anyway, we wouldn't | ||
need to override this block here. --> | ||
{% block metadata_fields %} | ||
{% snippet 'package/snippets/package_metadata_fields.html', data=data, errors=errors, country_codes=c.country_codes %} | ||
{% endblock %} |
40 changes: 40 additions & 0 deletions
40
ckanext/example_idatasetform/templates/package/snippets/package_metadata_fields.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
{% import 'macros/form.html' as form %} | ||
|
||
{% set groups_available = h.groups_available() %} | ||
{% if groups_available %} | ||
<div class="control-group"> | ||
{% set groups = h.dict_list_reduce(data.groups, 'id') %} | ||
<label for="field-groups" class="control-label">{{ _('Add to Groups') }}</label> | ||
<div class="controls"> | ||
<select id="field-groups" name="groups__{{ groups | count }}__id" data-module="autocomplete"> | ||
<option value="">{{ _('Select a group...') }}</option> | ||
{% for group in groups_available %} | ||
<option value="{{ group.id }}" {% if group.id in groups %}selected="selected"{% endif %}>{{ group.name }}</option> | ||
{% endfor %} | ||
</select> | ||
</div> | ||
</div> | ||
{% endif %} | ||
|
||
{{ form.input('author', label=_('Author'), id='field-author', placeholder=_('Joe Bloggs'), value=data.author, error=errors.author, classes=['control-medium']) }} | ||
|
||
{{ form.input('author_email', label=_('Author Email'), id='field-author-email', placeholder=_('joe@example.com'), value=data.author_email, error=errors.author_email, classes=['control-medium']) }} | ||
|
||
{{ form.input('maintainer', label=_('Maintainer'), id='field-maintainer', placeholder=_('Joe Bloggs'), value=data.maintainer, error=errors.maintainer, classes=['control-medium']) }} | ||
|
||
{{ form.input('maintainer_email', label=_('Maintainer Email'), id='field-maintainer-email', placeholder=_('joe@example.com'), value=data.maintainer_email, error=errors.maintainer_email, classes=['control-medium']) }} | ||
|
||
<label class="control-label" for="field-country_code">{{ _("Country Code") }}</label> | ||
<div class="controls"> | ||
<select id="field-country_code" name="country_code" data-module="autocomplete"> | ||
{% for country_code in country_codes %} | ||
<option value="{{ country_code }}" {% if country_code in data.get('country_code', []) %}selected="selected"{% endif %}>{{ country_code }}</option> | ||
{% endfor %} | ||
</select> | ||
</div> | ||
|
||
{# | ||
{% block custom_fields %} | ||
{% snippet 'snippets/custom_form_fields.html', extras=data.extras, errors=errors, limit=3 %} | ||
{% endblock %} | ||
#} |
21 changes: 21 additions & 0 deletions
21
ckanext/example_idatasetform/templates/package/snippets/package_metadata_form.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{% import "macros/form.html" as form %} | ||
|
||
{% set data = data or {} %} | ||
{% set errors = errors or {} %} | ||
|
||
<form class="dataset-form dataset-resource-form form-horizontal" method="post" data-module="basic-form"> | ||
{{ h.snippet('package/snippets/stages.html', stages=['complete', 'complete', 'active'], pkg_name=pkg_name) }} | ||
{{ form.errors(error_summary) }} | ||
|
||
<!-- Pass country_codes to package_metadata_fields.html. | ||
FIXME: If CKAN would pass c to package_metadata_fields.html anyway, we | ||
wouldn't need to override this template here. --> | ||
{% snippet 'package/snippets/package_metadata_fields.html', data=data, errors=errors, groups_available=groups_available, country_codes=country_codes %} | ||
|
||
<div class="form-actions"> | ||
{# TODO: Go back to previous resource form #} | ||
<button class="btn" name="save" value="go-resources" type="submit">{{ _('Previous') }}</button> | ||
<button class="btn btn-primary" name="save" value="finish" type="submit">{{ _('Finish') }}</button> | ||
</div> | ||
|
||
</form> |
129 changes: 129 additions & 0 deletions
129
ckanext/example_idatasetform/tests/test_example_idatasetform_plugin.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
import ckan | ||
import ckan.lib.create_test_data | ||
import paste.fixture | ||
import pylons.test | ||
import routes | ||
|
||
|
||
class TestExampleIDatasetFormPlugin: | ||
|
||
@classmethod | ||
def setup(cls): | ||
cls.app = paste.fixture.TestApp(pylons.test.pylonsapp) | ||
ckan.plugins.load('example_idatasetform') | ||
ckan.lib.create_test_data.CreateTestData.create() | ||
|
||
@classmethod | ||
def teardown(cls): | ||
ckan.model.repo.rebuild_db() | ||
|
||
def test_example_idatasetform_plugin(self): | ||
|
||
# Get the new dataset stage 1 page. | ||
offset = routes.url_for(controller='package', action='new') | ||
extra_environ = {'REMOTE_USER': 'tester'} | ||
response = self.app.get(offset, extra_environ=extra_environ) | ||
|
||
# Fill out the new dataset stage 1 form and submit it. | ||
form = response.forms[1] | ||
form['name'] = 'idatasetform_test_dataset' | ||
form['title'] = 'IDatasetForm Test Dataset' | ||
# Submit the form and get a redirected to the stage 2 form. | ||
response = form.submit('save', extra_environ=extra_environ) | ||
assert response.status == 302 | ||
response = response.follow(extra_environ=extra_environ) | ||
assert response.status == 200 | ||
|
||
# Fill out the new dataset stage 2 form and submit it. | ||
form = response.forms[1] | ||
form['name'] = 'idatasetform_test_resource' | ||
form['resource_type'] = 'api' | ||
form['url'] = 'www.example.com' | ||
response = form.submit('save', 3, extra_environ=extra_environ) | ||
assert response.status == 302 | ||
response = response.follow(extra_environ=extra_environ) | ||
assert response.status == 200 | ||
|
||
# Check that the custom Country Code field and its possible values | ||
# are on the new dataset stage 3 page. | ||
assert '<select id="field-country_code" name="country_code"' in ( | ||
response) | ||
assert '<option value="de"' in response | ||
assert '<option value="es"' in response | ||
assert '<option value="fr"' in response | ||
assert '<option value="ie"' in response | ||
assert '<option value="uk"' in response | ||
|
||
# Fill out the new dataset stage 3 form and submit it. | ||
form = response.forms[1] | ||
form['country_code'] = 'uk' | ||
response = form.submit('save', 3, extra_environ=extra_environ) | ||
assert response.status == 302 | ||
response = response.follow(extra_environ=extra_environ) | ||
assert response.status == 200 | ||
assert response.request.url.endswith( | ||
'/dataset/idatasetform_test_dataset') | ||
|
||
# Check that the custom Country Code field appears with the correct | ||
# value on the dataset read page. | ||
assert '<p><strong>Country Code</strong>: uk</p>' in response | ||
|
||
# Get the edit dataset page for the dataset we just created. | ||
offset = routes.url_for(controller='package', action='edit', | ||
id='idatasetform_test_dataset') | ||
response = self.app.get(offset, extra_environ=extra_environ) | ||
|
||
# Check that the custom country_code field is on the page. | ||
assert '<select id="field-country_code" name="country_code"' in ( | ||
response) | ||
# Check that the right value is selected by default. | ||
assert '<option value="uk" selected="selected">uk</option>' in ( | ||
response) | ||
|
||
# Fill out the form and submit it, changing the country_code value. | ||
form = response.forms[1] | ||
#form['tag_string'] = 'testing, idatasetform, test_update_tag' | ||
form['country_code'] = 'fr' | ||
response = form.submit('save', extra_environ=extra_environ) | ||
assert response.status == 302 | ||
response = response.follow(extra_environ=extra_environ) | ||
assert response.status == 200 | ||
assert response.request.url.endswith( | ||
'/dataset/idatasetform_test_dataset') | ||
|
||
# Test the contents of the updated dataset read page. | ||
assert '<p><strong>Country Code</strong>: fr</p>' in response | ||
|
||
# FIXME: Tags aren't shown on the dataset read page, so check them | ||
# another way. | ||
# TODO: Also edit some other fields and check their values. | ||
assert '<a href="/tag/idatasetform">idatasetform</a>' in response | ||
assert '<a href="/tag/testing">testing</a>' in response | ||
assert '<a href="/tag/test_update_tag">test_update_tag</a>' in response | ||
|
||
# Fetch the dataset search page, just to test that the plugin's | ||
# search_template() method gets called. | ||
offset = routes.url_for(controller='package', action='search') | ||
response = self.app.get(offset) | ||
assert response.status == 200 | ||
|
||
# Fetch the dataset history page, just to test that the plugin's | ||
# history_template() method gets called. | ||
offset = routes.url_for(controller='package', action='history', | ||
id='idatasetform_test_dataset') | ||
response = self.app.get(offset) | ||
assert response.status == 200 | ||
|
||
# TODO: It might be better to test that custom templates returned by | ||
# these methods are actually used, not just that the methods get | ||
# called. | ||
import ckanext.example_idatasetform.plugin as plugin | ||
assert plugin.ExampleIDatasetFormPlugin.num_times_package_form_called == 2 | ||
assert plugin.ExampleIDatasetFormPlugin.num_times_read_template_called == 2 | ||
#assert plugin.ExampleIDatasetFormPlugin.num_times_edit_template_called == 2 | ||
assert plugin.ExampleIDatasetFormPlugin.num_times_new_template_called == 1 | ||
#assert plugin.ExampleIDatasetFormPlugin.num_times_index_template_called == 1 | ||
assert plugin.ExampleIDatasetFormPlugin.num_times_history_template_called == 1 | ||
|
||
# TODO: Test IDatasetForm's comments_template() method. | ||
# (I think this requires the disqus plugin?) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters