[ckan-dev] Repeatable metadata elements
Pabitra Dash
pkdash_reena at hotmail.com
Wed Apr 2 21:19:09 UTC 2014
Thanks Khalegh. I will give that a try. It seems it should work for my case too.
Pabitra
From: khalegh at highwaythreesolutions.com
Date: Wed, 2 Apr 2014 11:38:24 -0700
To: ckan-dev at lists.okfn.org
Subject: Re: [ckan-dev] Repeatable metadata elements
Hello Pabitra,
I asked the same question a while ago but I didn’t get any answers. I had to add multiple values for a group of fields like contact name, contact email, etc. I checked ckan src for schema definition and came up with the following solution which I am not sure if it is the best way of doing it.
In my dataset form class I am overriding create_package_schema, update_package_schema, show_package_schema. Both create_package_schema and update_package_schema use the same schema definition. Here is part of the schema containing the multi value fields :
schema.update({ ... , 'contacts' : contacts_db_schema(), 'dates' : dates_to_db_schema(), ... , })
def contacts_db_schema(): schema = { 'name' : [check_empty, convert_to_extras], 'organization' : [check_empty, convert_to_extras], 'email' : [check_empty, valid_email, convert_to_extras], 'role' : [check_empty, convert_to_extras], 'private' : [ignore_missing, convert_to_extras], 'delete' :[ignore_missing, convert_to_extras] } return schema
Ckan uses this schema when creating or updating a dataset. It generates keys like (contacts, 0, name) or (contacts, 0, email) which are used by convert_to_extras convertor function.
I tried to do the same for show_package_schema, but ckan doesn’t give me the same key for fields so that I can use them to get the corresponding values from extras. The other problem is that ckan’s default convert_to_extras function takes the last part of the key when storing the data in extras which obviously doesn’t work when having multiple values.
For show_package_schema I am simply updating the schema as follow : def show_package_schema(self): schema = super(DatasetFormClass, self).show_package_schema() schema['tags']['__extras'].append(toolkit.get_converter('free_tags_only')) schema.update({ 'contacts' : [convert_from_extras, ignore_missing], 'dates' : [convert_from_extras, ignore_missing], })
Then I had to override both convert_to_extras and convert_from_extras. Be careful you cannot use the same converters for resources, you need to use ckan’s default if you have extra fields for resources.def convert_to_extras(key, data, errors, context):# print "key :====> ", key, "data : ====>", data[key] extras = data.get(('extras',), []) if not extras: data[('extras',)] = extras keyStr = ':'.join([str(x) for x in key]) extras.append({'key': keyStr, 'value': data[key]})
def convert_from_extras(key, data, errors, context):
print "key : <====", key, "\n" def remove_from_extras(data, keyList): to_remove = [] for data_key, data_value in data.iteritems(): if (data_key[0] == 'extras' and data_key[1] in keyList) : to_remove.append(data_key) for item in to_remove: del data[item] indexList = [] # A list containing the index of items in extras to be removed. new_data = {} #A new dictionary for data stored in extras with the given key for data_key, data_value in data.iteritems(): if (data_key[0] == 'extras' and data_key[-1] == 'key'): #Extract the key components eparated by ':' keyList = data_value.split(':') #Check for multiple value inputs and convert the list item index to integer if (len(keyList) > 1): keyList[1] = int(keyList[1]) #Construct the key for the stored value(s) newKey = tuple(keyList) if (key[-1] == newKey[0]): #Retrieve data from extras and add it to new_data so it can be added to the data dictionary. new_data[newKey] = data[('extras', data_key[1], 'value')] #Add the data index in extras to the list of items to be removed. indexList.append(data_key[1]) #print new_data #Remove all data from extras with the given index remove_from_extras(data, indexList)
#Remove previous data stored under the given key del data[key] deleteIndex = [] for data_key, data_value in new_data.iteritems(): #If this is a deleted record then add it to the deleted list to be removed from data later. if ('delete' in data_key and data_value == '1'): deleteIndex.append(data_key[1]) deleted = [] for data_key, data_value in new_data.iteritems(): if (len(data_key) > 1 and data_key[1] in deleteIndex): deleted.append(data_key) for item in deleted : del new_data[item]
#Add data extracted from extras to the data dictionary for data_key, data_value in new_data.iteritems(): data[data_key] = data_value I am attaching my template and javascript for contacts here as well. I hope this helps. Please let me know if you have any problem with the code.
Best,Khalegh
--Forwarded Message Attachment--
{#
adds input fields of contact information.
contacts - The contact fields to add (list of contacts).
errors - A dict of errors for the fields.
Example:
{snippet 'snippets/contact_fields.html', contacts=data.contacts, errors=errors %}
#}
{% import "macros/form.html" as form %}
{% set contacts = contacts or [] %}
{% set organizations_available = h.edc_orgs() %}
{% for contact in contacts %}
{% if errors.contacts %}
{% set email_error = errors.contacts[loop.index0].email %}
{% set name_error = errors.contacts[loop.index0].name %}
{% set org_error = errors.contacts[loop.index0].organization %}
{% set role_error = errors.contacts[loop.index0].role %}
{% set contact_error = email_error or name_error or org_error or rol_error %}
{% endif %}
Name
Organization
Select an organization
{% for organization in organizations_available %}
{% set selected_org = contact.organization and organization.id == contact.organization %}
{{ organization.title }}
{% endfor %}
Email
Role
Select a contact role
{% for role in h.edc_tags(_("contact_roles")) %}
{% set selected_role = contact.role and contact.role == role.id %}
{{ role.name }}
{% endfor %}
{% set is_private = (contact.private and contact.private == _('Private')) %}
Private Contact
Delete
{% if name_error and name_error is iterable %}
Name: {{ name_error|join(', ') }}
{% endif %}
{% if email_error and email_error is iterable %}
Email: {{ email_error|join(', ') }}
{% endif %}
{% if org_error and org_error is iterable %}
Organization: {{ org_error|join(', ') }}
{% endif %}
{% if role_error and role_error is iterable %}
Role: {{ role_error|join(', ') }}
{% endif %}
{% endfor %}
{% set len = contacts|count %}
{% if len == 0 %}
Name
Organization
Select an organization
{% for organization in organizations_available %}
{{ organization.name }}
{% endfor %}
Email
Role
Select a contact role
{% for role in h.edc_tags(_("contact_roles")) %}
{{ role.name }}
{% endfor %}
Private Contact
Delete
{% endif %}
Add another contact
On Apr 2, 2014, at 10:47 AM, Pabitra Dash <pkdash_reena at hotmail.com> wrote:Hi All,
I have implemented a custom metadata schema for dataset. We have another requirement to make some of the metadata elements repeatable. For example, I have creator element and creator_email element which I want to make repeatable. So in the UI when the user selects add a creator, I should be able to store name and email for another creator. Not sure how I define a schema for these kind of repeatable elements.
Would appreciate any help on this.
Pabitra
_______________________________________________
ckan-dev mailing list
ckan-dev at lists.okfn.org
https://lists.okfn.org/mailman/listinfo/ckan-dev
Unsubscribe: https://lists.okfn.org/mailman/options/ckan-dev
_______________________________________________
ckan-dev mailing list
ckan-dev at lists.okfn.org
https://lists.okfn.org/mailman/listinfo/ckan-dev
Unsubscribe: https://lists.okfn.org/mailman/options/ckan-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.okfn.org/pipermail/ckan-dev/attachments/20140402/97c894e5/attachment-0003.html>
More information about the ckan-dev
mailing list