From 7ebc619f369be2a91aaa8389de08bcaf8c66414f Mon Sep 17 00:00:00 2001 From: Jaden Diefenbaugh <blakcap@users.noreply.github.com> Date: Mon, 3 Apr 2017 12:35:43 +0200 Subject: [PATCH] documentation on all edit directives --- .../app/directives/edit/addGroupMenu.js | 6 ++++- .../app/directives/edit/addItemsMenu.js | 24 +++++++++++++++---- .../app/directives/edit/itemContainer.js | 5 +++- .../reports/app/directives/edit/layout.js | 8 ++++--- .../app/directives/edit/panelContent.js | 12 ++++++---- .../app/directives/edit/panelExperiments.js | 11 ++++++++- .../reports/app/directives/edit/panelItems.js | 6 ++++- .../reports/app/directives/edit/plotItem.js | 4 +++- .../app/directives/edit/tableFieldSelector.js | 24 +++++++++++++++++++ .../reports/app/directives/edit/tableItem.js | 12 +++++++++- .../reports/app/directives/edit/textItem.js | 18 ++++++++------ 11 files changed, 106 insertions(+), 24 deletions(-) diff --git a/beat/web/reports/static/reports/app/directives/edit/addGroupMenu.js b/beat/web/reports/static/reports/app/directives/edit/addGroupMenu.js index 40dcee254..dcefe7f83 100644 --- a/beat/web/reports/static/reports/app/directives/edit/addGroupMenu.js +++ b/beat/web/reports/static/reports/app/directives/edit/addGroupMenu.js @@ -23,7 +23,7 @@ /* * groupAddGroupMenu * Desc: - * represents the menu for creating groups + * the menu & validating for creating a new group */ angular.module('reportApp') .directive("groupAddGroupMenu", ['GroupsService', function(GroupsService){ @@ -32,12 +32,16 @@ angular.module('reportApp') }, link: function(scope){ scope.newGroupName = { val: '' }; + // validates the user input scope.hasError = (val) => { // if val is undefined, empty, or a dup, its an err const isErr = !val || val.length === 0 || GroupsService.groups.find(g => g.name === val); // cast to boolean return !!isErr; }; + + // creates a new group if the new group name is valid + // wipes the input on successful creation scope.createGroup = () => { if(scope.hasError(scope.newGroupName.val)){ return; diff --git a/beat/web/reports/static/reports/app/directives/edit/addItemsMenu.js b/beat/web/reports/static/reports/app/directives/edit/addItemsMenu.js index dde9770fe..d4ee0c8a1 100644 --- a/beat/web/reports/static/reports/app/directives/edit/addItemsMenu.js +++ b/beat/web/reports/static/reports/app/directives/edit/addItemsMenu.js @@ -23,7 +23,7 @@ /* * groupAddItemsMenu * Desc: - * represents the menu for adding report items to a group + * the button group for adding report items (plot, table, text) to a group */ angular.module('reportApp') .directive("groupAddItemsMenu", ['ExperimentsService', 'GroupsService', function(ExperimentsService, GroupsService){ @@ -32,17 +32,22 @@ angular.module('reportApp') group: '=' }, link: function(scope){ + // finds the id for the next report item of + // the given type + // by looking at the existing items 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; + // helper func for adding a table scope.addNewTable = () => { const id = getNextItemId('table'); @@ -62,14 +67,22 @@ angular.module('reportApp') const defaultFields = Array.from(defaultFieldsSet); console.log(defaultFields); - scope.group.addReportItem(id, { + // tables have an arr of selected fields + // and a float precision + let content = { fields: defaultFields, precision: 10 - }); + }; + + scope.group.addReportItem(id, content); }; + // helper func for adding a plot scope.addNewPlot = (plot) => { let id = getNextItemId('plot'); + + // plots have a given name (by analyzer) + // and a plot type let content = { name: plot.label, type: plot.type @@ -78,9 +91,12 @@ angular.module('reportApp') scope.group.addReportItem(id, content); }; + // helper func for adding a text block scope.addNewText = () => { let id = getNextItemId('text'); - scope.group.addReportItem(id, ''); + // text blocks just have raw RST + let content = ''; + scope.group.addReportItem(id, content); }; }, template: ` diff --git a/beat/web/reports/static/reports/app/directives/edit/itemContainer.js b/beat/web/reports/static/reports/app/directives/edit/itemContainer.js index 3aed999c4..1361c99e7 100644 --- a/beat/web/reports/static/reports/app/directives/edit/itemContainer.js +++ b/beat/web/reports/static/reports/app/directives/edit/itemContainer.js @@ -23,7 +23,9 @@ /* * groupItemContainer * Desc: - * container for an item in the group + * container for an item in the group. + * depending of the type of the item (found in the item's id) + * it creates a table, plot, or text item */ angular.module('reportApp') .directive("groupItemContainer", [function(){ @@ -34,6 +36,7 @@ angular.module('reportApp') }, link: function(scope){ scope.item = scope.reportItem; + // report-wide-unique prefix for the item to use scope.domId = `${scope.group.name}_${scope.id}`; }, template: ` diff --git a/beat/web/reports/static/reports/app/directives/edit/layout.js b/beat/web/reports/static/reports/app/directives/edit/layout.js index c7b99a60b..c2b40bfae 100644 --- a/beat/web/reports/static/reports/app/directives/edit/layout.js +++ b/beat/web/reports/static/reports/app/directives/edit/layout.js @@ -23,17 +23,19 @@ /* * groupsLayout * Desc: - * controls the layout of the reports content, generating group panels - * using the GroupsService data. + * controls the layout of the reports content, + * generating group panels using the GroupsService data, + * and holding the menu for adding a group */ 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; scope.GroupsService = GroupsService; + + // drag handle CSS selector scope.sortableOptions = { handle: '.panel-heading > .panel-title > .action-buttons > .drag-handle' }; diff --git a/beat/web/reports/static/reports/app/directives/edit/panelContent.js b/beat/web/reports/static/reports/app/directives/edit/panelContent.js index 9bced2aaf..bfb1fecc1 100644 --- a/beat/web/reports/static/reports/app/directives/edit/panelContent.js +++ b/beat/web/reports/static/reports/app/directives/edit/panelContent.js @@ -23,7 +23,11 @@ /* * groupPanelContent * Desc: - * lays out the content of a group in a panel + * presents the group to the user in logical blocks: + * - a panel holds content + * - panel header contains an (editable) group name label + * - panel header also contains the buttons to add report items + * - two sub-panels are added: the experiments list, and the items list */ angular.module('reportApp').directive("groupPanelContent", ['GroupsService', function(GroupsService){ return { @@ -33,7 +37,7 @@ angular.module('reportApp').directive("groupPanelContent", ['GroupsService', fun link: function(scope){ scope.deleteGroup = GroupsService.deleteGroup; scope.editingGroupName = false; - scope.change = () => { + scope.toggleEditingGroupName = () => { scope.editingGroupName = !scope.editingGroupName; }; }, @@ -49,7 +53,7 @@ angular.module('reportApp').directive("groupPanelContent", ['GroupsService', fun aria-expanded="true" aria-controls="collapse-{{group.name}}"> </a> - <span ng-if='!editingGroupName' ng-click='change()'> + <span ng-if='!editingGroupName' ng-click='toggleEditingGroupName()'> <span>{{ group.name }}</span> <span style='cursor: pointer;' class='glyphicon glyphicon-pencil'></span> </span> @@ -61,7 +65,7 @@ angular.module('reportApp').directive("groupPanelContent", ['GroupsService', fun class='form-control' placeholder='Group name...' ng-model='group._name'/> - <span class='input-group-addon' ng-click='change()'> + <span class='input-group-addon' ng-click='toggleEditingGroupName()'> Save </span> </span> diff --git a/beat/web/reports/static/reports/app/directives/edit/panelExperiments.js b/beat/web/reports/static/reports/app/directives/edit/panelExperiments.js index 6435a1bc9..8913dcbd2 100644 --- a/beat/web/reports/static/reports/app/directives/edit/panelExperiments.js +++ b/beat/web/reports/static/reports/app/directives/edit/panelExperiments.js @@ -23,7 +23,9 @@ /* * groupPanelExperiments * Desc: - * displays the experiments panel of the group + * displays the experiments panel of the group - + * a table of experiments in the group, their databases/protocols, and aliases. + * Also has a menu for adding (compatible) experiments to the group. */ angular.module('reportApp').directive("groupPanelExperiments", ['GroupsService', 'ExperimentsService', 'UrlService', function(GroupsService, ExperimentsService, UrlService){ return { @@ -33,6 +35,9 @@ angular.module('reportApp').directive("groupPanelExperiments", ['GroupsService', link: function(scope){ scope.experiments = ExperimentsService.experiments; scope.dropdownId = `${scope.group.name}_exp_add_dropdown`; + + // find experiments that are not in the group but are + // compatible with the existing experiments (if any) scope.expsNotInGroup = () => { return ExperimentsService.experimentNames // exps not in group @@ -41,10 +46,14 @@ angular.module('reportApp').directive("groupPanelExperiments", ['GroupsService', .filter(e => scope.group.analyzer === '' || ExperimentsService.getAnalyzerFromExpName(e) === scope.group.analyzer) ; }; + + // collects an array of formatted databases & protocols of an experiment + // format is "<database name>@<protocol name>" scope.getExpDatabases = (expName) => { let dbs = scope.experiments[expName].declaration.datasets; return Array.from(new Set(Object.values(dbs).map(db => `${db.database}@${db.protocol}`))); }; + scope.getAnalyzerFromExpName = ExperimentsService.getAnalyzerFromExpName; scope.getExpUrl = UrlService.getExperimentUrl; scope.getBlockUrl = UrlService.getBlockUrl; diff --git a/beat/web/reports/static/reports/app/directives/edit/panelItems.js b/beat/web/reports/static/reports/app/directives/edit/panelItems.js index 0f446ed82..0c73d4677 100644 --- a/beat/web/reports/static/reports/app/directives/edit/panelItems.js +++ b/beat/web/reports/static/reports/app/directives/edit/panelItems.js @@ -23,7 +23,8 @@ /* * groupPanelItems * Desc: - * displays the panel of report items of the group + * displays the panel of report items of the group, + * using the item container adaptor */ angular.module('reportApp').directive("groupPanelItems", [function(){ return { @@ -31,6 +32,9 @@ angular.module('reportApp').directive("groupPanelItems", [function(){ group: '=' }, link: function(scope){ + // CSS selector for drag handles for the ui-sortable functionality + // TODO: this needs to be changed each time the HTML hierarchy changes. + // Make it hierarchy-independent scope.sortableOptions = { handle: '.panel-heading > .panel-title > .action-buttons > .drag-handle' }; diff --git a/beat/web/reports/static/reports/app/directives/edit/plotItem.js b/beat/web/reports/static/reports/app/directives/edit/plotItem.js index c6dd7818c..0c399886c 100644 --- a/beat/web/reports/static/reports/app/directives/edit/plotItem.js +++ b/beat/web/reports/static/reports/app/directives/edit/plotItem.js @@ -23,7 +23,7 @@ /* * groupPlotItem * Desc: - * displays a plot report item + * displays a plot report item (basically a container for the plots code to insert into) */ angular.module('reportApp') .directive("groupPlotItem", ['ExperimentsService', 'PlotService', function(ExperimentsService, PlotService){ @@ -36,6 +36,8 @@ angular.module('reportApp') link: function(scope){ const group = scope.group; scope.domId = `${scope.group.name}_${scope.id}`; + + // container for the plots applet to insert into scope.renderDivId = `${scope.domId}-render`; PlotService.addPlot(scope.group, scope.id, scope.renderDivId); diff --git a/beat/web/reports/static/reports/app/directives/edit/tableFieldSelector.js b/beat/web/reports/static/reports/app/directives/edit/tableFieldSelector.js index 74baebb82..f3ac84adc 100644 --- a/beat/web/reports/static/reports/app/directives/edit/tableFieldSelector.js +++ b/beat/web/reports/static/reports/app/directives/edit/tableFieldSelector.js @@ -31,9 +31,13 @@ angular.module('reportApp') scope: { id: '=', group: '=', + // currently selected columns for the table colsSelected: '=', + // function to execute when the user clicks the submit button buttonAction: '&', + // title for the menu title: '@', + // text for the submit button buttonText: '@' }, link: function(scope){ @@ -46,34 +50,52 @@ angular.module('reportApp') }; scope.tableables = () => { + // start with the tableables generated in experimentsservice const tableables = ExperimentsService.tableables; const fieldArr = Object.entries(tableables) + // only look at fields that are from an experiment in the group .filter(([e, fields]) => scope.group.experiments.includes(e)) + // get the names of the fields .map(([e, fields]) => Object.keys(fields)) + // make one big array of all field names .reduce((arr, fArr) => [...arr, ...fArr], []) ; + // converting to and from a Set is a simple way of + // removing dups return Array.from(new Set(fieldArr)); }; + // has this fieldName already been processed? + // need to look at the already-processed field names scope.isUniqueTableable = (expName, fieldName) => { const tableables = ExperimentsService.tableables; const concatNames = (eName, fName) => `${eName}.${fName}`; + // see if this field is a repeat const isRepeat = Object.entries(tableables) .filter(([e, fields]) => { + // only look at tableables of exps that are in group let isInGroup = scope.group.experiments.includes(e); + // and have already been looked at let alreadyChecked = Object.keys(tableables).indexOf(e) < Object.keys(tableables).indexOf(expName); return isInGroup && alreadyChecked; }) + // get field names .map(([e, fields]) => Object.keys(fields)) + // flatten .reduce((arr, fArr) => [...arr, ...fArr], []) + // does this flattened array have this field name in it? .includes(fieldName) ; + // if it isnt a repeat, its unique! return !isRepeat; }; + // many tableable fields are fields from an analyzer, a block, or something else + // these fields have a field group name, a '.', and an actual field name + // find these group names and use them to subdivide the list of fields in the menu scope.tableablesGroups = () => { let groupNames = scope.tableables() .filter(f => f.includes('.')) @@ -82,8 +104,10 @@ angular.module('reportApp') return Array.from(new Set(groupNames)).sort((a, b) => groupNames.indexOf(a) - groupNames.indexOf(b)); }; + // finds the actual field name whether its in a field group or not scope.subfieldName = (field) => field.includes('.') ? field.split('.').slice(1).join('.') : field; + // toggle the selection of a field scope.toggleField = (fName) => { let idx = scope.colsSelected.indexOf(fName); if(idx > -1){ diff --git a/beat/web/reports/static/reports/app/directives/edit/tableItem.js b/beat/web/reports/static/reports/app/directives/edit/tableItem.js index 20b174006..9fba43f28 100644 --- a/beat/web/reports/static/reports/app/directives/edit/tableItem.js +++ b/beat/web/reports/static/reports/app/directives/edit/tableItem.js @@ -23,7 +23,8 @@ /* * groupTableItem * Desc: - * displays a table report item + * displays a table report item and lets the user + * manage this table's selected cols and float precision */ angular.module('reportApp') .directive("groupTableItem", ['GroupsService', 'ExperimentsService', function(GroupsService, ExperimentsService){ @@ -41,12 +42,21 @@ angular.module('reportApp') scope.colSelectorId = `${scope.domId}_columnSelector`; // 1 - 10 + // probably the most concise way of generating 1-10 computationally + // also vastly more complex than writing [1,2,3,4,5,6,7,9,10] scope.floatingPointRange = [...(new Array(10)).keys()].map(i => i + 1); // init the chosen cols for the table with the saved cols scope.chosenCols = Array.from(scope.fields); + // save new cols choice + // due to how angular handles functions passed as props to an element, + // it must return a function that does the actual work. scope.saveChosenCols = () => () => { + // we want to keep the selected columns in order, + // so try to add new columns in their correct indices, + // and rm cols by mutating the array + const newCols = scope.chosenCols .filter(c => !scope.fields.includes(c)); diff --git a/beat/web/reports/static/reports/app/directives/edit/textItem.js b/beat/web/reports/static/reports/app/directives/edit/textItem.js index 020fd50f2..c38e9aafd 100644 --- a/beat/web/reports/static/reports/app/directives/edit/textItem.js +++ b/beat/web/reports/static/reports/app/directives/edit/textItem.js @@ -23,7 +23,11 @@ /* * groupTextItem * Desc: - * displays a text report item + * displays a text report item: + * - sends the raw RST to the server to be compiled, + * and displays the returned HTML + * - the user can edit the RST using codemirror + * - the compile is async and doesnt require refreshing the page */ angular.module('reportApp') .directive("groupTextItem", ['GroupsService', '$sce', 'UrlService', 'reportFactory', function(GroupsService, $sce, UrlService, reportFactory){ @@ -34,6 +38,7 @@ angular.module('reportApp') }, link: function(scope){ // aliases + // angular requires that compiling raw html be sanitized scope.trustAsHtml = $sce.trustAsHtml; scope.item = scope.reportItem; scope.domId = `${scope.group.name}_${scope.item.id}`; @@ -43,13 +48,8 @@ angular.module('reportApp') mode: 'rst', }; - // readonly codemirror options - scope.srccmOptions = { - mode: 'rst', - readOnly: true - }; - // handle compiling content + // holds the last response from the server scope.compiledContent = { val: '' }; scope.compileContent = () => { let url = UrlService.getCompileRstUrl(); @@ -57,18 +57,22 @@ angular.module('reportApp') return reportFactory.compileRST(url, content) .then(data => { + // when compiled, save the raw html scope.compiledContent.val = data.data.html_str; }); }; // handle edit/save/cancel buttons scope.isEditing = { val: false }; + // when editing, use a buffer to hold the raw text scope.unsavedContent = { val: `${scope.item.content}` }; + // save the buffer to the actual report item content scope.saveAction = () => { scope.item.content = scope.unsavedContent.val; scope.compileContent(); scope.isEditing.val = false; }; + // discard buffer and use report item content scope.cancelAction = () => { scope.unsavedContent.val = `${scope.item.content}`; scope.isEditing.val = false; -- GitLab