From b81e244db26f4572fcce25a8d2322d1369d0e236 Mon Sep 17 00:00:00 2001 From: Steven Esser Date: Mon, 22 Apr 2019 16:22:41 -0700 Subject: [PATCH] Rename Dashboad to FileDashboard * Add dirs_count to first row * Add unique copyright holders to first row * Add file/mime type chart to second row * Add copyright holders chart to second row Addresses: #366 Signed-off-by: Steven Esser --- assets/app/css/main.css | 2 +- assets/app/js/controllers/dashboard.js | 295 --------------------- assets/app/js/controllers/fileDashboard.js | 227 ++++++++++++++++ assets/app/js/models/file.js | 1 + assets/app/js/renderer.js | 18 +- index.html | 58 ++-- 6 files changed, 258 insertions(+), 343 deletions(-) delete mode 100644 assets/app/js/controllers/dashboard.js create mode 100644 assets/app/js/controllers/fileDashboard.js diff --git a/assets/app/css/main.css b/assets/app/css/main.css index 54f51184..954db12c 100644 --- a/assets/app/css/main.css +++ b/assets/app/css/main.css @@ -548,7 +548,7 @@ div.dataTables_scrollHead th:first-child { width: 100%; } -#tab-dashboard{ +#tab-file-dashboard{ background-color: #FAFAFB; } diff --git a/assets/app/js/controllers/dashboard.js b/assets/app/js/controllers/dashboard.js deleted file mode 100644 index 6308551c..00000000 --- a/assets/app/js/controllers/dashboard.js +++ /dev/null @@ -1,295 +0,0 @@ -/* - # - # Copyright (c) 2017 nexB Inc. and others. All rights reserved. - # https://nexb.com and https://github.com/nexB/scancode-workbench/ - # The ScanCode software is licensed under the Apache License version 2.0. - # ScanCode is a trademark of nexB Inc. - # - # You may not use this software except in compliance with the License. - # You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0 - # Unless required by applicable law or agreed to in writing, software distributed - # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - # CONDITIONS OF ANY KIND, either express or implied. See the License for the - # specific language governing permissions and limitations under the License. - # - */ - -const Sequelize = require('sequelize'); -const Progress = require('../helpers/progress'); -const Utils = require('../helpers/utils'); -const Controller = require('./controller'); - -const LEGEND_COLORS = [ - '#B3F82F', - '#FFA330', - '#EC2D95', - '#2BE189', - '#9095F0', - '#2AD0D8', - '#AAB2BD', - '#3499DB' -]; - -const LEGEND_LIMIT = 8; - -/** - * The view responsible for displaying the summary information from ScanCode - * Scan data - */ -class Dashboard extends Controller { - constructor(dashboardId, workbenchDB) { - super(dashboardId, workbenchDB); - - this.totalFilesScanned = $('#total-files').find('.title'); - this.uniqueLicenses = $('#unique-licenses').find('.title'); - this.uniqueCopyrights = $('#unique-copyrights').find('.title'); - this.totalPackages = $('#total-packages').find('.title'); - - this.totalFilesProgressbar = - new Progress('#total-files .title', {size: 25}); - this.uniqueLicensesProgressbar = - new Progress('#unique-licenses .title', {size: 25}); - this.uniqueCopyrightsProgressbar = - new Progress('#unique-copyrights .title', {size: 25}); - this.totalPackagesProgressbar = - new Progress('#total-packages .title', {size: 25}); - this.sourceLanguageChartProgressbar = - new Progress('#source-chart .content', {size: 50}); - this.licenseCategoryChartProgressbar = - new Progress('#license-category-chart .content', {size: 50}); - this.licenseKeyChartProgressbar = - new Progress('#license-key-chart .content', {size: 50}); - this.packagesTypeChartProgressbar = - new Progress('#packages-type-chart .content', {size: 50}); - - this.sourceLanguageChart = c3.generate({ - bindto: '#source-chart .chart', - data: { - columns: [], - type: 'pie', - order: 'desc', - }, - color: { - pattern: LEGEND_COLORS - }, - }); - - this.licenseCategoryChart = c3.generate({ - bindto: '#license-category-chart .chart', - data: { - columns: [], - type: 'pie', - order: 'desc', - }, - color: { - pattern: LEGEND_COLORS - } - }); - - this.licenseKeyChart = c3.generate({ - bindto: '#license-key-chart .chart', - data: { - columns: [], - type: 'pie', - order: 'desc', - }, - color: { - pattern: LEGEND_COLORS - } - }); - - this.packagesTypeChart = c3.generate({ - bindto: '#packages-type-chart .chart', - data: { - columns: [], - type: 'bar', - order: 'desc', - }, - color: { - pattern: LEGEND_COLORS - } - }); - } - - reload() { - this.needsReload(false); - - // Get # files scanned at a certain path - this.totalFilesProgressbar.showIndeterminate(); - this.db().sync - .then((db) => db.File.findOne({where: {path: this.selectedPath()}})) - .then((row) => { - const files_count = row.type === 'directory' ? row.files_count : 1; - this.totalFilesScanned.text(files_count); - this.totalFilesProgressbar.hide(); - }); - - // Get total unique licenses detected at a certain path - this.uniqueLicensesProgressbar.showIndeterminate(); - this.db().sync - .then((db) => db.File.findAll({where: {path: {$like: `${this.selectedPath()}%`}}})) - .then((files) => files.map((val) => val.id)) - .then((fileIds) => this.db().sync - .then((db) => db.License.findAll({where: {fileId: fileIds}})) - .then((licenses) => licenses.map((val) => val.key)) - .then((keys) => this.uniqueLicenses.text(new Set(keys).size))) - .then(() => this.uniqueLicensesProgressbar.hide()); - - // Get total unique copyright statements detected at a certain path - this.uniqueCopyrightsProgressbar.showIndeterminate(); - this.db().sync - .then((db) => db.File.findAll({where: {path: {$like: `${this.selectedPath()}%`}}})) - .then((files) => files.map((val) => val.id)) - .then((fileIds) => this.db().sync - .then((db) => db.Copyright.findAll({where: {fileId: fileIds}})) - .then((copyrights) => copyrights.map((val) => val.statements)) - .then((statements) => statements.map((val) => val.pop())) - .then((statements) => this.uniqueCopyrights.text(new Set(statements).size))) - .then(() => this.uniqueCopyrightsProgressbar.hide()); - - // Get total number of packages detected - this.totalPackagesProgressbar.showIndeterminate(); - this.db().sync - .then((db) => db.File.findAll({where: {path: {$like: `${this.selectedPath()}%`}}})) - .then((files) => files.map((val) => val.id)) - .then((fileIds) => this.db().sync - .then((db) => db.Package.findAll({where: {fileId: fileIds}})) - .then((packages) => packages.map((val) => val.type)) - .then((types) => this.totalPackages.text(types.length))) - .then(() => this.totalPackagesProgressbar.hide()); - - // Get unique programming languages detected - this.sourceLanguageChartData = this._loadChartData('programming_language', this.selectedPath()); - - // Get license categories detected - this.licenseCategoryChartData = this._loadChartData('license_category', this.selectedPath()); - - // Get license keys detected - this.licenseKeyChartData = this._loadChartData('license_key', this.selectedPath()); - - // Get package types detected - this.packagesTypeChartData = this._loadChartData('packages_type', this.selectedPath()); - } - - redraw() { - if (this.needsReload()) { - this.reload(); - } - - // Get unique programming languages detected - this.sourceLanguageChartProgressbar.showIndeterminate(); - this.sourceLanguageChartData - .then((data) => this.sourceLanguageChart.load({ - columns: data, - unload: true, - done: () => { - this.sourceLanguageChartProgressbar.hide(); - this.sourceLanguageChart.hide('No Value Detected'); - this.sourceLanguageChart.flush(); - } - })); - - // Get license categories detected - this.licenseCategoryChartProgressbar.showIndeterminate(); - this.licenseCategoryChartData - .then((data) => this.licenseCategoryChart.load({ - columns: data, - unload: true, - done: () => { - this.licenseCategoryChartProgressbar.hide(); - this.licenseCategoryChart.hide('No Value Detected'); - this.licenseCategoryChart.flush(); - } - })); - - // Get license keys detected - this.licenseKeyChartProgressbar.showIndeterminate(); - this.licenseKeyChartData - .then((data) => this.licenseKeyChart.load({ - columns: data, - unload:true, - done: () => { - this.licenseKeyChartProgressbar.hide(); - this.licenseKeyChart.hide('No Value Detected'); - this.licenseKeyChart.flush(); - } - })); - - // Get package types detected - this.packagesTypeChartProgressbar.showIndeterminate(); - this.packagesTypeChartData - .then((data) => this.packagesTypeChart.load({ - columns: data, - unload: true, - done: () => { - this.packagesTypeChartProgressbar.hide(); - this.packagesTypeChart.hide('No Value Detected'); - this.packagesTypeChart.flush(); - } - })); - } - - _loadChartData(attribute, parentPath) { - let where = {}; - if (attribute === 'packages_type') { - where = { - type: { - $or: ['file', 'directory'] - } - }; - } else { - where = { - type: {$eq: 'file'} - }; - } - - if (parentPath) { - where.$and = [{path: {$like: `${parentPath}%`}}]; - } - - return this.db().sync.then((db) => { - return db.FlatFile - .findAll({ - attributes: [ - Sequelize.fn('TRIM', Sequelize.col(attribute)), - attribute, - 'type' - ], - where: where - }) - .then((data) => Utils.getAttributeValues(data, attribute)) - .then((data) => Dashboard.formatData(data)) - .then((data) => Dashboard.limitData(data, LEGEND_LIMIT)); - }); - } - - static limitData(data, limit) { - // TODO: Use partitioning (like in quicksort) to find top "limit" - // more efficiently. - // Sort data by count - return data.sort((a,b) => (a[1] > b[1]) ? 1 : -1) - .map((dataPair, i) => { - if (data.length - i >= limit) { - return ['other', dataPair[1]]; - } else { - return dataPair; - } - }); - } - - // Formats data for c3: [[key1, count1], [key2, count2], ...] - static formatData(names) { - // Sum the total number of times the name appears - const count = {}; - $.each(names, (i, name) => { - count[name] = count[name] + 1 || 1; - }); - - // Transform license count into array of objects with license name & count - return $.map(count, (val, key) => { - return [[key, val]]; - }); - } -} - -module.exports = Dashboard; diff --git a/assets/app/js/controllers/fileDashboard.js b/assets/app/js/controllers/fileDashboard.js new file mode 100644 index 00000000..10e10bed --- /dev/null +++ b/assets/app/js/controllers/fileDashboard.js @@ -0,0 +1,227 @@ +/* + # + # Copyright (c) 2017 - 2019 nexB Inc. and others. All rights reserved. + # https://nexb.com and https://github.com/nexB/scancode-workbench/ + # The ScanCode software is licensed under the Apache License version 2.0. + # ScanCode is a trademark of nexB Inc. + # + # You may not use this software except in compliance with the License. + # You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0 + # Unless required by applicable law or agreed to in writing, software distributed + # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + # CONDITIONS OF ANY KIND, either express or implied. See the License for the + # specific language governing permissions and limitations under the License. + # + */ + +const Sequelize = require('sequelize'); +const Progress = require('../helpers/progress'); +const Controller = require('./controller'); + +const LEGEND_COLORS = [ + '#B3F82F', + '#FFA330', + '#EC2D95', + '#2BE189', + '#9095F0', + '#2AD0D8', + '#AAB2BD', + '#3499DB' +]; + +const LEGEND_LIMIT = 8; + +/** + * The view responsible for displaying the summary information from ScanCode + * Scan data + */ +class FileDashboard extends Controller { + constructor(dashboardId, workbenchDB) { + super(dashboardId, workbenchDB); + + this.totalFilesScanned = $('#total-files').find('.title'); + this.totalDirsScanned = $('#total-dirs').find('.title'); + this.uniqueHolders = $('#unique-holders').find('.title'); + + // First row + this.totalFilesProgressbar = + new Progress('#total-files .title', {size: 25}); + this.totalDirsProgressbar = + new Progress('#total-dirs .title', {size: 25}); + this.uniqueHoldersProgressbar = + new Progress('#unique-holders .title', {size: 25}); + + // Second row + this.sourceLanguageChartProgressbar = + new Progress('#source-chart .content', {size: 50}); + this.fileTypeChartProgressbar = + new Progress('#license-category-chart .content', {size: 50}); + this.holdersChartProgressbar = + new Progress('#holders-chart .content', {size: 50}); + + this.sourceLanguageChart = c3.generate({ + bindto: '#source-chart .chart', + data: { + columns: [], + type: 'pie', + order: 'desc', + }, + color: { + pattern: LEGEND_COLORS + }, + }); + + this.fileTypeChart = c3.generate({ + bindto: '#file-type-chart .chart', + data: { + columns: [], + type: 'pie', + order: 'desc', + }, + color: { + pattern: LEGEND_COLORS + } + }); + + this.holdersChart = c3.generate({ + bindto: '#holders-chart .chart', + data: { + columns: [], + type: 'pie', + order: 'desc', + }, + color: { + pattern: LEGEND_COLORS + } + }); + } + + reload() { + this.needsReload(false); + + // Get # files, # dirs scanned at a certain path + this.totalFilesProgressbar.showIndeterminate(); + this.totalDirsProgressbar.showIndeterminate(); + this.db().sync + .then((db) => db.File.findOne({where: {path: this.selectedPath()}})) + .then((row) => { + const files_count = row.type === 'directory' ? row.files_count : 1; + const dirs_count = row.type === 'directory' ? row.dirs_count : 0; + this.totalFilesScanned.text(files_count); + this.totalDirsScanned.text(dirs_count); + this.totalFilesProgressbar.hide(); + this.totalDirsProgressbar.hide(); + }); + + // Get total unique copyright holders detected at a certain path + this.uniqueHoldersProgressbar.showIndeterminate(); + this.db().sync + .then((db) => db.File.findAll({where: {path: {$like: `${this.selectedPath()}%`}}})) + .then((files) => files.map((val) => val.id)) + .then((fileIds) => this.db().sync + .then((db) => db.Copyright.findAll({where: {fileId: fileIds}})) + .then((copyrights) => copyrights.map((val) => val.holders)) + .then((holders) => holders.map((val) => val.pop())) + .then((holders) => this.uniqueHolders.text(new Set(holders).size))) + .then(() => this.uniqueHoldersProgressbar.hide()); + + // Get unique programming languages detected + this.sourceLanguageChartData = this.db().sync + .then((db) => db.File.findAll({where: {path: {$like: `${this.selectedPath()}%`}}})) + .then((files) => files.map((val) => val.programming_language ? val.programming_language : 'No Value Detected')) + .then((langs) => FileDashboard.formatData(langs)) + .then((langs) => FileDashboard.limitData(langs, LEGEND_LIMIT)); + + // Get file (mime) type data + this.fileTypeChartData = this.db().sync + .then((db) => db.File.findAll({where: {path: {$like: `${this.selectedPath()}%`}}})) + .then((files) => files.map((val) => val.mime_type ? val.mime_type : 'No Value Detected')) + .then((types) => FileDashboard.formatData(types)) + .then((types) => FileDashboard.limitData(types, LEGEND_LIMIT)); + + + this.holdersChartData = this.db().sync + .then((db) => db.File.findAll({where: {path: {$like: `${this.selectedPath()}%`}}})) + .then((files) => files.map((val) => val.id)) + .then((fileIds) => this.db().sync + .then((db) => db.Copyright.findAll({where: {fileId: fileIds}})) + .then((copyrights) => copyrights.map((val) => val.holders ? val.holders : 'No Value Detected')) + .then((holders) => FileDashboard.formatData(holders)) + .then((holders) => FileDashboard.limitData(holders, LEGEND_LIMIT))); + } + + redraw() { + if (this.needsReload()) { + this.reload(); + } + + // Get unique programming languages detected + this.sourceLanguageChartProgressbar.showIndeterminate(); + this.sourceLanguageChartData + .then((data) => this.sourceLanguageChart.load({ + columns: data, + unload: true, + done: () => { + this.sourceLanguageChartProgressbar.hide(); + this.sourceLanguageChart.hide('No Value Detected'); + this.sourceLanguageChart.flush(); + } + })); + + // Get license categories detected + this.fileTypeChartProgressbar.showIndeterminate(); + this.fileTypeChartData + .then((data) => this.fileTypeChart.load({ + columns: data, + unload: true, + done: () => { + this.fileTypeChartProgressbar.hide(); + this.fileTypeChart.hide('No Value Detected'); + this.fileTypeChart.flush(); + } + })); + + // Get license keys detected + this.holdersChartProgressbar.showIndeterminate(); + this.holdersChartData + .then((data) => this.holdersChart.load({ + columns: data, + unload:true, + done: () => { + this.holdersChartProgressbar.hide(); + this.holdersChart.hide('No Value Detected'); + this.holdersChart.flush(); + } + })); + } + + static limitData(data, limit) { + // TODO: Use partitioning (like in quicksort) to find top "limit" + // more efficiently. + // Sort data by count + return data.sort((a,b) => (a[1] > b[1]) ? 1 : -1) + .map((dataPair, i) => { + if (data.length - i >= limit) { + return ['other', dataPair[1]]; + } else { + return dataPair; + } + }); + } + + // Formats data for c3: [[key1, count1], [key2, count2], ...] + static formatData(names) { + // Sum the total number of times the name appears + const count = {}; + $.each(names, (i, name) => { + count[name] = count[name] + 1 || 1; + }); + + // Transform license count into array of objects with license name & count + return $.map(count, (val, key) => { + return [[key, val]]; + }); + } +} + +module.exports = FileDashboard; diff --git a/assets/app/js/models/file.js b/assets/app/js/models/file.js index 8597263f..c5255942 100644 --- a/assets/app/js/models/file.js +++ b/assets/app/js/models/file.js @@ -32,6 +32,7 @@ module.exports = function(sequelize, DataTypes) { sha1: DataTypes.STRING, md5: DataTypes.STRING, files_count: DataTypes.INTEGER, + dirs_count: DataTypes.INTEGER, mime_type: DataTypes.STRING, file_type: DataTypes.STRING, programming_language: DataTypes.STRING, diff --git a/assets/app/js/renderer.js b/assets/app/js/renderer.js index 3281cd89..fb11b2c7 100644 --- a/assets/app/js/renderer.js +++ b/assets/app/js/renderer.js @@ -20,7 +20,7 @@ const Progress = require('./helpers/progress'); const DejaCodeExportDialog = require('./controllers/dejacodeExportDialog'); const ConclusionDialog = require('./controllers/conclusionDialog'); -const Dashboard = require('./controllers/dashboard'); +const FileDashboard = require('./controllers/fileDashboard'); const BarChart = require('./controllers/barChart'); const JsTree = require('./controllers/jsTree'); const ScanDataTable = require('./controllers/scanDataTable'); @@ -45,7 +45,7 @@ $(document).ready(() => { // Create default values for all of the data and ui classes let workbenchDB = new WorkbenchDB(); - const dashboard = new Dashboard('#tab-dashboard', workbenchDB); + const fileDashboard = new FileDashboard('#tab-file-dashboard', workbenchDB); const barChart = new BarChart('#tab-barchart', workbenchDB) .on('bar-clicked', (attribute, value) => { @@ -126,7 +126,7 @@ $(document).ready(() => { const showScanDataButton = $('#show-tab-scandata'); const showConclusionButton = $('#show-tab-conclusion'); const showBarChartButton = $('#show-tab-barchart'); - const showDashboardButton = $('#show-tab-dashboard'); + const showFileDashboardButton = $('#show-tab-file-dashboard'); const showWelcomePageButton = $('#show-tab-welcomepage'); // Import a ScanCode JSON resutls file @@ -155,9 +155,9 @@ $(document).ready(() => { barChart.redraw(); }); - showDashboardButton.click(() => { + showFileDashboardButton.click(() => { splitter.show(); - dashboard.redraw(); + fileDashboard.redraw(); }); showWelcomePageButton.click(() => { @@ -184,12 +184,12 @@ $(document).ready(() => { ipcRenderer.on('zoom-in', zoomIn); ipcRenderer.on('zoom-out', zoomOut); - // Opens the dashboard view when the app is first opened + // Opens the Welcome Page view when the app is first opened showWelcomePageButton.trigger('click'); function updateViewsByPath(path) { // Update all the views with the given path string - $('#dashboard-title-text').text('Dashboard - ' + path); + $('#file-dashboard-title-text').text('File Info Dashboard - ' + path); scanDataTable.columns(0).search(path); conclusionDialog.selectedPath(path); @@ -197,7 +197,7 @@ $(document).ready(() => { jstree.selectedPath(path); scanDataTable.selectedPath(path); conclusionsTable.selectedPath(path); - dashboard.selectedPath(path); + fileDashboard.selectedPath(path); barChart.selectedPath(path); redrawCurrentView(); @@ -285,7 +285,7 @@ $(document).ready(() => { jstree.db(workbenchDB); scanDataTable.db(workbenchDB); conclusionsTable.db(workbenchDB); - dashboard.db(workbenchDB); + fileDashboard.db(workbenchDB); barChart.db(workbenchDB); // Reload the jstree, then trigger the current view to reload. diff --git a/index.html b/index.html index 587801d4..80b7746f 100644 --- a/index.html +++ b/index.html @@ -72,7 +72,7 @@
  • -
  • @@ -152,49 +152,41 @@

    Additional Help and Documentation:

    - -
    -
    -
    -

    Dashboard

    + +
    +
    +
    +

    File Info Dashboard

    -
    +
    -
    +

    0

    -

    Files Scanned

    -
    -
    -
    -
    -
    -
    -

    0

    -

    Unique Licenses Detected

    +

    Total Number of Files

    -
    -
    +
    +

    0

    -

    Unique Copyrights Detected

    +

    Total Number of Directories

    -
    -
    +
    +

    0

    -

    Packages Detected

    +

    Unique Copyright Holders Detected

    -
    +

    Programming Languages

    @@ -204,30 +196,20 @@

    0

    -
    -
    -
    -

    License Categories

    -
    -
    -
    -
    -
    -
    -
    +
    -

    License Keys

    +

    File Types

    -
    +
    -

    Package Types

    +

    Copyright Holders