[#94,transform][m]: rework transform functionality and integration extensively (+ its working again!).

* view.transform.js: major refactor
  * general record transform rather than column transform
  * Normal dataset view rather than modal
  * Reworked UI
  * split out css from grid.css and simplify
* model.js + backend.memory.js: transform implemented on Dataset and Memory (Dataset passes off to _store).
* multiview: use new transform view
* _includes/recline-deps - rework to use source files rather than built ones
This commit is contained in:
Rufus Pollock
2012-06-28 23:52:58 +01:00
parent c2f5debf1f
commit aa1a362bb2
10 changed files with 153 additions and 226 deletions

View File

@@ -161,6 +161,15 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
});
return facetResults;
};
this.transform = function(editFunc) {
var toUpdate = costco.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.update(record);
});
return this.save(toUpdate);
};
};
}(jQuery, this.recline.Backend.Memory));

View File

@@ -20,9 +20,9 @@ var costco = function() {
;
if (!after) after = {};
if (currentColumn) {
preview.push({before: JSON.stringify(before[currentColumn]), after: JSON.stringify(after[currentColumn])});
preview.push({before: before[currentColumn], after: after[currentColumn]});
} else {
preview.push({before: JSON.stringify(before), after: JSON.stringify(after)});
preview.push({before: before, after: after});
}
}
return preview;
@@ -53,9 +53,9 @@ var costco = function() {
});
return {
edited: edited,
updates: edited,
docs: updatedDocs,
deleted: deleted,
deletes: deleted,
failed: failed
};
}

View File

@@ -149,6 +149,20 @@ my.Dataset = Backbone.Model.extend({
return this._store.save(this._changes, this.toJSON());
},
transform: function(editFunc) {
var self = this;
if (!this._store.transform) {
alert('Transform is not supported with this backend: ' + this.get('backend'));
return;
}
this.trigger('recline:flash', {message: "Updating all visible docs. This could take a while...", persist: true, loader: true});
this._store.transform(editFunc).done(function() {
// reload data as records have changed
self.query();
self.trigger('recline:flash', {message: "Records updated successfully"});
});
},
// ### query
//
// AJAX method with promise API to get records from the backend.

View File

@@ -83,7 +83,7 @@ my.MultiView = Backbone.View.extend({
</div> \
</div> \
<div class="recline-results-info"> \
Results found <span class="doc-count">{{docCount}}</span> \
<span class="doc-count">{{docCount}}</span> records\
</div> \
<div class="menu-right"> \
<div class="btn-group" data-toggle="buttons-checkbox"> \
@@ -139,6 +139,12 @@ my.MultiView = Backbone.View.extend({
model: this.model,
state: this.state.get('view-timeline')
}),
}, {
id: 'transform',
label: 'Transform',
view: new my.Transform({
model: this.model
})
}];
}
// these must be called after pageViews are created
@@ -264,6 +270,8 @@ my.MultiView = Backbone.View.extend({
this.$filterEditor.toggle();
} else if (action === 'fields') {
this.$fieldsView.toggle();
} else if (action === 'transform') {
this.transformView.el.toggle();
}
},

View File

@@ -8,61 +8,23 @@ this.recline.View = this.recline.View || {};
// ## ColumnTransform
//
// View (Dialog) for doing data transformations (on columns of data).
my.ColumnTransform = Backbone.View.extend({
className: 'transform-column-view modal fade in',
// View (Dialog) for doing data transformations
my.Transform = Backbone.View.extend({
className: 'recline-transform',
template: ' \
<div class="modal-header"> \
<a class="close" data-dismiss="modal">×</a> \
<h3>Functional transform on column {{name}}</h3> \
<div class="script"> \
<h2> \
Transform Script \
<button class="okButton btn btn-primary">Run on all records</button> \
</h2> \
<textarea class="expression-preview-code"></textarea> \
</div> \
<div class="modal-body"> \
<div class="grid-layout layout-tight layout-full"> \
<table> \
<tbody> \
<tr> \
<td colspan="4"> \
<div class="grid-layout layout-tight layout-full"> \
<table rows="4" cols="4"> \
<tbody> \
<tr style="vertical-align: bottom;"> \
<td colspan="4"> \
Expression \
</td> \
</tr> \
<tr> \
<td colspan="3"> \
<div class="input-container"> \
<textarea class="expression-preview-code"></textarea> \
</div> \
</td> \
<td class="expression-preview-parsing-status" width="150" style="vertical-align: top;"> \
No syntax error. \
</td> \
</tr> \
<tr> \
<td colspan="4"> \
<div id="expression-preview-tabs"> \
<span>Preview</span> \
<div id="expression-preview-tabs-preview"> \
<div class="expression-preview-container"> \
</div> \
</div> \
</div> \
</td> \
</tr> \
</tbody> \
</table> \
</div> \
</td> \
</tr> \
</tbody> \
</table> \
</div> \
<div class="expression-preview-parsing-status"> \
No syntax error. \
</div> \
<div class="modal-footer"> \
<button class="okButton btn primary">&nbsp;&nbsp;Update All&nbsp;&nbsp;</button> \
<button class="cancelButton btn danger">Cancel</button> \
<div class="preview"> \
<h3>Preview</h3> \
<div class="expression-preview-container"></div> \
</div> \
',
@@ -71,19 +33,19 @@ my.ColumnTransform = Backbone.View.extend({
'keydown .expression-preview-code': 'onEditorKeydown'
},
initialize: function() {
initialize: function(options) {
this.el = $(this.el);
this.render();
},
render: function() {
var htmls = Mustache.render(this.template,
{name: this.state.currentColumn}
);
var htmls = Mustache.render(this.template);
this.el.html(htmls);
// Put in the basic (identity) transform script
// TODO: put this into the template?
var editor = this.el.find('.expression-preview-code');
editor.val("function(doc) {\n doc['"+ this.state.currentColumn+"'] = doc['"+ this.state.currentColumn+"'];\n return doc;\n}");
var col = this.model.fields.models[0].id;
editor.val("function(doc) {\n doc['"+ col +"'] = doc['"+ col +"'];\n return doc;\n}");
editor.focus().get(0).setSelectionRange(18, 18);
editor.keydown();
},
@@ -96,58 +58,38 @@ my.ColumnTransform = Backbone.View.extend({
this.trigger('recline:flash', {message: "Error with function! " + editFunc.errorMessage});
return;
}
this.el.modal('hide');
this.trigger('recline:flash', {message: "Updating all visible docs. This could take a while...", persist: true, loader: true});
var docs = self.model.currentRecords.map(function(doc) {
return doc.toJSON();
});
// TODO: notify about failed docs?
var toUpdate = costco.mapDocs(docs, editFunc).edited;
var totalToUpdate = toUpdate.length;
function onCompletedUpdate() {
totalToUpdate += -1;
if (totalToUpdate === 0) {
self.trigger('recline:flash', {message: toUpdate.length + " records updated successfully"});
alert('WARNING: We have only updated the docs in this view. (Updating of all docs not yet implemented!)');
self.remove();
}
}
// TODO: Very inefficient as we search through all docs every time!
_.each(toUpdate, function(editedDoc) {
var realDoc = self.model.currentRecords.get(editedDoc.id);
realDoc.set(editedDoc);
realDoc.save().then(onCompletedUpdate).fail(onCompletedUpdate);
});
this.el.remove();
this.model.transform(editFunc);
},
editPreviewTemplate: ' \
<div class="expression-preview-table-wrapper"> \
<table class="table table-condensed"> \
<table class="table table-condensed table-bordered"> \
<thead> \
<tr> \
<th class="expression-preview-heading"> \
before \
</th> \
<th class="expression-preview-heading"> \
after \
</th> \
<th></th> \
{{#fields}} \
<th>{{id}}</th> \
{{/fields}} \
</tr> \
</thead> \
<tbody> \
{{#rows}} \
<tr> \
<th>Before</th> \
{{#row.before}} \
<td class="expression-preview-value"> \
{{before}} \
</td> \
<td class="expression-preview-value"> \
{{after}} \
{{.}} \
</td> \
{{/row.before}} \
</tr> \
<tr> \
<th>After</th> \
{{#row.after}} \
<td class="expression-preview-value"> \
{{.}} \
</td> \
{{/row.after}} \
</tr> \
{{/rows}} \
</tbody> \
</table> \
</div> \
',
onEditorKeydown: function(e) {
@@ -161,10 +103,27 @@ my.ColumnTransform = Backbone.View.extend({
var docs = self.model.currentRecords.map(function(doc) {
return doc.toJSON();
});
var previewData = costco.previewTransform(docs, editFunc, self.state.currentColumn);
var previewData = costco.previewTransform(docs, editFunc);
var $el = self.el.find('.expression-preview-container');
var templated = Mustache.render(self.editPreviewTemplate, {rows: previewData.slice(0,4)});
$el.html(templated);
var fields = self.model.fields.toJSON();
var rows = _.map(previewData.slice(0,4), function(row) {
return {
before: _.map(fields, function(field) {
return row.before[field.id];
}),
after: _.map(fields, function(field) {
return row.after[field.id];
})
};
});
$el.html('');
_.each(rows, function(row) {
var templated = Mustache.render(self.editPreviewTemplate, {
fields: fields,
row: row
});
$el.append(templated);
});
} else {
errors.text(editFunc.errorMessage);
}