diff --git a/docs/src/backend.ckan.html b/docs/src/backend.ckan.html index 373c3422..2130baa2 100644 --- a/docs/src/backend.ckan.html +++ b/docs/src/backend.ckan.html @@ -8,11 +8,34 @@

General notes

+

We need 2 things to make most requests:

+ +
    +
  1. CKAN API endpoint
  2. +
  3. ID of resource for which request is being made
  4. +
+ +

There are 2 ways to specify this information.

+ +

EITHER (checked in order):

+
  my.__type__ = 'ckan';

Default CKAN API endpoint used for requests (you can change this but it will affect every request!)

  my.API_ENDPOINT = 'http://datahub.io/api';

fetch

  my.fetch = function(dataset) {
-    var wrapper = my.DataStore();
+
  • The dataset has an endpoint attribute pointing to the CKAN API endpoint
  • + + +

    OR:

    + +

    Set the url attribute of the dataset to point to the Resource on the CKAN instance. The endpoint and id will then be automatically computed.

      my.__type__ = 'ckan';

    Default CKAN API endpoint used for requests (you can change this but it will affect every request!)

    + +

    DEPRECATION: this will be removed in v0.7. Please set endpoint attribute on dataset instead

      my.API_ENDPOINT = 'http://datahub.io/api';

    fetch

      my.fetch = function(dataset) {
    +    if (dataset.endpoint) {
    +      var wrapper = my.DataStore(dataset.endpoint);
    +    } else {
    +      var out = my._parseCkanResourceUrl(dataset.url);
    +      dataset.id = out.resource_id;
    +      var wrapper = my.DataStore(out.endpoint);
    +    }
         var dfd = $.Deferred();
         var jqxhr = wrapper.search({resource_id: dataset.id, limit: 0});
         jqxhr.done(function(results) {

    map ckan types to our usual types ...

          var fields = _.map(results.result.fields, function(field) {
    @@ -43,8 +66,14 @@
       }
     
       my.query = function(queryObj, dataset) {
    +    if (dataset.endpoint) {
    +      var wrapper = my.DataStore(dataset.endpoint);
    +    } else {
    +      var out = my._parseCkanResourceUrl(dataset.url);
    +      dataset.id = out.resource_id;
    +      var wrapper = my.DataStore(out.endpoint);
    +    }
         var actualQuery = my._normalizeQuery(queryObj, dataset);
    -    var wrapper = my.DataStore();
         var dfd = $.Deferred();
         var jqxhr = wrapper.search(actualQuery);
         jqxhr.done(function(results) {
    @@ -74,15 +103,21 @@
         }
     
         return that;
    -  }
    +  };

    Parse a normal CKAN resource URL and return API endpoint etc

    + +

    Normal URL is something like http://demo.ckan.org/dataset/some-dataset/resource/eb23e809-ccbb-4ad1-820a-19586fc4bebd

      my._parseCkanResourceUrl = function(url) {
    +    parts = url.split('/');
    +    var len = parts.length;
    +    return {
    +      resource_id: parts[len-1],
    +      endpoint: parts.slice(0,[len-4]).join('/') + '/api'
    +    }
    +  };
     
       var CKAN_TYPES_MAP = {
         'int4': 'integer',
         'int8': 'integer',
    -    'float8': 'float',
    -    'text': 'string',
    -    'json': 'object',
    -    'timestamp': 'date'
    +    'float8': 'float'
       };
     
     }(jQuery, this.recline.Backend.Ckan));
    diff --git a/docs/src/backend.csv.html b/docs/src/backend.csv.html
    index 4de7225a..34118c88 100644
    --- a/docs/src/backend.csv.html
    +++ b/docs/src/backend.csv.html
    @@ -97,7 +97,7 @@ http://www.uselesscode.org/javascript/csv/

    for (i = 0; i < s.length; i += 1) { cur = s.charAt(i);

    If we are at a EOF or EOR

          if (inQuote === false && (cur === delimiter || cur === "\n")) {
    -  field = processField(field);

    Add the current field to the current row

            row.push(field);

    If this is EOR append row to output and flush row

            if (cur === "\n") {
    +	field = processField(field);

    Add the current field to the current row

            row.push(field);

    If this is EOR append row to output and flush row

            if (cur === "\n") {
               out.push(row);
               row = [];
             }

    Flush the field buffer

            field = '';
    diff --git a/docs/src/backend.memory.html b/docs/src/backend.memory.html
    index 6ab3e14f..3cdd26fc 100644
    --- a/docs/src/backend.memory.html
    +++ b/docs/src/backend.memory.html
    @@ -112,16 +112,20 @@ What's wrong is we sort on the last entry in the sort list if there are
           }
     
           function range(record, filter) {
    +        var startnull = (filter.start == null || filter.start === '');
    +        var stopnull = (filter.stop == null || filter.stop === '');
             var parse = getDataParser(filter);
             var value = parse(record[filter.field]);
             var start = parse(filter.start);
    -        var stop  = parse(filter.stop);
    -
    -        return (value >= start && value <= stop);
    +        var stop  = parse(filter.stop);

    if at least one end of range is set do not allow '' to get through +note that for strings '' <= {any-character} e.g. '' <= 'a'

            if ((!startnull || !stopnull) && value === '') {
    +          return false;
    +        }
    +        return ((startnull || value >= start) && (stopnull || value <= stop));
           }
     
    -      function geo_distance() {

    TODO code here

          }
    -    };

    we OR across fields but AND across terms in query string

        this._applyFreeTextQuery = function(results, queryObj) {
    +      function geo_distance() {

    TODO code here

          }
    +    };

    we OR across fields but AND across terms in query string

        this._applyFreeTextQuery = function(results, queryObj) {
           if (queryObj.q) {
             var terms = queryObj.q.split(' ');
             var patterns=_.map(terms, function(term) {
    @@ -135,10 +139,10 @@ What's wrong is we sort on the last entry in the sort list if there are
                   var value = rawdoc[field.id];
                   if ((value !== null) && (value !== undefined)) { 
                     value = value.toString();
    -              } else {

    value can be null (apparently in some cases)

                    value = '';
    -              }

    TODO regexes?

                  foundmatch = foundmatch || (pattern.test(value.toLowerCase()));

    TODO: early out (once we are true should break to spare unnecessary testing) + } else {

    value can be null (apparently in some cases)

                    value = '';
    +              }

    TODO regexes?

                  foundmatch = foundmatch || (pattern.test(value.toLowerCase()));

    TODO: early out (once we are true should break to spare unnecessary testing) if (foundmatch) return true;

                });
    -            matches = matches && foundmatch;

    TODO: early out (once false should break to spare unnecessary testing) + matches = matches && foundmatch;

    TODO: early out (once false should break to spare unnecessary testing) if (!matches) return false;

              });
               return matches;
             });
    @@ -151,9 +155,9 @@ if (!matches) return false;

    if (!queryObj.facets) { return facetResults; } - _.each(queryObj.facets, function(query, facetId) {

    TODO: remove dependency on recline.Model

            facetResults[facetId] = new recline.Model.Facet({id: facetId}).toJSON();
    +      _.each(queryObj.facets, function(query, facetId) {

    TODO: remove dependency on recline.Model

            facetResults[facetId] = new recline.Model.Facet({id: facetId}).toJSON();
             facetResults[facetId].termsall = {};
    -      });

    faceting

          _.each(records, function(doc) {
    +      });

    faceting

          _.each(records, function(doc) {
             _.each(queryObj.facets, function(query, facetId) {
               var fieldId = query.terms.field;
               var val = doc[fieldId];
    @@ -170,7 +174,7 @@ if (!matches) return false;

    var terms = _.map(tmp.termsall, function(count, term) { return { term: term, count: count }; }); - tmp.terms = _.sortBy(terms, function(item) {

    want descending order

              return -item.count;
    +        tmp.terms = _.sortBy(terms, function(item) {

    want descending order

              return -item.count;
             });
             tmp.terms = tmp.terms.slice(0, 10);
           });
    @@ -178,7 +182,7 @@ if (!matches) return false;

    }; this.transform = function(editFunc) { - var toUpdate = recline.Data.Transform.mapDocs(this.data, editFunc);

    TODO: very inefficient -- could probably just walk the documents and updates in tandem and update

          _.each(toUpdate.updates, function(record, idx) {
    +      var toUpdate = recline.Data.Transform.mapDocs(this.data, editFunc);

    TODO: very inefficient -- could probably just walk the documents and updates in tandem and update

          _.each(toUpdate.updates, function(record, idx) {
             self.data[idx] = record;
           });
           return this.save(toUpdate);
    diff --git a/docs/src/docco.css b/docs/src/docco.css
    index 04cc7ecb..5aa0a8d7 100644
    --- a/docs/src/docco.css
    +++ b/docs/src/docco.css
    @@ -21,12 +21,6 @@ h1, h2, h3, h4, h5, h6 {
       h1 {
         margin-top: 40px;
       }
    -hr {
    -    border: 0 none;
    -    border-top: 1px solid #e5e5ee;
    -    height: 1px;
    -    margin: 20px 0;
    -}
     #container {
       position: relative;
     }
    @@ -121,7 +115,7 @@ table td {
       }
         pre, tt, code {
           font-size: 12px; line-height: 18px;
    -      font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
    +      font-family: Monaco, Consolas, "Lucida Console", monospace;
           margin: 0; padding: 0;
         }
     
    diff --git a/docs/src/model.html b/docs/src/model.html
    index 5ac6ed8c..a177a460 100644
    --- a/docs/src/model.html
    +++ b/docs/src/model.html
    @@ -297,6 +297,9 @@ WARNING: these will not persist unless you call save on Dataset

    if (this.attributes.label === null) { this.set({label: this.id}); } + if (this.attributes.type.toLowerCase() in this._typeMap) { + this.attributes.type = this._typeMap[this.attributes.type.toLowerCase()]; + } if (options) { this.renderer = options.renderer; this.deriver = options.deriver; @@ -306,6 +309,17 @@ WARNING: these will not persist unless you call save on Dataset

    } this.facets = new my.FacetList(); }, + _typeMap: { + 'text': 'string', + 'double': 'number', + 'float': 'number', + 'numeric': 'number', + 'int': 'integer', + 'datetime': 'date-time', + 'bool': 'boolean', + 'timestamp': 'date-time', + 'json': 'object' + }, defaultRenderers: { object: function(val, field, doc) { return JSON.stringify(val); @@ -313,7 +327,7 @@ WARNING: these will not persist unless you call save on Dataset

    geo_point: function(val, field, doc) { return JSON.stringify(val); }, - 'float': function(val, field, doc) { + 'number': function(val, field, doc) { var format = field.get('format'); if (format === 'percentage') { return val + '%'; diff --git a/docs/src/view.graph.html b/docs/src/view.graph.html index 96d55abc..fd87f132 100644 --- a/docs/src/view.graph.html +++ b/docs/src/view.graph.html @@ -123,7 +123,8 @@ generate the element itself (you can then append view.el to the DOM.

    }; var getFormattedX = function (x) { - var xfield = self.model.fields.get(self.state.attributes.group);

    time series

          var isDateTime = xfield.get('type') === 'date';
    +      var xfield = self.model.fields.get(self.state.attributes.group);

    time series

          var xtype = xfield.get('type');
    +      var isDateTime = (xtype === 'date' || xtype === 'date-time' || xtype  === 'time');
     
           if (self.model.records.models[parseInt(x)]) {
             x = self.model.records.models[parseInt(x)].get(self.state.attributes.group);
    @@ -232,7 +233,8 @@ generate the element itself (you can then append view.el to the DOM.

    var points = []; _.each(self.model.records.models, function(doc, index) { var xfield = self.model.fields.get(self.state.attributes.group); - var x = doc.getFieldValue(xfield);

    time series

            var isDateTime = xfield.get('type') === 'date';
    +        var x = doc.getFieldValue(xfield);

    time series

            var xtype = xfield.get('type');
    +        var isDateTime = (xtype === 'date' || xtype === 'date-time' || xtype  === 'time');
             
             if (isDateTime) {

    datetime

              if (self.state.attributes.graphType != 'bars' && self.state.attributes.graphType != 'columns') {

    not bar or column

                x = new Date(x).getTime();
               } else {

    bar or column

                x = index;
    diff --git a/docs/src/view.slickgrid.html b/docs/src/view.slickgrid.html
    index 0745f9c4..481b5571 100644
    --- a/docs/src/view.slickgrid.html
    +++ b/docs/src/view.slickgrid.html
    @@ -72,7 +72,7 @@ row = row index, cell = cell index, value = value, columnDef = column definition
           columns.push(column);
         });

    Restrict the visible columns

        var visibleColumns = columns.filter(function(column) {
           return _.indexOf(self.state.get('hiddenColumns'), column.id) == -1;
    -    });

    Order them if there is ordering info on the state

        if (this.state.get('columnsOrder')){
    +    });

    Order them if there is ordering info on the state

        if (this.state.get('columnsOrder') && this.state.get('columnsOrder').length > 0) {
           visibleColumns = visibleColumns.sort(function(a,b){
             return _.indexOf(self.state.get('columnsOrder'),a.id) > _.indexOf(self.state.get('columnsOrder'),b.id) ? 1 : -1;
           });
    diff --git a/docs/src/widget.filtereditor.html b/docs/src/widget.filtereditor.html
    index 08a94a2b..128e953e 100644
    --- a/docs/src/widget.filtereditor.html
    +++ b/docs/src/widget.filtereditor.html
    @@ -13,18 +13,18 @@
           <a href="#" class="js-add-filter">Add filter</a> \
           <form class="form-stacked js-add" style="display: none;"> \
             <fieldset> \
    -          <label>Filter type</label> \
    -          <select class="filterType"> \
    -            <option value="term">Term (text)</option> \
    -            <option value="range">Range</option> \
    -            <option value="geo_distance">Geo distance</option> \
    -          </select> \
               <label>Field</label> \
               <select class="fields"> \
                 {{#fields}} \
                 <option value="{{id}}">{{label}}</option> \
                 {{/fields}} \
               </select> \
    +          <label>Filter type</label> \
    +          <select class="filterType"> \
    +            <option value="term">Value</option> \
    +            <option value="range">Range</option> \
    +            <option value="geo_distance">Geo distance</option> \
    +          </select> \
               <button type="submit" class="btn">Add</button> \
             </fieldset> \
           </form> \
    @@ -44,7 +44,7 @@
             <fieldset> \
               <legend> \
                 {{field}} <small>{{type}}</small> \
    -            <a class="js-remove-filter" href="#" title="Remove this filter">&times;</a> \
    +            <a class="js-remove-filter" href="#" title="Remove this filter" data-filter-id="{{id}}">&times;</a> \
               </legend> \
               <input type="text" value="{{term}}" name="term" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
             </fieldset> \
    @@ -55,7 +55,7 @@
             <fieldset> \
               <legend> \
                 {{field}} <small>{{type}}</small> \
    -            <a class="js-remove-filter" href="#" title="Remove this filter">&times;</a> \
    +            <a class="js-remove-filter" href="#" title="Remove this filter" data-filter-id="{{id}}">&times;</a> \
               </legend> \
               <label class="control-label" for="">From</label> \
               <input type="text" value="{{start}}" name="start" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
    @@ -69,7 +69,7 @@
             <fieldset> \
               <legend> \
                 {{field}} <small>{{type}}</small> \
    -            <a class="js-remove-filter" href="#" title="Remove this filter">&times;</a> \
    +            <a class="js-remove-filter" href="#" title="Remove this filter" data-filter-id="{{id}}">&times;</a> \
               </legend> \
               <label class="control-label" for="">Longitude</label> \
               <input type="text" value="{{point.lon}}" name="lon" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
    @@ -125,7 +125,7 @@
       onRemoveFilter: function(e) {
         e.preventDefault();
         var $target = $(e.target);
    -    var filterId = $target.closest('.filter').attr('data-filter-id');
    +    var filterId = $target.attr('data-filter-id');
         this.model.queryState.removeFilter(filterId);
       },
       onTermFiltersUpdate: function(e) {