better error messages, more robust csv uploading

This commit is contained in:
Max Ogden
2011-09-16 21:56:23 -07:00
parent 935e0d15a0
commit fe8df046da
10 changed files with 145 additions and 98 deletions

6
app.js
View File

@@ -115,12 +115,6 @@ ddoc.lists = {
} }
} }
ddoc.validate_doc_update = function (newDoc, oldDoc, userCtx) {
if (newDoc._deleted === true && userCtx.roles.indexOf('_admin') === -1) {
throw "Only admin can delete documents on this database.";
}
};
couchapp.loadAttachments(ddoc, path.join(__dirname, 'attachments')); couchapp.loadAttachments(ddoc, path.join(__dirname, 'attachments'));
module.exports = ddoc; module.exports = ddoc;

View File

@@ -6,16 +6,8 @@
<link rel="stylesheet" href="style/reset.css" media="screen"> <link rel="stylesheet" href="style/reset.css" media="screen">
<link rel="stylesheet" href="style/data-table.css" media="screen"> <link rel="stylesheet" href="style/data-table.css" media="screen">
<link rel="stylesheet" href="style/style.css" media="screen"> <link rel="stylesheet" href="style/style.css" media="screen">
<script type="text/javascript" src="script/lib/jquery-1.6.1.min.js"></script>
<!-- only using jqueryui for draggable -- a lighter solution would be nice --> <!-- only using jqueryui for draggable -- a lighter solution would be nice -->
<script type="text/javascript" src="script/lib/jquery-ui-1.8.14.custom.min.js"></script> <script type="text/javascript" src="script/deps-min.js"></script>
<script type="text/javascript" src="script/lib/jquery.mustache.js"></script>
<script type="text/javascript" src="script/lib/jquery.couch2.js"></script>
<script type="text/javascript" src="script/lib/jquery.hotkeys.js"></script>
<script type="text/javascript" src="script/lib/sammy-0.6.3.min.js"></script>
<script type="text/javascript" src="script/lib/underscore.js"></script>
<script type="text/javascript" src="script/lib/microevent.js"></script>
<script type="text/javascript" src="script/lib/traverse.js"></script>
<script type="text/javascript" src="script/util.js"></script> <script type="text/javascript" src="script/util.js"></script>
<script type="text/javascript" src="script/costco.js"></script> <script type="text/javascript" src="script/costco.js"></script>
<script type="text/javascript" src="script/recline.js"></script> <script type="text/javascript" src="script/recline.js"></script>
@@ -32,7 +24,10 @@
<div class="project-actions"></div> <div class="project-actions"></div>
<div class="project-controls"></div> <div class="project-controls"></div>
</div> </div>
<div class="main_content"></div> <div class="main_content">
<div class="left-panel"></div>
<div class="right-panel"></div>
</div>
</div> </div>
<div id="notification-container"> <div id="notification-container">
@@ -94,43 +89,40 @@
<script type='text/mustache' class="generatingTemplate"><div class="loading">Loading...</div></script> <script type='text/mustache' class="generatingTemplate"><div class="loading">Loading...</div></script>
<script type='text/mustache' class="tableContainerTemplate"> <script type='text/mustache' class="tableContainerTemplate">
<div id="right-panel"> <div id="tool-panel">
<div id="tool-panel"> <div id="summary-bar">
<div id="summary-bar"> <span id="docCount"></span>
<span id="docCount"></span> </div>
<div id="download">
</div>
</div>
<div id="view-panel">
<div class="viewpanel-header">
<div class="viewpanel-pagesize">
<span>
Show:
</span>
<a href="javascript:{}" class="viewPanel-pagingControls-page action">5</a>
<a href="javascript:{}" class="viewPanel-pagingControls-page selected">10</a>
<a href="javascript:{}" class="viewPanel-pagingControls-page action">25</a>
<a href="javascript:{}" class="viewPanel-pagingControls-page action">50</a>
<span>
rows
</span>
</div> </div>
<div id="download"> <div class="viewpanel-sorting">
</div>
<div class="viewpanel-paging">
<a href="javascript:{}" class="first inaction">« first</a>
<a href="javascript:{}" class="previous inaction"> previous</a>
<span class="viewpanel-pagingcount">
1 - 10
</span>
<a href="javascript:{}" class="next action">next </a>
<a href="javascript:{}" class="last action">last »</a>
</div> </div>
</div> </div>
<div id="view-panel"> <div class="data-table-container">
<div class="viewpanel-header">
<div class="viewpanel-pagesize">
<span>
Show:
</span>
<a href="javascript:{}" class="viewPanel-pagingControls-page action">5</a>
<a href="javascript:{}" class="viewPanel-pagingControls-page selected">10</a>
<a href="javascript:{}" class="viewPanel-pagingControls-page action">25</a>
<a href="javascript:{}" class="viewPanel-pagingControls-page action">50</a>
<span>
rows
</span>
</div>
<div class="viewpanel-sorting">
</div>
<div class="viewpanel-paging">
<a href="javascript:{}" class="first inaction">« first</a>
<a href="javascript:{}" class="previous inaction"> previous</a>
<span class="viewpanel-pagingcount">
1 - 10
</span>
<a href="javascript:{}" class="next action">next </a>
<a href="javascript:{}" class="last action">last »</a>
</div>
</div>
<div class="data-table-container">
</div>
</div> </div>
</div> </div>
</script> </script>
@@ -304,7 +296,7 @@
Paste in an array of JSON objects representing the documents that you would like to insert into the database. Paste in an array of JSON objects representing the documents that you would like to insert into the database.
</p> </p>
<p class="info"> <p class="info">
<code>[{woo: "pizza"}, {tasty: "muffins"}]</code> <code>[{"woo": "pizza"}, {"tasty": "muffins"}]</code>
</p> </p>
<div class="menu-container data-table-cell-editor"> <div class="menu-container data-table-cell-editor">
<textarea class="data-table-cell-copypaste-editor" bind="textarea">{{value}}</textarea> <textarea class="data-table-cell-copypaste-editor" bind="textarea">{{value}}</textarea>

View File

@@ -47,10 +47,10 @@ onmessage = function(message) {
var req = new XMLHttpRequest(); var req = new XMLHttpRequest();
req.onprogress = req.upload.onprogress = function(e) { req.onprogress = req.upload.onprogress = function(e) {
if(e.lengthComputable) postMessage(JSON.stringify({ percent: (e.loaded / e.total) * 100 })); if(e.lengthComputable) postMessage({ percent: (e.loaded / e.total) * 100 });
}; };
req.onreadystatechange = function() { if (req.readyState == 4) postMessage(JSON.stringify( {done: true} )) }; req.onreadystatechange = function() { if (req.readyState == 4) postMessage({done: true, response: req.responseText}) };
req.open('POST', message.data.url); req.open('POST', message.data.url);
req.setRequestHeader('Content-Type', 'application/json'); req.setRequestHeader('Content-Type', 'application/json');
req.send(JSON.stringify({docs: docs})); req.send(JSON.stringify({docs: docs}));

View File

@@ -90,7 +90,14 @@ var costco = function() {
if(!docs.length) dfd.resolve("Failed: No docs specified"); if(!docs.length) dfd.resolve("Failed: No docs specified");
couch.request({url: app.baseURL + "api/_bulk_docs", type: "POST", data: JSON.stringify({docs: docs})}) couch.request({url: app.baseURL + "api/_bulk_docs", type: "POST", data: JSON.stringify({docs: docs})})
.then( .then(
function(resp) {ensureCommit().then(function() { dfd.resolve(resp) })}, function(resp) {ensureCommit().then(function() {
var error = couch.responseError(resp);
if (error) {
dfd.reject(error);
} else {
dfd.resolve(resp);
}
})},
function(err) { dfd.reject(err.responseText) } function(err) { dfd.reject(err.responseText) }
); );
return dfd.promise(); return dfd.promise();
@@ -119,25 +126,29 @@ var costco = function() {
data: event.target.result data: event.target.result
}; };
var worker = new Worker('script/costco-csv-worker.js'); var worker = new Worker('script/costco-csv-worker.js');
worker.onmessage = function(message) { worker.onmessage = function(event) {
message = JSON.parse(message.data); var message = event.data;
console.log(message) if (message.done) {
var error = couch.responseError(JSON.parse(message.response))
if (message.done) { console.log('e',error)
util.hide('dialog'); if (error) {
util.notify("Data uploaded successfully!"); app.emitter.emit(error, 'error');
recline.initializeTable(app.offset); } else {
} else if (message.percent) { util.notify("Data uploaded successfully!");
if (message.percent === 100) { recline.initializeTable(app.offset);
util.notify("Waiting for CouchDB...", {persist: true, loader: true}) }
} else { util.hide('dialog');
util.notify("Uploading... " + message.percent + "%"); } else if (message.percent) {
} if (message.percent === 100) {
} else { util.notify("Waiting for CouchDB...", {persist: true, loader: true})
util.notify(JSON.stringify(message)); } else {
} util.notify("Uploading... " + message.percent + "%");
}; }
worker.postMessage(payload); } else {
util.notify(JSON.stringify(message));
}
};
worker.postMessage(payload);
}; };
} else { } else {
util.notify('File not selected. Please try again'); util.notify('File not selected. Please try again');

19
attachments/script/deps-min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -10,9 +10,32 @@
url: "/" url: "/"
}; };
couch.errors = {
forbidden: "You aren't allowed to do that."
}
couch.responseError = function(response) {
if(_.isArray(response) && (response.length > 0) ) response = response[0];
if (response.error) return couch.errors[response.error];
}
couch.request = function(opts) { couch.request = function(opts) {
var ajaxOpts = $.extend({}, defaults, opts); var ajaxOpts = $.extend({}, defaults, opts)
return $.ajax(ajaxOpts).promise(); , dfd = $.Deferred()
;
$.ajax(ajaxOpts).then(
function(successResponse) {
var error = couch.responseError(successResponse);
if (error) app.emitter.emit(error, 'error');
dfd.resolve(successResponse);
},
function(errorResponse) {
app.emitter.emit("Fatal XHR Error", 'error');
}
)
return dfd.promise();
} }
couch.get = function(url) { couch.get = function(url) {

View File

@@ -122,7 +122,12 @@ var recline = function() {
} }
function getPageSize() { function getPageSize() {
return parseInt($(".viewpanel-pagesize .selected").text()); var pagination = $(".viewpanel-pagesize .selected");
if (pagination.length > 0) {
return parseInt(pagination.text())
} else {
return 10;
}
} }
function fetchRows(id, skip) { function fetchRows(id, skip) {
@@ -158,9 +163,9 @@ var recline = function() {
) )
} }
function getDbInfo() { function getDbInfo(url) {
var dfd = $.Deferred(); var dfd = $.Deferred();
return couch.request({url: app.baseURL + "api"}).then(function(dbInfo) { return couch.request({url: url}).then(function(dbInfo) {
app.dbInfo = dbInfo; app.dbInfo = dbInfo;
$.extend(app.dbInfo, { $.extend(app.dbInfo, {
@@ -176,18 +181,15 @@ var recline = function() {
} }
function bootstrap() { function bootstrap(id) {
util.registerEmitter(); app.dbPath = app.baseURL + "api/";
util.listenFor(['esc', 'return']); util.listenFor(['esc', 'return']);
getDbInfo().then(function( dbInfo ) { getDbInfo(app.dbPath).then(function( dbInfo ) {
util.render('title', 'project-title', dbInfo);
util.render('tableContainer', app.container);
util.render('title', 'project-title', app.dbInfo);
util.render( 'generating', 'project-actions' ); util.render( 'generating', 'project-actions' );
updateDocCount(app.dbInfo.doc_count);
couch.session().then(function(session) { couch.session().then(function(session) {
if ( session.userCtx.name ) { if ( session.userCtx.name ) {
var text = "Sign out"; var text = "Sign out";
@@ -202,19 +204,21 @@ var recline = function() {
} }
function initializeTable(offset) { function initializeTable(offset) {
util.render( 'tableContainer', 'right-panel' );
showDialog('busy'); showDialog('busy');
couch.request({url: app.baseURL + 'api/headers'}).then(function ( headers ) { couch.request({url: app.dbPath + 'headers'}).then(function ( headers ) {
util.hide('dialog'); util.hide('dialog');
getDbInfo().then(function(dbInfo) { getDbInfo(app.dbPath).then(function(dbInfo) {
updateDocCount(dbInfo.doc_count); updateDocCount(dbInfo.doc_count);
}); });
app.headers = headers; app.headers = headers;
app.csvUrl = app.baseURL + 'api/csv?headers=' + escape(JSON.stringify(headers)); app.csvUrl = app.dbPath + 'csv?headers=' + escape(JSON.stringify(headers));
util.render( 'actions', 'project-actions', $.extend({}, app.dbInfo, {url: app.csvUrl}) ); util.render( 'actions', 'project-actions', $.extend({}, app.dbInfo, {url: app.csvUrl}) );
fetchRows(false, offset); fetchRows(false, offset);
}) })
} }
return { return {
formatDiskSize: formatDiskSize, formatDiskSize: formatDiskSize,
handleMenuClick: handleMenuClick, handleMenuClick: handleMenuClick,

View File

@@ -1,6 +1,7 @@
var app = { var app = {
baseURL: util.getBaseURL(document.location.pathname), baseURL: util.getBaseURL(document.location.pathname),
container: 'main_content' container: 'main_content',
emitter: util.registerEmitter()
}; };
app.handler = function(route) { app.handler = function(route) {
@@ -227,7 +228,6 @@ app.after = {
}, },
function (err) { function (err) {
util.hide('dialog'); util.hide('dialog');
util.notify("Error uploading: " + err.responseText);
} }
); );
} else { } else {
@@ -246,6 +246,10 @@ app.sammy = $.sammy(function () {
}); });
$(function() { $(function() {
app.emitter.on('error', function(error) {
util.notify("Server error: " + error);
})
util.traverse = require('traverse'); util.traverse = require('traverse');
app.sammy.run(); app.sammy.run();
}) })

View File

@@ -32,7 +32,7 @@ var util = function() {
}; };
}; };
MicroEvent.mixin(Emitter); MicroEvent.mixin(Emitter);
app.emitter = new Emitter(); return new Emitter();
} }
function listenFor(keys) { function listenFor(keys) {

View File

@@ -450,7 +450,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
font-size: 11px; font-size: 11px;
} }
#left-panel { .left-panel {
position: fixed; position: fixed;
overflow: hidden; overflow: hidden;
padding: 0px; padding: 0px;
@@ -459,14 +459,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
background: #e3e9ff; background: #e3e9ff;
} }
#left-panel .ui-tabs .ui-tabs-panel { .left-panel .ui-tabs .ui-tabs-panel {
border-left: none; border-left: none;
border-right: none; border-right: none;
border-bottom: none; border-bottom: none;
padding: 0; padding: 0;
} }
#right-panel { .right-panel {
position: fixed; position: fixed;
top: 40px; top: 40px;
left: 0px; left: 0px;