diff --git a/beat/web/reports/static/reports/app/directives/groupAddItemsMenu.js b/beat/web/reports/static/reports/app/directives/groupAddItemsMenu.js new file mode 100644 index 0000000000000000000000000000000000000000..6909978c75a79f0d4b8dbdff29cb5cfee4917363 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupAddItemsMenu.js @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupAddItemsMenu + * Desc: + * represents the menu for adding report items to a group + */ +angular.module('reportApp') +.directive("groupAddItemsMenu", ['ExperimentsService', 'GroupsService', function(ExperimentsService, GroupsService){ + return { + scope: { + group: '=' + }, + link: function(scope){ + const getNextItemId = (type) => { + let count = Object.values(scope.group.reportItems) + .filter(v => v.id.includes(type)) + .length + ; + let nextId = `${type}_${count}`; + return nextId; + }; + + scope.plottables = ExperimentsService.plottables; + + // use the data sub-obj to hold the selected fields + // to keep twoway binding + scope.newTable = { data: [] }; + + scope.addNewTable = function() { + return () => { + let id = getNextItemId('table'); + scope.group.addReportItem(id, Array.from(scope.newTable.data)); + scope.newTable.data.length = 0; + }; + }; + + scope.addNewPlot = (content) => { + let id = getNextItemId('plot'); + scope.group.addReportItem(id, content); + }; + + scope.addNewText = () => { + let id = getNextItemId('text'); + scope.group.addReportItem(id, ''); + }; + + scope.tableFieldSelectorId = `${scope.group.name}_newTableDropdownToggle`; + }, + template: ` +<div class="btn-group" role="group"> + <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + Add Plot + <span class="caret"></span> + </button> + <ul class='dropdown-menu' ng-repeat='(expName, plots) in plottables' ng-if='expName === group.experiments[0]'> + <li ng-repeat='plot in plots'> + <a href='#' ng-click='addNewPlot(plot.type)'>{{ plot.type }}</a> + </li> + </ul> +</div> +<div class="btn-group" role="group" + group-table-field-selector + id='tableFieldSelectorId' + group='group' + cols-selected='newTable.data' + button-action='addNewTable()' + title="Add Table" + button-text="Add Table" + > +</div> +<div class="btn-group" role="group"> + <button + ng-click='addNewText()' + type="button" + class="btn btn-default" + > + Add Text Block + </button> +</div> +` + } +}]); diff --git a/beat/web/reports/static/reports/app/directives/groupItemContainer.js b/beat/web/reports/static/reports/app/directives/groupItemContainer.js new file mode 100644 index 0000000000000000000000000000000000000000..012be32a75a0ee58a3f5bcc6a577246ca2437083 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupItemContainer.js @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupItemContainer + * Desc: + * container for an item in the group + */ +angular.module('reportApp') +.directive("groupItemContainer", [function(){ + return { + scope: { + group: '=', + reportItem: '=' + }, + link: function(scope){ + scope.item = scope.reportItem; + scope.domId = `${scope.group.name}_${scope.id}`; + }, + template: ` +<div + group-table-item + ng-if="item.id.includes('table')" + class='panel panel-default' + group='group' + id='item.id' + fields='item.content' + > +</div> +<div + group-plot-item + ng-if="item.id.includes('plot')" + class='panel panel-default' + group='group' + id='item.id' + content='item.content' + > +</div> +<div + group-text-item + ng-if="item.id.includes('text')" + class='panel panel-default' + group='group' + report-item='item' + > +</div> +` + } +}]); diff --git a/beat/web/reports/static/reports/app/directives/groupPanelAliases.js b/beat/web/reports/static/reports/app/directives/groupPanelAliases.js new file mode 100644 index 0000000000000000000000000000000000000000..9e663b962833ec99088ccbd11aa2a2d35c8b9f69 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupPanelAliases.js @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupPanelAliases + * Desc: + * displays & manages the aliases of the group + */ +angular.module('reportApp').directive("groupPanelAliases", [function(){ + return { + scope: { + group: '=' + }, + link: function(scope){ + }, + template: ` +<div id="{{group.name}}-aliaslist-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{group.name}}-aliaslist-heading" + href="#collapse-{{group.name}}-aliaslist" + aria-expanded="true" + aria-controls="collapse-{{group.name}}-aliaslist"> + Aliases + </a> + </h4> +</div> +<div id="collapse-{{group.name}}-aliaslist" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{group.name}}-aliaslist-heading"> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th>Experiment</th> + <th>Alias</th> + </tr> + </thead> + <tbody> + <tr ng-repeat='(expName, alias) in group.aliases'> + <td>{{ expName }}</td> + <td><input ng-model='group.aliases[expName]'></input></td> + </tr> + </tbody> + </table> +</div> +` + } +}]); diff --git a/beat/web/reports/static/reports/app/directives/groupPanelContent.js b/beat/web/reports/static/reports/app/directives/groupPanelContent.js new file mode 100644 index 0000000000000000000000000000000000000000..f80c5ddd55374227772aabd25b1ec0f3ccc9ca7c --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupPanelContent.js @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupPanelContent + * Desc: + * lays out the content of a group in a panel + */ +angular.module('reportApp').directive("groupPanelContent", ['GroupsService', function(GroupsService){ + return { + scope: { + group: '=' + }, + link: function(scope){ + scope.deleteGroup = GroupsService.deleteGroup; + }, + template: ` +<div id="{{group.name}}-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{group.name}}-heading" + href="#collapse-{{group.name}}" + aria-expanded="true" + aria-controls="collapse-{{group.name}}"> + {{ group.name }} + </a> + <div class='btn-group'> + <button class='btn btn-danger' ng-click='deleteGroup(group.name)'>Delete Group</button> + </div> + </h4> +</div> +<div id="collapse-{{group.name}}" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{group.name}}-heading"> + <div class="panel-body panel-group"> + <i ng-if='group.analyzer.length > 0'>Analyzer: {{ group.analyzer }}</i> + <div group-panel-experiments group='group' class='panel panel-default'></div> + <div ng-if='group.experiments.length > 0' group-panel-aliases group='group' class='panel panel-default'></div> + <div ng-if='group.experiments.length > 0' group-panel-items group='group' class='panel panel-default'></div> + </div> +</div> +` + } +}]); + diff --git a/beat/web/reports/static/reports/app/directives/groupPanelExperiments.js b/beat/web/reports/static/reports/app/directives/groupPanelExperiments.js new file mode 100644 index 0000000000000000000000000000000000000000..83c0b5e4037b8c46a27844a6c000553b3a0e6474 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupPanelExperiments.js @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupPanelExperiments + * Desc: + * displays the experiments panel of the group + */ +angular.module('reportApp').directive("groupPanelExperiments", ['ExperimentsService', function(ExperimentsService){ + return { + scope: { + group: '=' + }, + link: function(scope){ + scope.dropdownId = `${scope.group.name}_exp_add_dropdown`; + scope.expsNotInGroup = () => { + return Object.keys(ExperimentsService.experiments) + // exps not in group + .filter(e => !scope.group.experiments.includes(e)) + // exp has compatible analyzer + .filter(e => scope.group.analyzer === '' || ExperimentsService.getAnalyzerFromExpName(e) === scope.group.analyzer) + ; + }; + scope.getAnalyzerFromExpName = ExperimentsService.getAnalyzerFromExpName; + }, + template: ` +<div id="{{group.name}}-explist-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{group.name}}-explist-heading" + href="#collapse-{{group.name}}-explist" + aria-expanded="true" + aria-controls="collapse-{{group.name}}-explist"> + Experiments + </a> + <div class='btn-group'> + <div class="dropdown"> + <button class="btn btn-default dropdown-toggle" ng-class='{disabled: expsNotInGroup().length === 0}' type="button" id="{{ dropdownId }}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> + Add Experiment + <span class="caret"></span> + </button> + <ul class="dropdown-menu" aria-labelledby="{{ dropdownId }}"> + <li + ng-repeat='exp in expsNotInGroup()' + ng-click='group.addExperiment(exp, getAnalyzerFromExpName(exp))'> + <a href="#">{{ exp }}</a> + </li> + </ul> + </div> + </div> + </h4> +</div> +<div id="collapse-{{group.name}}-explist" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{group.name}}-explist-heading"> + <div class="panel-body"> + <ul class='list-group'> + <li class='list-group-item' ng-repeat='exp in group.experiments'> + <button class="btn btn-danger" aria-hidden="true" ng-click='group.removeExperiment(exp)'>Remove</button> + <span>{{ exp }}</span> + </li> + </ul> + </div> +</div> +` + } +}]); diff --git a/beat/web/reports/static/reports/app/directives/groupPanelItems.js b/beat/web/reports/static/reports/app/directives/groupPanelItems.js new file mode 100644 index 0000000000000000000000000000000000000000..916f24b26e8273acb1a630e2685359d7f064d1eb --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupPanelItems.js @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupPanelItems + * Desc: + * displays the panel of report items of the group + */ +angular.module('reportApp').directive("groupPanelItems", [function(){ + return { + scope: { + group: '=' + }, + link: function(scope){ + }, + template: ` +<div id="{{group.name}}-itemlist-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{group.name}}-itemlist-heading" + href="#collapse-{{group.name}}-itemlist" + aria-expanded="true" + aria-controls="collapse-{{group.name}}-itemlist"> + Items + </a> + <div + group-add-items-menu + class="btn-group" role="group" role='tab' + group='group' + ></div> + </h4> +</div> +<div id="collapse-{{group.name}}-itemlist" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{group.name}}-itemlist-heading"> + <div class="panel-body"> + <div class='panel-group'> + <div + group-item-container + ng-repeat='item in group._reportItems' + group='group' + report-item='item' + style='margin-bottom: 5px;' + > + </div> + </div> + </div> +</div> +` + } +}]); diff --git a/beat/web/reports/static/reports/app/directives/groupPlotItem.js b/beat/web/reports/static/reports/app/directives/groupPlotItem.js new file mode 100644 index 0000000000000000000000000000000000000000..7681ff63ed71db986aeeabd480ffc066a0577bd4 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupPlotItem.js @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupPlotItem + * Desc: + * displays a plot report item + */ +angular.module('reportApp') +.directive("groupPlotItem", ['ExperimentsService', function(ExperimentsService){ + return { + scope: { + group: '=', + id: '=', + content: '=' + }, + link: function(scope){ + scope.domId = `${scope.group.name}_${scope.id}`; + }, + template: ` +<div id="{{domId}}-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{domId}}-heading" + href="#collapse-{{domId}}" + aria-expanded="true" + aria-controls="collapse-{{domId}}"> + {{ domId }} + </a> + + <div class="btn-group" role="group" role='tab'> + <button class='btn btn-default' ng-click='hardRefresh()'> + <span class='glyphicon glyphicon-refresh'></span> + </button> + <button class='btn btn-danger' ng-click='group.removeReportItem(id)'> + Delete Plot + </button + </div> + </h4> +</div> +<div id="collapse-{{domId}}" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{domId}}-heading"> + {{ id }} content +</div> +` + } +}]); diff --git a/beat/web/reports/static/reports/app/directives/groupTableFieldSelector.js b/beat/web/reports/static/reports/app/directives/groupTableFieldSelector.js new file mode 100644 index 0000000000000000000000000000000000000000..d1340426869ca1f2ab070f6587ff41f243487c04 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupTableFieldSelector.js @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * GroupTableFieldSelector + * Desc: + * Handles the choosing of table columns + */ +angular.module('reportApp') +.directive("groupTableFieldSelector", ['ExperimentsService', function(ExperimentsService){ + return { + scope: { + id: '=', + group: '=', + colsSelected: '=', + buttonAction: '&', + title: '@', + buttonText: '@' + }, + link: function(scope){ + // bootstrap's auto dropdown toggling is disabled for the table creation dropdown + // add a click handler for the table creation dropdown submit button to toggle + // manually + scope.clickButton = (e) => { + $(`#${scope.id}`).dropdown('toggle'); + scope.buttonAction()(); + }; + + scope.tableables = () => { + const tableables = ExperimentsService.tableables; + const fieldArr = Object.entries(tableables) + .filter(([e, fields]) => scope.group.experiments.includes(e)) + .map(([e, fields]) => Object.keys(fields)) + .reduce((arr, fArr) => [...arr, ...fArr], []) + ; + + return Array.from(new Set(fieldArr)); + }; + + scope.isUniqueTableable = (expName, fieldName) => { + const tableables = ExperimentsService.tableables; + const concatNames = (eName, fName) => `${eName}.${fName}`; + + const isRepeat = Object.entries(tableables) + .filter(([e, fields]) => { + let isInGroup = scope.group.experiments.includes(e); + let alreadyChecked = Object.keys(tableables).indexOf(e) < Object.keys(tableables).indexOf(expName); + return isInGroup && alreadyChecked; + }) + .map(([e, fields]) => Object.keys(fields)) + .reduce((arr, fArr) => [...arr, ...fArr], []) + .includes(fieldName) + ; + + return !isRepeat; + }; + + console.log(scope.buttonText); + }, + template: ` +<button id='{{ id }}' type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + {{ title }} + <span class="caret"></span> +</button> +<div class='dropdown-menu' ng-click="$event.stopPropagation();"> + <h4>Select columns to show in Table</h4> + <select multiple ng-model='colsSelected'> + <option ng-repeat='fName in tableables()' ng-if='isUniqueTableable(fName)'>{{ fName }}</option> + </select> + <button class='btn btn-default' ng-click='clickButton($event)'>{{ buttonText }}</button> +</div> +` + } +}]); diff --git a/beat/web/reports/static/reports/app/directives/groupTableItem.js b/beat/web/reports/static/reports/app/directives/groupTableItem.js new file mode 100644 index 0000000000000000000000000000000000000000..9d436334975ff2a44e0889c5a60011142643d371 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupTableItem.js @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupTableItem + * Desc: + * displays a table report item + */ +angular.module('reportApp') +.directive("groupTableItem", ['ExperimentsService', function(ExperimentsService){ + return { + scope: { + group: '=', + id: '=', + fields: '=' + }, + link: function(scope){ + // add 'expName' to the beginning of the fields to show in the table + // if it isnt already there + if(scope.fields.length === 0 || scope.fields[0] !== 'Experiment'){ + scope.fields.unshift('Experiment'); + } + + console.log(scope.fields); + scope.tableables = ExperimentsService.tableables || {}; + scope.getFieldType = (field) => { + if(field === scope.fields[0]){ + return 'string'; + } + + let hasFieldObj = Object.values(scope.tableables) + .find(o => o[field]); + let fVal = hasFieldObj ? hasFieldObj[field] : {}; + let type; + if(fVal.type){ + type = fVal.type; + } else if(Number.isSafeInteger(fVal)){ + type = 'integer'; + } else if(Number.isFinite(fVal)){ + type = 'float'; + } else if(typeof fVal === 'string'){ + type = 'string'; + } else { + type = undefined; + } + + return type + }; + scope.getFieldVal = (expName, field) => { + let fVal = scope.tableables[expName] ? + scope.tableables[expName][field] : undefined; + let val; + if(field === scope.fields[0]){ + val = scope.group.aliases[expName].length > 0 ? scope.group.aliases[expName] : expName; + } else if(fVal.value){ + val = fVal.value; + } else if (fVal){ + val = fVal; + } else { + val = '-'; + } + + return val; + }; + + scope.chosenCols = Array.from(scope.fields); + scope.saveChosenCols = () => () => { + scope.fields.length = 1; + scope.chosenCols.forEach(f => scope.fields.push(f)); + }; + + // need to nest actual value in an obj to get angular + // to watch it correctly + scope.sortField = { val: 'Experiment', isReversed: false }; + scope.sortFunc = (expName) => { + return scope.getFieldType(scope.sortField.val) ? + scope.getFieldVal(expName, scope.sortField.val) : expName; + }; + scope.setSortField = (field) => { + if(scope.sortField.val === field){ + scope.sortField.isReversed = !scope.sortField.isReversed; + } else { + scope.sortField.val = field; + scope.sortField.isReversed = false; + } + }; + + scope.domId = `${scope.group.name}_${scope.id}`; + scope.colSelectorId = `${scope.domId}_columnSelector`; + }, + template: ` +<div id="{{domId}}-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{domId}}-heading" + href="#collapse-{{domId}}" + aria-expanded="true" + aria-controls="collapse-{{domId}}"> + {{ domId }} + </a> + + <div class="btn-group" role="group" role='tab'> + <button class='btn btn-default' ng-click='hardRefresh()'> + <span class='glyphicon glyphicon-refresh'></span> + </button> + <div class="btn-group" role="group" + group-table-field-selector + id='colSelectorId' + group='group' + cols-selected='chosenCols' + button-action='saveChosenCols()' + title="Choose Columns" + button-text="Save Column Choices" + > + </div> + </div> + <button class='btn btn-danger' ng-click='group.removeReportItem(id)'> + Delete Table + </button + </h4> +</div> +<div id="collapse-{{domId}}" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{domId}}-heading"> + <div style='height: 100%; overflow-x: auto;'> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th ng-repeat='field in fields'> + <span + ng-if="sortField.val == field" + class='glyphicon' + ng-class="{ + 'glyphicon-chevron-up': sortField.isReversed, + 'glyphicon-chevron-down': !sortField.isReversed + }" + > + </span> + <a role='button' ng-click='setSortField(field)'> + {{ field }} <i>({{ getFieldType(field) }})</i> + </a> + </th> + </tr> + </thead> + <tbody> + <tr ng-repeat="exp in group.experiments | orderBy:sortFunc:sortField.isReversed"> + <td ng-repeat='field in fields'> + {{ tableables[exp] ? getFieldVal(exp, field) : '' }} + </td> + </tr> + </tbody> + </table> + </div> +</div> +` + } +}]); diff --git a/beat/web/reports/static/reports/app/directives/groupTextItem.js b/beat/web/reports/static/reports/app/directives/groupTextItem.js new file mode 100644 index 0000000000000000000000000000000000000000..98364dc4e0ed8b1c9bc810f228c94437a039732b --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupTextItem.js @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupTextItem + * Desc: + * displays a plot report item + */ +angular.module('reportApp') +.directive("groupTextItem", [function(){ + return { + scope: { + group: '=', + reportItem: '=' + }, + link: function(scope){ + scope.item = scope.reportItem; + scope.domId = `${scope.group.name}_${scope.item.id}`; + const editorOptions = { + debug: 'info', + modules: { + toolbar: [ + [{ header: [1, 2, false] }], + ['bold', 'italic', 'underline'], + ['image', 'code-block'] + ] + }, + theme: 'snow' + }; + + // the text editor + scope.editor; + scope.hasUnsavedContent = { val: false, change () { this.val = true; } }; + + let setupEditor = (el) => { + scope.editor = new Quill(el, editorOptions); + if(typeof scope.item.content === 'object'){ + scope.editor.setContents(scope.item.content); + } + scope.editor.on('text-change', (newDelta) => { + scope.hasUnsavedContent.change(); + console.log(scope.hasUnsavedContent); + }); + }; + + let editorId = `${scope.domId}-text-editor`; + scope.$watch(() => document.querySelector(`#${editorId}`), + (oldEl, newEl) => { + if(newEl){ + setupEditor(newEl); + } + } + ); + + scope.saveContent = () => { + let newContent = scope.editor.getContents(); + scope.hasUnsavedContent.val = false; + scope.item.content = newContent; + }; + }, + template: ` +<div id="{{domId}}-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{domId}}-heading" + href="#collapse-{{domId}}" + aria-expanded="true" + aria-controls="collapse-{{domId}}"> + {{ domId }} + </a> + <button class='btn btn-success' ng-click='saveContent()'> + Save Content + </button> + + <div class="btn-group" role="group" role='tab'> + <button class='btn btn-danger' ng-click='group.removeReportItem(item.id)'> + Delete Text Block + </button + </div> + <div ng-if='hasUnsavedContent.val'> + <strong>Warning:</strong> Press the 'Save Content' button to save changes. + </div> + </h4> +</div> +<div id="collapse-{{domId}}" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{domId}}-heading"> + <div id='{{domId}}-text-editor'></div> +</div> +` + }; +}]); diff --git a/beat/web/reports/static/reports/app/directives/groupsLayout.js b/beat/web/reports/static/reports/app/directives/groupsLayout.js new file mode 100644 index 0000000000000000000000000000000000000000..458e17d56e321fb3aa53ab452f34636c8f8db192 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/groupsLayout.js @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupsLayout + * Desc: + * controls the layout of the reports content, generating group panels + * using the GroupsService data. + */ +angular.module('reportApp').directive("groupsLayout", ['GroupsService', function(GroupsService){ + return { + // isolate scope for modularity & less coupling + scope: { + }, + link: function(scope, el, attr){ + scope.groups = GroupsService.groups; + }, + template: ` +<div id='groupsLayout' class='panel-group'> + <div + ng-if='true || groups.length > 1' + group-panel-content + class='panel panel-default' + ng-repeat='group in groups track by $index' + group='group' + > + </div> + <!-- + <div + group-table-item + ng-if='groups.length == 1' + class='panel panel-default' + ng-repeat='table in groups[0]._reportItems' + group='groups[0]' + id='table.id' + fields='table.content' + > + </div> + !--> +</div> +` + } +}]); diff --git a/beat/web/reports/templates/reports/report.html b/beat/web/reports/templates/reports/report.html index 6a38aa008d47f0bfcb50cebf51479f27c0ebdda9..32b21caf380ffd76cf24749b1c8370c23356aee5 100644 --- a/beat/web/reports/templates/reports/report.html +++ b/beat/web/reports/templates/reports/report.html @@ -42,6 +42,8 @@ <link rel="stylesheet" href="{% fingerprint "chosen-bootstrap/chosen.bootstrap.min.css" %}" type="text/css" media="screen" /> <link rel="stylesheet" href="{% fingerprint "datatables/media/css/dataTables.bootstrap.min.css" %}" type="text/css" media="screen" /> <link rel="stylesheet" href="{% fingerprint "jquery-ui/themes/base/minified/jquery-ui.min.css" %}" type="text/css" media="screen" /> + <link href="//cdn.quilljs.com/1.2.2/quill.snow.css" rel="stylesheet"> + <link href="//cdn.quilljs.com/1.2.2/quill.bubble.css" rel="stylesheet"> {% code_editor_css %} {% endblock %} @@ -53,6 +55,7 @@ <script src="{% fingerprint "chosen/chosen.jquery.min.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "datatables/media/js/jquery.dataTables.min.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "datatables/media/js/dataTables.bootstrap.min.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="//cdn.quilljs.com/1.2.2/quill.min.js"></script> <!-- Use Google's CDN for angular-js with a local fallback --> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script> @@ -83,6 +86,7 @@ <!-- services --> <script src="{% fingerprint "reports/app/services/groupsService.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/services/experimentsService.js" %}" type="text/javascript" charset="utf-8"></script> <!-- directives --> @@ -102,6 +106,17 @@ <script src="{% fingerprint "reports/app/directives/tableDynamic.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "reports/app/directives/tablePrecision.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "reports/app/directives/theColumn.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupsLayout.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupPanelContent.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupPanelExperiments.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupPanelItems.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupAddItemsMenu.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupItemContainer.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupTableItem.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupPlotItem.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupTextItem.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupTableFieldSelector.js" %}" type="text/javascript" charset="utf-8"></script> + <script src="{% fingerprint "reports/app/directives/groupPanelAliases.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% fingerprint "ui/js/smartselector.js" %}" type="text/javascript" charset="utf-8"></script>