-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Final changes before end of Phase 1 Development #25
Changes from all commits
942e699
af504b0
e090478
93e4508
73c5933
afdba83
64b5ccc
c9719bc
5d71387
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
from ckan.lib.helpers import flash_error | ||
import ckan.plugins as p | ||
import ckan.plugins.toolkit as tk | ||
import validators as v | ||
|
@@ -15,7 +16,7 @@ def create_relevant_governing_documents(): | |
except tk.ObjectNotFound: | ||
data = {'name': 'relevant_governing_documents'} | ||
vocab = tk.get_action('vocabulary_create')(context, data) | ||
for tag in opts.relevant_governing_documents(): | ||
for tag in opts.relevant_governing_documents(): | ||
data = {'name': tag, 'vocabulary_id': vocab['id']} | ||
tk.get_action('tag_create')(context, data) | ||
|
||
|
@@ -60,7 +61,6 @@ def parse_resource_related_gist(data_related_items, resource_id): | |
|
||
class ExampleIDatasetFormPlugin(p.SingletonPlugin, tk.DefaultDatasetForm): | ||
|
||
|
||
# modify ckan behavior on changes/(saves/updates/deletes) to resources | ||
p.implements(p.IResourceController) | ||
def _which_check_keys_changed(self, old, new): | ||
|
@@ -75,30 +75,49 @@ def _redirect_to_edit_on_change(self, resource, field): | |
|
||
def _delete_and_rebuild_datadict(self, resource): | ||
import json | ||
import unicodedata | ||
|
||
if 'datadict' in resource and 'id' in resource: | ||
record = resource['datadict'] | ||
resource.pop('datadict') | ||
json_record = json.loads(record) | ||
record = resource.pop('datadict') | ||
|
||
if record: | ||
# Cleanse of errant unicode characters | ||
record = unicodedata.normalize('NFKD', record).encode('ascii', 'ignore') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice. Does this fix or just patch the problem for release? When ckan reads the data dictionary back from the database, does the formatting remain or does it get stripped out? Do very long entries still get cut off and have some special characters appended? If not probably worth a comment somewhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It normalizes non-ascii characters to their ascii equivalent, if one exists, so formatting should be kept. As far as I can tell, this fixes the long entry problem as well, I created one with 500 or so words that worked fine. New lines are still not being retained though, but that was existing behavior. Let me know if you know where that is happening. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure, but it sounds like this is good to go. |
||
|
||
try: | ||
json_record = json.loads(record) | ||
except ValueError as err: | ||
# Invalid JSON, so don't remove old data | ||
error_message = "Error saving data dictionary: {0}. Data was: {1}".format(err, record) | ||
flash_error(error_message) | ||
return | ||
|
||
try: | ||
ds.delete_datastore_json(resource['id'], 'datadict') | ||
# don't fail if the filter is bad! (e.g., title_colname doesn't exist) | ||
except (tk.ObjectNotFound, tk.ValidationError), err: | ||
# code review: write tests for this. | ||
error_message = "Error saving data dictionary: {0}. Data was: {1}".format(e, record) | ||
flash_error(error_message) | ||
pass | ||
|
||
try: | ||
ds.delete_datastore_json(resource['id'], 'datadict') | ||
# don't fail if the filter is bad! (e.g., title_colname doesn't exist) | ||
except (tk.ObjectNotFound, tk.ValidationError), err: | ||
# code review: write tests for this. | ||
pass | ||
ds.create_datastore(resource['id'], json_title='datadict', json_record=json_record) | ||
return | ||
ds.create_datastore(resource['id'], json_title='datadict', json_record=json_record) | ||
except UnicodeEncodeError as err: | ||
error_message = "Error saving data dictionary: {0}. Data was: {1}".format(e, record) | ||
flash_error(error_message) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Awesome, but we probably want more explicit instructions, so that people know to avoid special characters and limit the amount of text? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had this happen when adding a data dictionary to a new dataset, no matter what I put into the fields. I think this is because the two stage dataset creation puts the data dictionary update logic into a weird state. I will look into it more tomorrow to see if there's a better fix. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. Check out the logs. Ultimately it seems to occur when CKAN attempts some text conversion before sending the data to the Datastore. It chokes on special characters that result from things like new line formatting, and it also seems to happen when there's too much text, which, for some reason, results in what might be a warning that includes special characters. Lots of implied question marks in the above. Happy hunting :) |
||
|
||
# This function is a template/starter for a more fine-grained email-on-change functionality | ||
# than the default notification that CKAN provides. It is not currently used. | ||
def _email_on_change(self, context, resource, field): | ||
# if specified fields have changed notify the relevant people | ||
if self.changed[field]: | ||
# print 'trigger email on change to '+field | ||
# filter by dataset name? | ||
# print 'trigger email on change to '+field | ||
# filter by dataset name? | ||
followers = tk.get_action('dataset_follower_list')(context,{'id': resource['package_id']}) | ||
for f in followers: | ||
# filter by group? | ||
# get email addresses | ||
# get email addresses | ||
print tk.get_action('user_show')(context,{'id': f['id']})['email'] | ||
# send a notification of change by email | ||
|
||
|
@@ -111,24 +130,23 @@ def after_create(self, context, resource): | |
# (so that users aren't forced to enter a confusing link). | ||
# some of that could be moved here if desired. | ||
return | ||
|
||
def before_update(self, context, current, resource): | ||
# note keys that have changed (current is old, resource is new) | ||
self._which_check_keys_changed(current, resource) | ||
if current.get('resource_type', '') == 'Data Dictionary' \ | ||
and resource.get('resource_type', '') == 'Data Dictionary': | ||
self._delete_and_rebuild_datadict(resource) | ||
|
||
def after_update(self, context, resource): | ||
''' do things on field changes ''' | ||
# unfinished email trigger: | ||
# self._email_on_change(context,resource,'privacy_contains_pii') | ||
self._redirect_to_edit_on_change(resource, 'resource_type') | ||
# reset monitored keys | ||
# reset monitored keys | ||
for key in self.changed: | ||
self.changed[key] = False | ||
return | ||
|
||
|
||
def before_delete(self, context, resource, resources): | ||
return | ||
def after_delete(self, context, resources): | ||
|
@@ -146,7 +164,7 @@ def get_helpers(self): | |
'options_legal_authority_for_collection': opts.legal_authority_for_collection, | ||
'options_privacy_pia_title': opts.privacy_pia_title, | ||
'options_privacy_sorn_number': opts.privacy_sorn_number, | ||
'tag_relevant_governing_documents': tag_relevant_governing_documents, | ||
'tag_relevant_governing_documents': tag_relevant_governing_documents, | ||
'options_relevant_governing_documents': opts.relevant_governing_documents, | ||
'options_content_periodicity': opts.content_periodicity, | ||
'options_update_frequency': opts.update_frequency, | ||
|
@@ -165,12 +183,12 @@ def get_helpers(self): | |
'create_datastore':ds.create_datastore, | ||
'get_unique_datastore_json':ds.get_unique_datastore_json, | ||
'delete_datastore_json':ds.delete_datastore_json, | ||
|
||
'parse_resource_related_gist': parse_resource_related_gist, | ||
'github_api_url': github_api_url, | ||
} | ||
|
||
|
||
# the main extra fields update/show functionality | ||
p.implements(p.IDatasetForm) | ||
def _modify_package_schema(self, schema): | ||
|
@@ -225,7 +243,7 @@ def _modify_package_schema(self, schema): | |
tk.get_converter('convert_to_extras')], | ||
'content_temporal_range_end' : [v.end_after_start_validator, v.reasonable_date_validator, | ||
tk.get_validator('ignore_missing'), | ||
tk.get_converter('convert_to_extras')], | ||
tk.get_converter('convert_to_extras')], | ||
'content_temporal_range_start' : [v.end_after_start_validator, v.reasonable_date_validator, | ||
tk.get_validator('ignore_missing'), | ||
tk.get_converter('convert_to_extras')], | ||
|
@@ -278,8 +296,8 @@ def _modify_package_schema(self, schema): | |
'resource_type' : [tk.get_validator('ignore_missing'),], | ||
'storage_location' : [tk.get_validator('ignore_missing'),], | ||
'storage_location_path' : [tk.get_validator('ignore_missing'),], | ||
'database_server' : [ tk.get_validator('ignore_missing'),], | ||
'database_name' : [ tk.get_validator('ignore_missing'),], | ||
'database_server' : [ tk.get_validator('ignore_missing'),], | ||
'database_name' : [ tk.get_validator('ignore_missing'),], | ||
'database_schema' : [ tk.get_validator('ignore_missing'),], | ||
'db_role_level_1' : [ tk.get_validator('ignore_missing'),], | ||
'db_role_level_2' : [ tk.get_validator('ignore_missing'),], | ||
|
@@ -292,17 +310,17 @@ def _modify_package_schema(self, schema): | |
'db_role_level_9' : [ tk.get_validator('ignore_missing'),], | ||
}) | ||
return schema | ||
|
||
def create_package_schema(self): | ||
schema = super(ExampleIDatasetFormPlugin, self).create_package_schema() | ||
schema = self._modify_package_schema(schema) | ||
return schema | ||
|
||
def update_package_schema(self): | ||
schema = super(ExampleIDatasetFormPlugin, self).update_package_schema() | ||
schema = self._modify_package_schema(schema) | ||
return schema | ||
|
||
def show_package_schema(self): | ||
schema = super(ExampleIDatasetFormPlugin, self).show_package_schema() | ||
schema.update({ | ||
|
@@ -392,9 +410,9 @@ def show_package_schema(self): | |
'intake_date' : [tk.get_validator('ignore_missing'),], | ||
'resource_type' : [ tk.get_validator('ignore_missing'),], | ||
'storage_location' : [ tk.get_validator('ignore_missing'),], | ||
'storage_location_path' : [ tk.get_validator('ignore_missing'),], | ||
'database_server' : [ tk.get_validator('ignore_missing'),], | ||
'database_name' : [ tk.get_validator('ignore_missing'),], | ||
'storage_location_path' : [ tk.get_validator('ignore_missing'),], | ||
'database_server' : [ tk.get_validator('ignore_missing'),], | ||
'database_name' : [ tk.get_validator('ignore_missing'),], | ||
'database_schema' : [ tk.get_validator('ignore_missing'),], | ||
'db_role_level_1' : [ tk.get_validator('ignore_missing'),], | ||
'db_role_level_2' : [ tk.get_validator('ignore_missing'),], | ||
|
@@ -415,18 +433,18 @@ def show_package_schema(self): | |
tk.get_validator('ignore_missing')] | ||
}) | ||
return schema | ||
|
||
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 [] | ||
|
||
|
||
p.implements(p.IConfigurer) | ||
def update_config(self, config): | ||
# Add this plugin's templates dir to CKAN's extra_template_paths, so | ||
|
@@ -460,7 +478,7 @@ def _change_facets(self, facets_dict): | |
facets_dict['res_type'] = p.toolkit._('Resource Types') | ||
facets_dict['res_format'] = p.toolkit._('Formats') | ||
return facets_dict | ||
|
||
# now return the same altered search facets for the dataset, group and organization page | ||
def dataset_facets(self, facets_dict, package_type): | ||
return self._change_facets(facets_dict) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{% set tags = h.get_facet_items_dict('tags', limit=3) %} | ||
{% set placeholder = _('E.g. mortgage') %} | ||
|
||
<div class="module module-search module-narrow module-shallow box"> | ||
<form class="module-content search-form" method="get" action="{% url_for controller='package', action='search' %}"> | ||
<h3 class="heading">{{ _("Search data") }}</h3> | ||
<div class="search-input control-group search-giant"> | ||
<input type="text" class="search" name="q" value="" autocomplete="off" placeholder="{% block search_placeholder %}{{ placeholder }}{% endblock %}" /> | ||
<button type="submit"> | ||
<i class="icon-search"></i> | ||
<span>{{ _('Search') }}</span> | ||
</button> | ||
</div> | ||
</form> | ||
<div class="tags"> | ||
<h3>{{ _('Popular tags') }}</h3> | ||
{% for tag in tags %} | ||
<a class="tag" href="{% url_for controller='package', action='search', tags=tag.name %}">{{ h.truncate(tag.display_name, 22) }}</a> | ||
{% endfor %} | ||
</div> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 hope that wasn't too painful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only took me a bit to figure out what was going on, no worries :)