Skip to content

Commit

Permalink
Merge branch 'feature-2389-better-dataexplorer-error-handling'
Browse files Browse the repository at this point in the history
  • Loading branch information
amercader committed Jun 29, 2012
2 parents 1bca3b5 + f04cdbc commit 2df478d
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 160 deletions.
17 changes: 7 additions & 10 deletions ckan/controllers/datastore.py
@@ -1,9 +1,9 @@
from ckan.lib.base import BaseController, abort, _, c, response, request, g
import ckan.model as model
from ckan.lib.helpers import json
from ckan.lib.jsonp import jsonpify
from ckan.logic import get_action, check_access
from ckan.logic import NotFound, NotAuthorized, ValidationError
from ckan.logic import NotFound, NotAuthorized



class DatastoreController(BaseController):
Expand All @@ -21,8 +21,6 @@ def read(self, id, url=''):

try:
resource = get_action('resource_show')(context, {'id': id})
if not resource.get('webstore_url', ''):
return {'error': 'DataStore is disabled for this resource'}
self._make_redirect(id, url)
return ''
except NotFound:
Expand All @@ -35,13 +33,12 @@ def write(self, id, url):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author}
try:
resource = model.Resource.get(id)
if not resource:
abort(404, _('Resource not found'))
if not resource.webstore_url:
return {'error': 'DataStore is disabled for this resource'}
context["resource"] = resource
check_access('resource_update', context, {'id': id})
resource_dict = get_action('resource_show')(context,{'id':id})
if not resource_dict['webstore_url']:
resource_dict['webstore_url'] = u'active'
get_action('resource_update')(context,resource_dict)

self._make_redirect(id, url)
return ''
except NotFound:
Expand Down
62 changes: 40 additions & 22 deletions ckan/public/scripts/application.js
Expand Up @@ -491,7 +491,7 @@ CKAN.View.ResourceEditor = Backbone.View.extend({
CKAN.View.Resource = Backbone.View.extend({
initialize: function() {
this.el = $(this.el);
_.bindAll(this,'updateName','updateIcon','name','askToDelete','openMyPanel','setErrors','setupDynamicExtras','addDynamicExtra', 'onDatastoreEnabledChange');
_.bindAll(this,'updateName','updateIcon','name','askToDelete','openMyPanel','setErrors','setupDynamicExtras','addDynamicExtra' );
this.render();
},
render: function() {
Expand Down Expand Up @@ -526,12 +526,8 @@ CKAN.View.Resource = Backbone.View.extend({
// Hook to open panel link
this.li.find('.resource-open-my-panel').click(this.openMyPanel);
this.table.find('.js-resource-edit-delete').click(this.askToDelete);
this.table.find('.js-datastore-enabled-checkbox').change(this.onDatastoreEnabledChange);
// Hook to markdown editor
CKAN.Utils.setupMarkdownEditor(this.table.find('.markdown-editor'));
if (resource_object.resource.webstore_url) {
this.table.find('.js-datastore-enabled-checkbox').prop('checked', true);
}

// Set initial state
this.updateName();
Expand Down Expand Up @@ -729,12 +725,6 @@ CKAN.View.Resource = Backbone.View.extend({
removeFromDom: function() {
this.li.remove();
this.table.remove();
},
onDatastoreEnabledChange: function(e) {
var isChecked = this.table.find('.js-datastore-enabled-checkbox').prop('checked');
var webstore_url = isChecked ? 'enabled' : null;
this.model.set({webstore_url: webstore_url});
this.table.find('.js-datastore-enabled-text').val(webstore_url);
}
});

Expand Down Expand Up @@ -867,7 +857,6 @@ CKAN.View.ResourceAddUpload = Backbone.View.extend({
, hash: data._checksum
, cache_url: data._location
, cache_url_updated: lastmod
, webstore_url: data._location
}
, {
error: function(model, error) {
Expand Down Expand Up @@ -934,7 +923,6 @@ CKAN.View.ResourceAddUrl = Backbone.View.extend({
size: data.size,
mimetype: data.mimetype,
last_modified: data.last_modified,
webstore_url: 'enabled',
url_error: (data.url_errors || [""])[0]
});
self.collection.add(newResource);
Expand All @@ -944,9 +932,6 @@ CKAN.View.ResourceAddUrl = Backbone.View.extend({
}
else {
newResource.set({url: urlVal, resource_type: this.options.mode});
if (newResource.get('resource_type')=='file') {
newResource.set({webstore_url: 'enabled'});
}
this.collection.add(newResource);
this.resetForm();
}
Expand Down Expand Up @@ -1034,7 +1019,7 @@ CKAN.Utils = function($, my) {

input_box.attr('name', new_name);
input_box.attr('id', new_name);

var $new = $('<div class="ckan-dataset-to-add"><p></p></div>');
$new.append($('<input type="hidden" />').attr('name', old_name).val(ui.item.value));
$new.append('<i class="icon-plus-sign"></i> ');
Expand Down Expand Up @@ -1479,7 +1464,7 @@ CKAN.Utils = function($, my) {
}
});
};

// This only needs to happen on dataset pages, but it doesn't seem to do
// any harm to call it anyway.
$('#user_follow_button').on('click', followButtonClicked);
Expand Down Expand Up @@ -1585,6 +1570,14 @@ CKAN.DataPreview = function ($, my) {
my.loadPreviewDialog = function(resourceData) {
my.$dialog.html('<h4>Loading ... <img src="http://assets.okfn.org/images/icons/ajaxload-circle.gif" class="loading-spinner" /></h4>');

function showError(msg){
msg = msg || CKAN.Strings.errorLoadingPreview;
return $('#ckanext-datapreview')
.append('<div></div>')
.addClass('alert alert-error fade in')
.html(msg);
}

function initializeDataExplorer(dataset) {
var views = [
{
Expand Down Expand Up @@ -1618,6 +1611,7 @@ CKAN.DataPreview = function ($, my) {
}
});


// -----------------------------
// Setup the Embed modal dialog.
// -----------------------------
Expand Down Expand Up @@ -1674,7 +1668,7 @@ CKAN.DataPreview = function ($, my) {
}

// 4 situations
// a) have a webstore_url
// a) webstore_url is active (something was posted to the datastore)
// b) csv or xls (but not webstore)
// c) can be treated as plain text
// d) none of the above but worth iframing (assumption is
Expand All @@ -1697,14 +1691,38 @@ CKAN.DataPreview = function ($, my) {
if (resourceData.webstore_url) {
resourceData.elasticsearch_url = '/api/data/' + resourceData.id;
var dataset = new recline.Model.Dataset(resourceData, 'elasticsearch');
initializeDataExplorer(dataset);
var errorMsg = CKAN.Strings.errorLoadingPreview + ': ' + CKAN.Strings.errorDataStore;
dataset.fetch()
.done(function(dataset){
initializeDataExplorer(dataset);
})
.fail(function(error){
if (error.message) errorMsg += ' (' + error.message + ')';
showError(errorMsg);
});

}
else if (resourceData.formatNormalized in {'csv': '', 'xls': ''}) {
// set format as this is used by Recline in setting format for DataProxy
resourceData.format = resourceData.formatNormalized;
var dataset = new recline.Model.Dataset(resourceData, 'dataproxy');
initializeDataExplorer(dataset);
$('.recline-query-editor .text-query').hide();
var errorMsg = CKAN.Strings.errorLoadingPreview + ': ' +CKAN.Strings.errorDataProxy;
dataset.fetch()
.done(function(dataset){

dataset.bind('query:fail', function(error) {
$('#ckanext-datapreview .data-view-container').hide();
$('#ckanext-datapreview .header').hide();
$('.preview-header .btn').hide();
});

initializeDataExplorer(dataset);
$('.recline-query-editor .text-query').hide();
})
.fail(function(error){
if (error.message) errorMsg += ' (' + error.message + ')';
showError(errorMsg);
});
}
else if (resourceData.formatNormalized in {
'rdf+xml': '',
Expand Down
11 changes: 0 additions & 11 deletions ckan/public/scripts/templates.js
Expand Up @@ -27,7 +27,6 @@ CKAN.Templates.resourceEntry = ' \
</li>';

var youCanUseMarkdownString = CKAN.Strings.youCanUseMarkdown.replace('%a', '<a href="http://daringfireball.net/projects/markdown/syntax" target="_blank">').replace('%b', '</a>');
var shouldADataStoreBeEnabledString = CKAN.Strings.shouldADataStoreBeEnabled.replace('%a', '<a href="http://docs.ckan.org/en/latest/datastore.html" target="_blank">').replace('%b', '</a>');
var datesAreInISOString = CKAN.Strings.datesAreInISO.replace('%a', '<a href="http://en.wikipedia.org/wiki/ISO_8601#Calendar_dates" target="_blank">').replace('%b', '</a>').replace('%c', '<strong>').replace('%d', '</strong>');

// TODO it would be nice to unify this with the markdown editor specified in helpers.py
Expand Down Expand Up @@ -93,16 +92,6 @@ CKAN.Templates.resourceDetails = ' \
{{/if}} \
</div> \
</div> \
<div class="control-group datastore-enabled"> \
<label for="" class="control-label" property="rdfs:label">'+CKAN.Strings.datastoreEnabled+'</label> \
<div class="controls"> \
<label class="checkbox"> \
<input type="checkbox" class="js-datastore-enabled-checkbox" /> \
<input type="hidden" name="resources__${num}__webstore_url" value="${resource.webstore_url}" class="js-datastore-enabled-text" /> \
<span class="hint">'+shouldADataStoreBeEnabledString+'</span> \
</label> \
</div> \
</div> \
<div class="control-group"> \
<label for="" class="control-label" property="rdfs:label">'+CKAN.Strings.lastModified+'</label> \
<div class="controls"> \
Expand Down
5 changes: 5 additions & 0 deletions ckan/public/scripts/vendor/recline/recline.js
Expand Up @@ -3177,6 +3177,11 @@ this.recline.Backend = this.recline.Backend || {};
var dfd = $.Deferred();
this._wrapInTimeout(jqxhr).done(function(schema) {
// only one top level key in ES = the type so we can ignore it
// CKAN
if (!schema){
dfd.reject({'message':'Elastic Search did not return a mapping'});
return;
}
var key = _.keys(schema)[0];
var fieldData = _.map(schema[key].properties, function(dict, fieldName) {
dict.id = fieldName;
Expand Down
4 changes: 3 additions & 1 deletion ckan/templates/js_strings.html
Expand Up @@ -65,11 +65,13 @@
addExtraField = _('Add Extra Field'),
deleteResource = _('Delete Resource'),
youCanUseMarkdown = _('You can use %aMarkdown formatting%b here.'),
shouldADataStoreBeEnabled = _('Should a %aDataStore table and Data API%b be enabled for this resource?'),
datesAreInISO = _('Dates are in %aISO Format%b &mdash; eg. %c2012-12-25%d or %c2010-05-31T14:30%d.'),
dataFileUploaded = _('Data File (Uploaded)'),
follow = _('Follow'),
unfollow = _('Unfollow'),
errorLoadingPreview = _('Could not load preview'),
errorDataProxy = _('DataProxy returned an error'),
errorDataStore = _('DataStore returned an error')
), indent=4)}

</script>
Expand Down
36 changes: 3 additions & 33 deletions ckan/templates/package/resource_read.html
Expand Up @@ -15,36 +15,9 @@
<xi:include href="../snippets/data-viewer-embed-dialog.html" />

<py:def function="optional_head">
<!-- data preview -->
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/leaflet/0.3.1/leaflet.css')}" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/leaflet/0.3.1/leaflet.ie.css')}" />
<![endif]-->
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/data-explorer.css')}" />
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/graph.css')}" />
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/map.css')}" />
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/grid.css')}" />
<style type="text/css">
.recline-query-editor form, .recline-query-editor .text-query {
height: 28px;
}

.recline-query-editor .pagination ul {
margin: 0;
padding: 0;
}

/* needed for Chrome but not FF */
.header .recline-query-editor .add-on {
margin-left: -27px;
}
<xi:include href="../snippets/recline-extra-header.html" />

/* needed for FF but not chrome */
.header .recline-query-editor .input-prepend {
vertical-align: top;
}
</style>
<!-- /data preview -->
<style type="text/css">
.resource-actions {
margin-right: 0;
Expand Down Expand Up @@ -202,12 +175,9 @@ <h3>Additional Information</h3>
</div>

<py:def function="optional_footer">
<!-- data preview -->
<script type="text/javascript" src="${h.url_for_static('/scripts/vendor/jquery.mustache/jquery.mustache.js')}"></script>
<script type="text/javascript" src="${h.url_for_static('/scripts/vendor/flot/0.7/jquery.flot.js')}"></script>
<script type="text/javascript" src="${h.url_for_static('/scripts/vendor/flot/0.7/jquery.flot.js')}"></script>
<script type="text/javascript" src="${h.url_for_static('/scripts/vendor/leaflet/0.3.1/leaflet.js')}"></script>
<script src="${h.url_for_static('/scripts/vendor/recline/recline.js')}"></script>

<xi:include href="../snippets/recline-extra-footer.html" />
</py:def>

<xi:include href="layout.html" />
Expand Down
13 changes: 13 additions & 0 deletions ckan/templates/snippets/recline-extra-footer.html
@@ -0,0 +1,13 @@
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:i18n="http://genshi.edgewall.org/i18n"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip=""
>
<script type="text/javascript" src="${h.url_for_static('/scripts/vendor/flot/0.7/jquery.flot.js')}"></script>
<script type="text/javascript" src="${h.url_for_static('/scripts/vendor/flot/0.7/jquery.flot.js')}"></script>
<script type="text/javascript" src="${h.url_for_static('/scripts/vendor/leaflet/0.3.1/leaflet.js')}"></script>
<script src="${h.url_for_static('/scripts/vendor/recline/recline.js')}"></script>

</html>
38 changes: 38 additions & 0 deletions ckan/templates/snippets/recline-extra-header.html
@@ -0,0 +1,38 @@
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:i18n="http://genshi.edgewall.org/i18n"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip=""
>

<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/leaflet/0.3.1/leaflet.css')}" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/leaflet/0.3.1/leaflet.ie.css')}" />
<![endif]-->
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/data-explorer.css')}" />
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/graph.css')}" />
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/map.css')}" />
<link rel="stylesheet" href="${h.url_for_static('/scripts/vendor/recline/css/grid.css')}" />
<style type="text/css">
.recline-query-editor form, .recline-query-editor .text-query {
height: 28px;
}

.recline-query-editor .pagination ul {
margin: 0;
padding: 0;
}

/* needed for Chrome but not FF */
.header .recline-query-editor .add-on {
margin-left: -27px;
}

/* needed for FF but not chrome */
.header .recline-query-editor .input-prepend {
vertical-align: top;
}
</style>

</html>
4 changes: 3 additions & 1 deletion ckan/tests/functional/test_authz.py
Expand Up @@ -115,7 +115,9 @@ def _test_via_wui(self, action, user, entity_name, entity='dataset'):

r = res.body
r = r.replace('form_errors', '')
tests['error string'] = bool('error' not in r)
# Commenting as it seems a very ineffective way of checking for errors
# (e.g. tests fail if there is a JS string with the word 'error' on it)
# tests['error string'] = bool('error' not in r)
tests['status'] = bool(res.status in (200, 201))
tests['0 packages found'] = bool(u'<strong>0</strong> packages found' not in res)
is_ok = False not in tests.values()
Expand Down

0 comments on commit 2df478d

Please sign in to comment.