From fb040fcbfee81dc7280f2b735032162be6964e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Sch=C3=A4ffner-Gurney?= Date: Thu, 17 Sep 2015 13:48:52 -0400 Subject: [PATCH 1/7] zero state for instances without collections progress on istance properties static styling for instance properties in sidebar [wip] port, enterprise/community, reload missing zero state for instances without collections styling, changed default window size moved sampling-message into refine view --- src/collection-stats/index.js | 2 +- src/collection-stats/index.less | 35 ++++++--- src/field-list/field.jade | 2 +- src/home/collection.jade | 1 - src/home/collection.js | 10 --- src/home/index.jade | 5 ++ src/home/index.js | 28 ++++++-- src/home/index.less | 19 ++++- src/models/sampled-schema.js | 102 ++++++++++++++++++++++----- src/refine-view/index.jade | 8 ++- src/refine-view/index.js | 13 ++++ src/refine-view/index.less | 29 +++++--- src/sampling-message/index.jade | 6 +- src/sampling-message/index.js | 12 ++-- src/sidebar/index.jade | 1 + src/sidebar/index.js | 12 ++++ src/sidebar/instance-properties.jade | 14 ++++ src/sidebar/instance-properties.js | 41 +++++++++++ styles/palette.less | 5 +- styles/sidebar.less | 76 ++++++++++++++++---- 20 files changed, 340 insertions(+), 81 deletions(-) create mode 100644 src/sidebar/instance-properties.jade create mode 100644 src/sidebar/instance-properties.js diff --git a/src/collection-stats/index.js b/src/collection-stats/index.js index 25ec2c9680c..82648358156 100644 --- a/src/collection-stats/index.js +++ b/src/collection-stats/index.js @@ -34,7 +34,7 @@ var CollectionStatsView = AmpersandView.extend({ format: function(propertyName) { var value = this.model.get(propertyName) || 0; var precision = value <= 1000 ? '0' : '0.0'; - var format = propertyName.indexOf('_size') > -1 ? 'b' : 'a'; + var format = propertyName.indexOf('_size') > -1 ? ' b' : 'a'; return numeral(value).format(precision + format); }, derived: { diff --git a/src/collection-stats/index.less b/src/collection-stats/index.less index ae54b16c8b3..3e3bc22712d 100644 --- a/src/collection-stats/index.less +++ b/src/collection-stats/index.less @@ -4,42 +4,59 @@ position: absolute; right: 20px; text-align: right; - min-width: 450px; + min-width: 520px; } .collection-stats { display: inline-block; + position: relative; font-weight: 200; list-style: none; - padding: 0 30px 0 0; - margin: 11px 30px 0 0; - border-right: 1px solid @gray7; + padding: 0 20px 0 0; + margin: 0 20px 0 0; + + &::after { + content: ''; + display: block; + position: absolute; + right: 0; + top: 2px; + width: 1px; + height: 24px; + background: @gray7; + } &:last-child { - border-right: none; padding: 0; margin: 11px 0 0 0; + + &::after { + display: none; + } } } .collection-stats-item { display: inline-block; - margin-right: 10px; + margin-right: 12px; &:last-child { margin-right: 0; } } .collection-stats-primary-label { + display: inline-block; text-transform: uppercase; - letter-spacing: 1px; font-size: 12px; color: @gray3; + margin-right: 5px; } .collection-stats-primary-value { - font-size: 36px; - line-height: 36px; + display: inline-block; + font-size: 24px; + line-height: 24px; } .collection-stats-label { font-size: 11px; + line-height: 11px; color: @gray3; } .collection-stats-value { diff --git a/src/field-list/field.jade b/src/field-list/field.jade index eff820e8c5d..bae68f47ec9 100644 --- a/src/field-list/field.jade +++ b/src/field-list/field.jade @@ -1,5 +1,4 @@ .schema-field.schema-field-basic - hr.field-divider .row .col-sm-4 .schema-field-name @@ -10,3 +9,4 @@ div(data-hook='minichart-container') div(data-hook='fields-subview') div(data-hook='arrayfields-subview') + hr.field-divider \ No newline at end of file diff --git a/src/home/collection.jade b/src/home/collection.jade index 0ada7fc24b2..fe4a41e672d 100644 --- a/src/home/collection.jade +++ b/src/home/collection.jade @@ -1,5 +1,4 @@ .collection-view.clearfix - div(data-hook='sampling-message-subview') div.modal.fade(tabindex='-1', role='dialog', arialabelledby='Share Schema Confirmation', data-hook='share-schema-confirmation') div.modal-dialog.modal-sm .modal-content diff --git a/src/home/collection.js b/src/home/collection.js index 69cdac8bab6..25bbe6f3166 100644 --- a/src/home/collection.js +++ b/src/home/collection.js @@ -3,7 +3,6 @@ var CollectionStatsView = require('../collection-stats'); var FieldListView = require('../field-list'); var DocumentListView = require('../document-list'); var RefineBarView = require('../refine-view'); -var SamplingMessageView = require('../sampling-message'); var MongoDBCollection = require('../models/mongodb-collection'); var SampledSchema = require('../models/sampled-schema'); var app = require('ampersand-app'); @@ -161,15 +160,6 @@ var MongoDBCollectionView = View.extend({ collection: this.schema.documents }); } - }, - sampling_message: { - hook: 'sampling-message-subview', - prepareView: function(el) { - return new SamplingMessageView({ - el: el, - parent: this - }); - } } } }); diff --git a/src/home/index.jade b/src/home/index.jade index ed324f00970..45183279ed9 100644 --- a/src/home/index.jade +++ b/src/home/index.jade @@ -4,4 +4,9 @@ div.report-zero-state(data-hook='report-zero-state') img(src='images/zero-state-arrow-collections.png', width="110") span Choose a collection to analyze + div.no-collections-zero-state(data-hook='no-collections-zero-state') + span + |The MongoDB instance you are connected to does not contain any collections.  + a.show-connect-window Connect to another instance + |. div(data-hook='sidebar') diff --git a/src/home/index.js b/src/home/index.js index 4e00d7a9363..f1fa7676940 100644 --- a/src/home/index.js +++ b/src/home/index.js @@ -16,18 +16,30 @@ var HomeView = View.extend({ allowNull: true, default: null }, - showZeroState: { + showDefaultZeroState: { type: 'boolean', - default: true + default: false + }, + showNoCollectionsZeroState: { + type: 'boolean', + default: false } }, bindings: { - showZeroState: { + showDefaultZeroState: { hook: 'report-zero-state', type: 'booleanClass', no: 'hidden' + }, + showNoCollectionsZeroState: { + hook: 'no-collections-zero-state', + type: 'booleanClass', + no: 'hidden' } }, + events: { + 'click a.show-connect-window': 'onClickShowConnectWindow' + }, initialize: function() { this.listenTo(app.instance, 'sync', this.onInstanceFetched); this.listenTo(app.connection, 'change:name', this.updateTitle); @@ -36,6 +48,11 @@ var HomeView = View.extend({ app.instance.fetch(); }, onInstanceFetched: function() { + if(_.size(app.instance.collections) === 0) { + this.showNoCollectionsZeroState = true; + } else { + this.showDefaultZeroState = true; + } if (!this.ns) { app.instance.collections.unselectAll(); } else { @@ -58,11 +75,14 @@ var HomeView = View.extend({ this.ns = model.getId(); this.updateTitle(model); - this.showZeroState = false; + this.showDefaultZeroState = false; app.navigate(format('schema/%s', model.getId()), { silent: true }); }, + onClickShowConnectWindow: function() { + // code to close current connection window and open connect dialog + }, template: require('./index.jade'), subviews: { _collection: { diff --git a/src/home/index.less b/src/home/index.less index 83094a2a833..28f122f2940 100644 --- a/src/home/index.less +++ b/src/home/index.less @@ -53,7 +53,7 @@ } .report-zero-state { - margin: 124px 0 0 20px; + margin: 190px 0 0 20px; img { margin-bottom: 7px; @@ -65,13 +65,28 @@ margin-left: 15px; } } +.no-collections-zero-state { + text-align: center; + margin-top: 200px; + padding: 50px 75px; + font-size: 18px; + color: @gray4; + font-weight: 200; + + a { + cursor: pointer; + } +} .collection-view { header { - padding: 12px 25px; + padding: 12px 25px 0; position: relative; z-index: 1; } + h1 { + margin-top: 0; + } .column-container { display: flex; overflow: hidden; diff --git a/src/models/sampled-schema.js b/src/models/sampled-schema.js index d45027c6078..ee8031a92cc 100644 --- a/src/models/sampled-schema.js +++ b/src/models/sampled-schema.js @@ -6,8 +6,24 @@ var filterableMixin = require('ampersand-collection-filterable'); var SampledDocumentCollection = require('./sampled-document-collection'); var es = require('event-stream'); var debug = require('debug')('scout:models:schema'); +var debugMetrics = require('debug')('scout:metrics'); var app = require('ampersand-app'); +// @todo: stub for metrics module. currently just logs debug messages with scout:metrics marker +var metrics = { + track: function(label, err, obj) { + if (!obj) { + obj = err; + err = null; + } + if (err) { + debugMetrics('metrics error: ', err, obj); + } else { + debugMetrics('metrics log: ', obj); + } + } +}; + /** * wrapping mongodb-schema's FieldCollection with a filterable mixin */ @@ -25,6 +41,8 @@ module.exports = Schema.extend({ } }, session: { + // total number of documents counted under the given query + total: 'number', is_fetching: { type: 'boolean', default: false @@ -96,43 +114,93 @@ module.exports = Schema.extend({ var model = this; wrapError(this, options); - var parse = function(doc, cb) { - model.parse(doc); - cb(null, doc); + var success = options.success; + options.success = function(resp) { + if (success) { + success(model, resp, options); + } + model.trigger('sync'); }; + var start = new Date(); + var timeAtFirstDoc; + var erroredOnDocs = []; - var docs = []; + // No results found + var onEmpty = function() { + model.is_fetching = false; + model.documents.reset(); + model.documents.trigger('sync'); + options.success({}); + }; + var docs = []; var addToDocuments = function(doc, cb) { docs.push(doc); cb(); }; + var parse = function(doc, cb) { + if (!timeAtFirstDoc) { + timeAtFirstDoc = new Date(); + } + try { + model.parse(doc); + } catch (err) { + erroredOnDocs.push(doc); + metrics.track('Schema: Error: Parse', err, { + doc: doc, + schema: model.serialize() + }); + } + cb(null, doc); + }; + var onEnd = function(err) { model.is_fetching = false; if (err) { + metrics.track('Schema: Error: End', err, { + schema: model + }); return options.error(model, err); } model.documents.reset(docs); model.documents.trigger('sync'); - options.success({}); - }; - var success = options.success; - options.success = function(resp) { - if (success) { - success(model, resp, options); - } - model.trigger('sync'); + // @note (imlucas): Any other metrics? Feedback on `Schema *`? + metrics.track('Schema: Complete', { + Duration: new Date() - start, + 'Total Document Count': model.total, + 'Document Count': model.documents, + 'Errored Document Count': erroredOnDocs.length, + 'Time to First Doc': timeAtFirstDoc - start, + 'Schema Height': model.height, // # of top level keys + 'Schema Width': model.width, // max nesting depth + 'Schema Sparsity': model.sparsity // lots of fields missing or consistent + }); + options.success({}); }; model.trigger('request', {}, {}, options); - debug('creating sample stream'); - app.client.sample(model.ns, options) - .pipe(es.map(parse)) - .pipe(es.map(addToDocuments)) - .pipe(es.wait(onEnd)); + app.client.count(model.ns, options, function(err, count) { + if (err) { + metrics.track('Schema: Error: Count', err, { + schema: model + }); + return options.error(model, err); + } + debug('options', options, 'count', count.count); + model.total = count.count; + if (model.total === 0) { + return onEmpty(); + } + + debug('creating sample stream'); + app.client.sample(model.ns, options) + .pipe(es.map(parse)) + .pipe(es.map(addToDocuments)) + .pipe(es.wait(onEnd)); + }); }, serialize: function() { var res = this.getAttributes({ diff --git a/src/refine-view/index.jade b/src/refine-view/index.jade index 84183ca859d..e7a48a0100f 100644 --- a/src/refine-view/index.jade +++ b/src/refine-view/index.jade @@ -1,6 +1,10 @@ -.refine-view-container: .row: .col-md-12 - form: .input-group(data-hook='refine-input-group') +.refine-view-container + .query-input-container: .row: .col-md-12: form: .input-group(data-hook='refine-input-group') input.form-control.input-sm(type='text', data-hook='refine-input') span.input-group-btn button.btn.btn-info.btn-sm(type='button', data-hook='refine-button') Apply button.btn.btn-default.btn-sm(type='button', data-hook='reset-button') Reset + div(data-hook='sampling-message-subview') + + //- .sampling-message + //- span Query returned 9867 documents. This report is based on a sample of 1000 documents (10.13%). diff --git a/src/refine-view/index.js b/src/refine-view/index.js index e8a0103b6fb..206932b98e4 100644 --- a/src/refine-view/index.js +++ b/src/refine-view/index.js @@ -3,6 +3,8 @@ var EditableQuery = require('../models/editable-query'); var $ = require('jquery'); var EJSON = require('mongodb-extended-json'); var Query = require('mongodb-language-model').Query; +var SamplingMessageView = require('../sampling-message'); + // var debug = require('debug')('scout:refine-view:index'); module.exports = AmpersandView.extend({ @@ -55,6 +57,17 @@ module.exports = AmpersandView.extend({ } ] }, + subviews: { + sampling_message: { + hook: 'sampling-message-subview', + prepareView: function(el) { + return new SamplingMessageView({ + el: el, + parent: this + }); + } + } + }, events: { 'click [data-hook=refine-button]': 'refineClicked', 'click [data-hook=reset-button]': 'resetClicked', diff --git a/src/refine-view/index.less b/src/refine-view/index.less index 4df03906e44..b359a4f0e7a 100644 --- a/src/refine-view/index.less +++ b/src/refine-view/index.less @@ -1,19 +1,28 @@ .refine-view-container { - background: @gray8; - padding: 12px 25px; position: relative; z-index: 1; - input[type='text'] { - font-family: @font-family-monospace; - background: @pw; - height: 28px; - & + .input-group-btn { - padding-left: 10px; + .query-input-container { + padding: 12px 25px; + background: @gray8; - &:last-child > .btn { - margin-left: 2px; + input[type='text'] { + font-family: @font-family-monospace; + background: @pw; + height: 28px; + & + .input-group-btn { + padding-left: 10px; + + &:last-child > .btn { + margin-left: 2px; + } } } } + .sampling-message { + font-size: 14px; + font-weight: 200; + padding: 5px 25px; + border-bottom: 2px solid @gray7 + } } diff --git a/src/sampling-message/index.jade b/src/sampling-message/index.jade index 1f272cae0fa..76fc5a49af3 100644 --- a/src/sampling-message/index.jade +++ b/src/sampling-message/index.jade @@ -1,5 +1,5 @@ -.sampling-status - span(data-hook='is_sample') This report is based on a sample of  - span(data-hook='is_query') Your query returned  +.sampling-message + span(data-hook='is_sample') Query returned ### documents. This report is based on a sample of  + span(data-hook='is_query') Query returned  span(data-hook='sample_size_message') |. diff --git a/src/sampling-message/index.js b/src/sampling-message/index.js index c51a3aeee91..4a2e07948f0 100644 --- a/src/sampling-message/index.js +++ b/src/sampling-message/index.js @@ -2,6 +2,7 @@ var View = require('ampersand-view'); var pluralize = require('pluralize'); var format = require('util').format; var app = require('ampersand-app'); +var debug = require('debug')('scout:sampling-message:index'); var SamplingMessageView = View.extend({ session: { @@ -50,21 +51,22 @@ var SamplingMessageView = View.extend({ sample_size_message: { deps: ['schema_sample_size'], fn: function() { - return format('%d %s', this.parent.schema.sample_size, - pluralize('document', this.parent.schema.sample_size)); + return format('%d %s', this.parent.parent.schema.sample_size, + pluralize('document', this.parent.parent.schema.sample_size)); } } }, template: require('./index.jade'), initialize: function() { - this.listenTo(this.parent.schema, 'request', this.hide.bind(this)); - this.listenTo(this.parent.schema, 'sync', this.show.bind(this)); + this.listenTo(this.parent.parent.schema, 'request', this.hide.bind(this)); + this.listenTo(this.parent.parent.schema, 'sync', this.show.bind(this)); }, hide: function() { this.schema_sample_size = 0; }, show: function() { - this.schema_sample_size = this.parent.schema.sample_size; + debug('actual count', this.parent.parent.schema.total); + this.schema_sample_size = this.parent.parent.schema.sample_size; } }); module.exports = SamplingMessageView; diff --git a/src/sidebar/index.jade b/src/sidebar/index.jade index fceda4a02a9..d95e7b07a90 100644 --- a/src/sidebar/index.jade +++ b/src/sidebar/index.jade @@ -1,5 +1,6 @@ div .sidebar.panel div.compass-logo + div(data-hook='instance-properties-subview') div(data-hook='collection-filter-subview') div(data-hook='collection-list-subview') diff --git a/src/sidebar/index.js b/src/sidebar/index.js index 3ba3da2d8ec..c3cdcc3e527 100644 --- a/src/sidebar/index.js +++ b/src/sidebar/index.js @@ -1,7 +1,9 @@ var View = require('ampersand-view'); var mousetrap = require('mousetrap'); +var InstancePropertiesView = require('./instance-properties'); var CollectionFilterView = require('./collection-filter'); var CollectionListView = require('./collection-list'); +var app = require('ampersand-app'); var SidebarView = View.extend({ props: { @@ -33,6 +35,16 @@ var SidebarView = View.extend({ }, template: require('./index.jade'), subviews: { + instance_properties: { + hook: 'instance-properties-subview', + prepareView: function(el) { + return new InstancePropertiesView({ + el: el, + parent: this, + instance: app.instance + }); + } + }, collections_filter: { hook: 'collection-filter-subview', prepareView: function(el) { diff --git a/src/sidebar/instance-properties.jade b/src/sidebar/instance-properties.jade new file mode 100644 index 00000000000..6ad83223cf9 --- /dev/null +++ b/src/sidebar/instance-properties.jade @@ -0,0 +1,14 @@ +div.instance-properties + span.hostname(data-hook='hostname') + span.version + | version  + span(data-hook='version') + div.db-stats + span.num-databases + strong(data-hook='num-databases') + |  DBs + span.num-collections + strong(data-hook='num-collections') + |  Colls. + button.refresh-collections + i.fa.fa-repeat diff --git a/src/sidebar/instance-properties.js b/src/sidebar/instance-properties.js new file mode 100644 index 00000000000..549d3efde5e --- /dev/null +++ b/src/sidebar/instance-properties.js @@ -0,0 +1,41 @@ +var View = require('ampersand-view'); +var app = require('ampersand-app'); +// var debug = require('debug')('scout:sidebar:instace-properties'); + +var InstancePropertiesView = module.exports = View.extend({ + template: require('./instance-properties.jade'), + session: { + hostname: 'string', + version: 'string', + numDatabases: 'number', + numCollections: 'number', + instance: 'state' + }, + bindings: { + 'instance.host.hostname': { + type: 'text', + hook: 'hostname' + }, + 'instance.build.version': { + type: 'text', + hook: 'version' + }, + numCollections: { + type: 'text', + hook: 'num-collections' + }, + numDatabases: { + type: 'text', + hook: 'num-databases' + } + }, + initialize: function() { + this.listenTo(app.instance, 'sync', this.onInstanceFetched); + }, + onInstanceFetched: function() { + this.numDatabases = app.instance.databases.length; + this.numCollections = app.instance.collections.length; + } +}); + +module.exports = InstancePropertiesView; diff --git a/styles/palette.less b/styles/palette.less index 840a0261318..29d27c15da6 100644 --- a/styles/palette.less +++ b/styles/palette.less @@ -19,8 +19,9 @@ @gray7: #ebebed; @gray8: #f5f6f7; -@slate0: #4c5259; -@slate1: #70757a; +@slate0: #42494f; +@slate1: #4c5259; +@slate2: #70757a; @blue3: #5b81a9; // TODO: deprecate @blue4: #84a1bf; // TODO: deprecate diff --git a/styles/sidebar.less b/styles/sidebar.less index 90cd562b8d5..4660e837e95 100644 --- a/styles/sidebar.less +++ b/styles/sidebar.less @@ -1,4 +1,4 @@ -@sidebar-bg: @slate0; +@sidebar-bg: @slate1; @sidebar-width: 220px; .sidebar { @@ -9,14 +9,59 @@ margin-bottom: 0; .compass-logo { - background: @slate1 url("images/logo-compass-on-dark.png") center center no-repeat; + background: @slate0 url("images/logo-compass-on-dark.png") center center no-repeat; background-size: 162px 28px; width: @sidebar-width; - height: 72px; + height: 60px; + } + + .instance-properties { + position: absolute; + top: 78px; + left: 30px; + + .hostname { + display: block; + font-size: 16px; + line-height: 16px; + font-weight: 200; + color: @pw; + } + .version { + display: block; + font-size: 14px; + font-weight: normal; + color: @gray5; + } + .db-stats { + color: @gray5; + margin-top: 6px; + + .num-databases, + .num-collections { + display: inline-block; + border-right: 1px solid @slate2; + padding-right: 10px; + margin-right: 10px; + } + strong { + color: @pw; + font-weight: bold; + } + button.refresh-collections { + background: none; + border: none; + color: @pw; + font-size: 14px; + } + } } .list-filter { - padding: 12px; + position: absolute; + top: 156px; + width: 100%; + padding-bottom: 12px; box-shadow: 0 2px 0 rgba(0,0,0,0.2); i.search { @@ -27,16 +72,19 @@ color: @gray5; } input { - padding: 5px 5px 5px 30px; + padding: 0; + margin-left: 30px; + margin-top: 7px; height: auto; - background: @slate0; + background: @slate1; color: @pw; - width: 100%; - border: 1px solid lighten(@slate0, 5%); - } - input[type=search] { - border-radius: 18px; + width: 174px; + border: none; + border-bottom: 1px solid lighten(@slate1, 5%); } + // input[type=search] { + // border-radius: 18px; + // } } .panel-title { @@ -45,7 +93,7 @@ .list-group { line-height: 24px; position: absolute; - top: 128px; + top: 196px; bottom: 0px; overflow-y: auto; width: 100%; @@ -60,10 +108,10 @@ color: @gray5; &:hover { - background: lighten(@slate0, 5%); + background: lighten(@slate1, 5%); a { color: @pw; - border-left: 4px solid lighten(@slate0, 5%); + border-left: 4px solid lighten(@slate1, 5%); text-decoration: none; } } From 7c05d3fd163f2f99059f032f3d2ef0c2eae0291d Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Thu, 8 Oct 2015 15:20:33 -0400 Subject: [PATCH 2/7] made sampling message dynamic --- src/models/sampled-schema.js | 14 ++++-- src/refine-view/index.less | 2 +- src/sampling-message/index.jade | 15 +++++-- src/sampling-message/index.js | 80 +++++++++++++++++++-------------- 4 files changed, 68 insertions(+), 43 deletions(-) diff --git a/src/models/sampled-schema.js b/src/models/sampled-schema.js index ee8031a92cc..42a3677e31c 100644 --- a/src/models/sampled-schema.js +++ b/src/models/sampled-schema.js @@ -123,6 +123,7 @@ module.exports = Schema.extend({ }; var start = new Date(); var timeAtFirstDoc; + var avgTimePerDoc = 0; var erroredOnDocs = []; // No results found @@ -143,6 +144,7 @@ module.exports = Schema.extend({ if (!timeAtFirstDoc) { timeAtFirstDoc = new Date(); } + var timePerDoc = new Date(); try { model.parse(doc); } catch (err) { @@ -152,6 +154,9 @@ module.exports = Schema.extend({ schema: model.serialize() }); } + timePerDoc = new Date() - timePerDoc; + avgTimePerDoc += timePerDoc; + debug('avg time', timePerDoc); cb(null, doc); }; @@ -170,12 +175,13 @@ module.exports = Schema.extend({ metrics.track('Schema: Complete', { Duration: new Date() - start, 'Total Document Count': model.total, - 'Document Count': model.documents, + 'Sample Size': model.documents.length, 'Errored Document Count': erroredOnDocs.length, 'Time to First Doc': timeAtFirstDoc - start, - 'Schema Height': model.height, // # of top level keys - 'Schema Width': model.width, // max nesting depth - 'Schema Sparsity': model.sparsity // lots of fields missing or consistent + 'Average Time Per Doc': avgTimePerDoc / model.documents.length + // 'Schema Height': model.height, // # of top level keys + // 'Schema Width': model.width, // max nesting depth + // 'Schema Sparsity': model.sparsity // lots of fields missing or consistent }); options.success({}); }; diff --git a/src/refine-view/index.less b/src/refine-view/index.less index b359a4f0e7a..b8f22def19e 100644 --- a/src/refine-view/index.less +++ b/src/refine-view/index.less @@ -20,7 +20,7 @@ } } .sampling-message { - font-size: 14px; + font-size: 12px; font-weight: 200; padding: 5px 25px; border-bottom: 2px solid @gray7 diff --git a/src/sampling-message/index.jade b/src/sampling-message/index.jade index 76fc5a49af3..357290ff3ea 100644 --- a/src/sampling-message/index.jade +++ b/src/sampling-message/index.jade @@ -1,5 +1,12 @@ .sampling-message - span(data-hook='is_sample') Query returned ### documents. This report is based on a sample of  - span(data-hook='is_query') Query returned  - span(data-hook='sample_size_message') - |. + if is_sample + | Query returned  + b #{formatted_total_count} + |  #{total_count_document}. + | This report is based on a sample of  + b #{formatted_sample_size} + |  #{sample_size_document}. + else + | Query returned  + b #{formatted_sample_size} + |  #{sample_size_document}. diff --git a/src/sampling-message/index.js b/src/sampling-message/index.js index 4a2e07948f0..7f2d50c4f46 100644 --- a/src/sampling-message/index.js +++ b/src/sampling-message/index.js @@ -2,71 +2,83 @@ var View = require('ampersand-view'); var pluralize = require('pluralize'); var format = require('util').format; var app = require('ampersand-app'); +var numeral = require('numeral'); var debug = require('debug')('scout:sampling-message:index'); var SamplingMessageView = View.extend({ + template: require('./index.jade'), session: { parent: 'state' }, - bindings: { - visible: { - type: 'booleanClass', - no: 'hidden' - }, - sample_size_message: { - hook: 'sample_size_message' - }, - is_sample: [ - { - hook: 'is_sample', - type: 'booleanClass', - no: 'hidden' - }, - { - hook: 'is_query', - type: 'booleanClass', - yes: 'hidden' - } - ] - }, props: { - schema_sample_size: { + sample_size: { + type: 'number', + default: 0 + }, + total_count: { type: 'number', default: 0 } }, derived: { visible: { - deps: ['schema_sample_size'], + deps: ['sample_size'], fn: function() { - return this.schema_sample_size > 0; + return this.sample_size > 0; } }, is_sample: { - deps: ['schema_sample_size'], + deps: ['sample_size'], + fn: function() { + return this.sample_size === app.queryOptions.size; + } + }, + formatted_total_count: { + deps: ['total_count'], fn: function() { - return this.schema_sample_size === app.queryOptions.size; + return numeral(this.total_count).format('0,0'); } }, - sample_size_message: { - deps: ['schema_sample_size'], + formatted_sample_size: { + deps: ['sample_size'], fn: function() { - return format('%d %s', this.parent.parent.schema.sample_size, - pluralize('document', this.parent.parent.schema.sample_size)); + return numeral(this.sample_size).format('0,0'); + } + }, + total_count_document: { + deps: ['total_count'], + fn: function() { + return pluralize('document', this.total_count); + } + }, + sample_size_document: { + deps: ['sample_size'], + fn: function() { + return pluralize('document', this.sample_size); } } }, - template: require('./index.jade'), + bindings: { + visible: { + type: 'booleanClass', + no: 'hidden' + } + }, initialize: function() { this.listenTo(this.parent.parent.schema, 'request', this.hide.bind(this)); this.listenTo(this.parent.parent.schema, 'sync', this.show.bind(this)); }, hide: function() { - this.schema_sample_size = 0; + this.sample_size = 0; + this.total_count = 0; }, show: function() { - debug('actual count', this.parent.parent.schema.total); - this.schema_sample_size = this.parent.parent.schema.sample_size; + this.sample_size = this.parent.parent.schema.sample_size; + this.total_count = this.parent.parent.schema.total; + this.render(); + }, + render: function() { + this.renderWithTemplate(this); } }); module.exports = SamplingMessageView; From 670caed4afc43e36785fb5970ba8d610ccb44817 Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Thu, 8 Oct 2015 16:09:15 -0400 Subject: [PATCH 3/7] calculate avgTimePerDoc --- src/models/sampled-schema.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/models/sampled-schema.js b/src/models/sampled-schema.js index 42a3677e31c..93d84984b76 100644 --- a/src/models/sampled-schema.js +++ b/src/models/sampled-schema.js @@ -17,9 +17,9 @@ var metrics = { err = null; } if (err) { - debugMetrics('metrics error: ', err, obj); + debugMetrics(label, err, obj); } else { - debugMetrics('metrics log: ', obj); + debugMetrics(label, obj); } } }; @@ -123,7 +123,6 @@ module.exports = Schema.extend({ }; var start = new Date(); var timeAtFirstDoc; - var avgTimePerDoc = 0; var erroredOnDocs = []; // No results found @@ -144,7 +143,6 @@ module.exports = Schema.extend({ if (!timeAtFirstDoc) { timeAtFirstDoc = new Date(); } - var timePerDoc = new Date(); try { model.parse(doc); } catch (err) { @@ -154,9 +152,6 @@ module.exports = Schema.extend({ schema: model.serialize() }); } - timePerDoc = new Date() - timePerDoc; - avgTimePerDoc += timePerDoc; - debug('avg time', timePerDoc); cb(null, doc); }; @@ -172,13 +167,16 @@ module.exports = Schema.extend({ model.documents.trigger('sync'); // @note (imlucas): Any other metrics? Feedback on `Schema *`? + var totalTime = new Date() - start; + var timeToFirstDoc = timeAtFirstDoc - start; + metrics.track('Schema: Complete', { - Duration: new Date() - start, + Duration: totalTime, 'Total Document Count': model.total, 'Sample Size': model.documents.length, 'Errored Document Count': erroredOnDocs.length, - 'Time to First Doc': timeAtFirstDoc - start, - 'Average Time Per Doc': avgTimePerDoc / model.documents.length + 'Time to First Doc': timeToFirstDoc, + 'Average Time Per Doc': (totalTime - timeToFirstDoc) / model.documents.length // 'Schema Height': model.height, // # of top level keys // 'Schema Width': model.width, // max nesting depth // 'Schema Sparsity': model.sparsity // lots of fields missing or consistent From 86a18b989f65cb46088dce5f704e7b64ea057f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Sch=C3=A4ffner-Gurney?= Date: Fri, 9 Oct 2015 13:24:32 -0400 Subject: [PATCH 4/7] sidebar style tweaks --- src/sidebar/instance-properties.jade | 2 +- styles/sidebar.less | 60 +++++++++++++++++++++------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/sidebar/instance-properties.jade b/src/sidebar/instance-properties.jade index 6ad83223cf9..5dc146820bb 100644 --- a/src/sidebar/instance-properties.jade +++ b/src/sidebar/instance-properties.jade @@ -9,6 +9,6 @@ div.instance-properties |  DBs span.num-collections strong(data-hook='num-collections') - |  Colls. + |  Collections button.refresh-collections i.fa.fa-repeat diff --git a/styles/sidebar.less b/styles/sidebar.less index 4660e837e95..00c0940b4aa 100644 --- a/styles/sidebar.less +++ b/styles/sidebar.less @@ -18,7 +18,7 @@ .instance-properties { position: absolute; top: 78px; - left: 30px; + left: 10px; .hostname { display: block; @@ -29,13 +29,16 @@ } .version { display: block; - font-size: 14px; + font-size: 13px; + line-height: 14px; font-weight: normal; color: @gray5; } .db-stats { color: @gray5; - margin-top: 6px; + margin-top: 8px; + font-style: 13px; + line-height: 13px; .num-databases, .num-collections { @@ -45,14 +48,22 @@ margin-right: 10px; } strong { - color: @pw; + color: @gray7; font-weight: bold; } button.refresh-collections { background: none; border: none; - color: @pw; - font-size: 14px; + color: @gray7; + font-size: 13px; + border-radius: 2px; + padding: 4px 5px; + transition: all 150ms ease; + + &:hover { + background: @slate0; + color: @pw; + } } } } @@ -61,7 +72,7 @@ position: absolute; top: 156px; width: 100%; - padding-bottom: 12px; + // padding-bottom: 12px; box-shadow: 0 2px 0 rgba(0,0,0,0.2); i.search { @@ -69,18 +80,37 @@ position: absolute; margin-top: 9px; margin-left: 10px; - color: @gray5; + color: @gray7; } + // input { + // padding: 0; + // margin-left: 30px; + // margin-top: 7px; + // height: auto; + // background: @slate1; + // color: @pw; + // width: 174px; + // border: none; + // border-bottom: 1px solid lighten(@slate1, 5%); + // } + // input[type=search] { + // border-radius: 18px; + // } input { - padding: 0; - margin-left: 30px; - margin-top: 7px; + padding: 7px 5px 5px 30px; height: auto; - background: @slate1; + background: lighten(@slate1, 5%); color: @pw; - width: 174px; + width: 100%; border: none; - border-bottom: 1px solid lighten(@slate1, 5%); + transition: all 150ms ease; + + &:focus { + background: lighten(@slate1, 8%); + } + } + input::-webkit-input-placeholder { + color: @gray5; } // input[type=search] { // border-radius: 18px; @@ -93,7 +123,7 @@ .list-group { line-height: 24px; position: absolute; - top: 196px; + top: 189px; bottom: 0px; overflow-y: auto; width: 100%; From b294121f5a428b02a4b318b6f334d69cdb64b8ac Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Fri, 9 Oct 2015 16:20:27 -0400 Subject: [PATCH 5/7] set up ipc between main and render process zero-state for no collections now has functioning link to re-open the connect dialog. --- src/app.js | 16 ++++++++-------- src/electron/config/windows.js | 4 ++-- src/electron/index.js | 2 +- src/electron/window-manager.js | 6 ++++++ src/home/index.js | 4 +++- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/app.js b/src/app.js index c0f490b1436..c37e49c024c 100644 --- a/src/app.js +++ b/src/app.js @@ -223,13 +223,6 @@ var Application = View.extend({ event.preventDefault(); this.router.history.navigate(pathname); } - }, - sendMessage: function(msg) { - ipc.send('message', msg); - }, - onMessageReceived: function(msg) { - debug('message received from main process:', msg); - this.trigger(msg); } }); @@ -262,6 +255,13 @@ app.extend({ isFeatureEnabled: function(id) { return FEATURES[id] === true; }, + sendMessage: function(msg) { + ipc.send('message', msg); + }, + onMessageReceived: function(msg) { + debug('message received from main process:', msg); + this.trigger(msg); + }, init: function() { domReady(function() { state.render(); @@ -298,7 +298,7 @@ app.extend({ }); }); // set up ipc - ipc.on('message', state.onMessageReceived.bind(this)); + ipc.on('message', this.onMessageReceived.bind(this)); }, navigate: state.navigate.bind(state) }); diff --git a/src/electron/config/windows.js b/src/electron/config/windows.js index 243ddf06418..e00d5e568c2 100644 --- a/src/electron/config/windows.js +++ b/src/electron/config/windows.js @@ -5,8 +5,8 @@ /** * The outer dimensions to use for new windows. */ -exports.DEFAULT_WIDTH = 1024; -exports.DEFAULT_HEIGHT = 700; +exports.DEFAULT_WIDTH = 1280; +exports.DEFAULT_HEIGHT = 800; /** * The outer window dimensions to use for new dialog diff --git a/src/electron/index.js b/src/electron/index.js index 5e15a4a802b..b57945ee1d5 100644 --- a/src/electron/index.js +++ b/src/electron/index.js @@ -4,11 +4,11 @@ // will be created in `/tmp/Compass` // (`~\AppData\Local\Temp\Compass` on Windows). require('./crash-reporter'); +var debug = require('debug')('electron:index'); if (!require('electron-squirrel-startup')) { var app = require('app'); var serverctl = require('./scout-server-ctl'); - var debug = require('debug')('scout-electron'); app.on('window-all-closed', function() { debug('All windows closed. Quitting app.'); diff --git a/src/electron/window-manager.js b/src/electron/window-manager.js index 51c81f649c7..53036db1cb5 100644 --- a/src/electron/window-manager.js +++ b/src/electron/window-manager.js @@ -128,3 +128,9 @@ app.on('show connect dialog', function(opts) { app.on('ready', function() { app.emit('show connect dialog'); }); + +var ipc = require('ipc'); +ipc.on('message', function(event, msg) { + debug('message received in main process', msg); + app.emit(msg); +}); diff --git a/src/home/index.js b/src/home/index.js index f1fa7676940..c075d48f55c 100644 --- a/src/home/index.js +++ b/src/home/index.js @@ -48,7 +48,7 @@ var HomeView = View.extend({ app.instance.fetch(); }, onInstanceFetched: function() { - if(_.size(app.instance.collections) === 0) { + if (app.instance.collections.length === 0) { this.showNoCollectionsZeroState = true; } else { this.showDefaultZeroState = true; @@ -82,6 +82,8 @@ var HomeView = View.extend({ }, onClickShowConnectWindow: function() { // code to close current connection window and open connect dialog + app.sendMessage('show connect dialog'); + window.close(); }, template: require('./index.jade'), subviews: { From e2c6165acc2132b0a39edb28c17cef182c7ad09f Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Fri, 9 Oct 2015 18:27:36 -0400 Subject: [PATCH 6/7] reload collections, and spin button --- package.json | 3 ++- src/sidebar/instance-properties.jade | 4 ++-- src/sidebar/instance-properties.js | 24 +++++++++++++++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b99741b07c9..bf6c6e6b542 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "ampersand-state", "scout-server", "glob", - "electron-squirrel-startup" + "electron-squirrel-startup", + "ipc" ] }, "fonts": [ diff --git a/src/sidebar/instance-properties.jade b/src/sidebar/instance-properties.jade index 5dc146820bb..da3e380d234 100644 --- a/src/sidebar/instance-properties.jade +++ b/src/sidebar/instance-properties.jade @@ -10,5 +10,5 @@ div.instance-properties span.num-collections strong(data-hook='num-collections') |  Collections - button.refresh-collections - i.fa.fa-repeat + button.refresh-collections(data-hook='refresh-button') + i.fa(data-hook='refresh-icon') diff --git a/src/sidebar/instance-properties.js b/src/sidebar/instance-properties.js index 549d3efde5e..d5e5728264a 100644 --- a/src/sidebar/instance-properties.js +++ b/src/sidebar/instance-properties.js @@ -1,6 +1,7 @@ var View = require('ampersand-view'); var app = require('ampersand-app'); // var debug = require('debug')('scout:sidebar:instace-properties'); +var _ = require('lodash'); var InstancePropertiesView = module.exports = View.extend({ template: require('./instance-properties.jade'), @@ -9,7 +10,11 @@ var InstancePropertiesView = module.exports = View.extend({ version: 'string', numDatabases: 'number', numCollections: 'number', - instance: 'state' + instance: 'state', + is_fetching: { + type: 'boolean', + default: true + } }, bindings: { 'instance.host.hostname': { @@ -27,14 +32,31 @@ var InstancePropertiesView = module.exports = View.extend({ numDatabases: { type: 'text', hook: 'num-databases' + }, + is_fetching: { + type: 'booleanClass', + hook: 'refresh-icon', + yes: ['fa-refresh', 'fa-spin'], + no: 'fa-repeat' } }, + events: { + 'click button[data-hook=refresh-button]': 'onRefreshButtonClicked' + }, initialize: function() { this.listenTo(app.instance, 'sync', this.onInstanceFetched); }, onInstanceFetched: function() { + // delay switching the spinner back to static for 500ms, otherwise the reload is not noticable + _.delay(function() { + this.is_fetching = false; + }.bind(this), 500); this.numDatabases = app.instance.databases.length; this.numCollections = app.instance.collections.length; + }, + onRefreshButtonClicked: function() { + app.instance.fetch(); + this.is_fetching = true; } }); From bc8eb90369db7a90793f5acb0a254d44d8db5387 Mon Sep 17 00:00:00 2001 From: Thomas Rueckstiess Date: Fri, 9 Oct 2015 18:47:42 -0400 Subject: [PATCH 7/7] removed unneeded props. --- src/sidebar/instance-properties.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sidebar/instance-properties.js b/src/sidebar/instance-properties.js index d5e5728264a..2fb4dbd5ca3 100644 --- a/src/sidebar/instance-properties.js +++ b/src/sidebar/instance-properties.js @@ -6,8 +6,6 @@ var _ = require('lodash'); var InstancePropertiesView = module.exports = View.extend({ template: require('./instance-properties.jade'), session: { - hostname: 'string', - version: 'string', numDatabases: 'number', numCollections: 'number', instance: 'state',