Skip to content

Commit

Permalink
sequence search : button in output tab to add dataset
Browse files Browse the repository at this point in the history
sequence-search.js : declare and comment viewDatasetFlag.

blast-results.js : use upload-base and upload-table.
add columnsKeyString, c_name, c_chr, c_pos.
add services store, auth, classNames:blast-results.
add viewDatasetFlag.
add didReceiveAttrs() : initialise selectedParent, namespace.
add validateData()

app.scss : .blast-results : margin.
sequence-search.hbs : pass to blast-results : datasets, refreshDatasets, viewDataset.

blast-results.hbs : from data-csv : add message fields, and inputs : selectedDataset, selectedParent (currently read-only), namespace.
add checkbox viewDatasetFlag, submit button.

add upload-base.js, factored from data-base.js
add upload-table.js, factored from data-csv.js
  • Loading branch information
Don-Isdale committed May 31, 2021
1 parent 66d59ba commit 9640e0f
Show file tree
Hide file tree
Showing 7 changed files with 449 additions and 3 deletions.
2 changes: 2 additions & 0 deletions frontend/app/components/panel/sequence-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export default Component.extend({
resultRows : 50,
/** true means add / upload result to db as a Dataset */
addDataset : false,
/** true means view the blocks of the dataset after it is added. */
viewDatasetFlag : false,

classNames: ['col-xs-12'],

Expand Down
116 changes: 114 additions & 2 deletions frontend/app/components/panel/upload/blast-results.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,102 @@
import Component from '@ember/component';
import { observer, computed } from '@ember/object';
import { alias } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import { later as run_later } from '@ember/runloop';


import config from '../../../config/environment';

import uploadBase from '../../../utils/panel/upload-base';
import uploadTable from '../../../utils/panel/upload-table';


const dLog = console.debug;

/* global Handsontable */
/* global $ */

/*----------------------------------------------------------------------------*/

/**
* based on backend/scripts/dnaSequenceSearch.bash : columnsKeyString
* This also aligns with createTable() : colHeaders below.
*/
const columnsKeyString = [
'name', 'chr', 'pcIdentity', 'lengthOfHspHit', 'numMismatches', 'numGaps', 'queryStart', 'queryEnd', 'pos', 'end'
];
const c_name = 0, c_chr = 1, c_pos = 8;

/*----------------------------------------------------------------------------*/


/** Display a table of results from sequence-search API request
* /Feature/dnaSequenceSearch
*/
export default Component.extend({
apiServers: service(),
blockService : service('data/block'),
/** Similar comment to data-csv.js applies re. store (user could select server via GUI).
* store is used by upload-table.js : getDatasetId() and submitFile()
*/
store : alias('apiServers.primaryServer.store'),
auth: service('auth'),


classNames: ['blast-results'],

/** true enables display of the search inputs. */
showSearch : false,

/** true means view the blocks of the dataset after it is added. */
viewDatasetFlag : false,

/** copied from data-base.js, not used yet */
/*--------------------------------------------------------------------------*/

/** copied from data-base.js. for uploadBase*/
isProcessing: false,
successMessage: null,
errorMessage: null,
warningMessage: null,
progressMsg: '',


setProcessing : uploadBase.setProcessing,
setSuccess : uploadBase.setSuccess,
setError : uploadBase.setError,
setWarning : uploadBase.setWarning,
clearMsgs : uploadBase.clearMsgs,

/** data-csv.js : scrollToTop() scrolls up #left-panel-upload, but that is not required here. */
scrollToTop() {
},

updateProgress : uploadBase.updateProgress,


/*--------------------------------------------------------------------------*/

/** copied from data-base.js, for uploadTable */

selectedDataset: 'new',
newDatasetName: '',
nameWarning: null,
selectedParent: '',
dataType: 'linear',
namespace: '',

getDatasetId : uploadTable.getDatasetId,
isDupName : uploadTable.isDupName,
onNameChange : observer('newDatasetName', uploadTable.onNameChange),
onSelectChange : observer('selectedDataset', 'selectedParent', uploadTable.onSelectChange),

/*--------------------------------------------------------------------------*/

actions : {
submitFile : uploadTable.submitFile
},

/*--------------------------------------------------------------------------*/

dataMatrix : computed('data.[]', function () {
let cells = this.get('data').map((r) => r.split('\t'));
return cells;
Expand All @@ -45,6 +112,12 @@ export default Component.extend({
// this.showTable();
},

didReceiveAttrs() {
this._super(...arguments);
this.set('selectedParent', this.get('search.parent'));
this.set('namespace', this.get('search.parent') + ':blast');
},

/*--------------------------------------------------------------------------*/

/** Outcomes of the API search request.
Expand Down Expand Up @@ -205,5 +278,44 @@ query ID, subject ID, % identity, length of HSP (hit), # mismatches, # gaps, que
table.updateSettings({data:[]});
},

/*--------------------------------------------------------------------------*/

/** called by upload-table.js : onSelectChange()
* No validation of user input is required because table content is output from blast process.
*/
checkBlocks() {
},


/** upload-table.js : submitFile() expects this function.
* In blast-results, the data is not user input so validation is not required.
*/
validateData() {
/** based on data-csv.js : validateData(), which uses table.getSourceData();
* in this case sourceData is based on .dataMatrix instead
* of going via the table.
*/
return new Promise((resolve, reject) => {
let table = this.get('table');
if (table === null) {
resolve([]);
}
let sourceData = this.get('dataMatrix');
/** the last row is empty. */
let validatedData = sourceData
.filter((row) => (row[c_name] !== '') && (row[c_chr]))
.map((row) =>
({
name: row[c_name],
// blast output chromosome is e.g. 'chr2A'; Pretzel uses simply '2A'.
block: row[c_chr].replace(/^chr/,''),
// Make sure val is a number, not a string.
val: Number(row[c_pos])
}) );
resolve(validatedData);
});
}

/*--------------------------------------------------------------------------*/

});
4 changes: 4 additions & 0 deletions frontend/app/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,10 @@ div#left-panel-upload select
margin-bottom: 5px;
}

.blast-results {
margin: 1em;
}

.feature-list.active-input > div > div > div.from-input > a {
background-color : #e8e8f7;
}
Expand Down
5 changes: 4 additions & 1 deletion frontend/app/templates/components/panel/sequence-search.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,12 @@ AGCTGGGTGTCGTTGATCTTCAGGTCCTTCTGGATGTACAGCGACGCTCC" }}
</tab.pane>

{{#each this.searches as |search|}}
<tab.pane @id={{search.tabId}} @title="Output : Blast Matches">
<tab.pane @id={{search.tabId}} @title="Output : Blast Matches" style="overflow: auto;">

{{panel/upload/blast-results search=search
datasets=datasets
refreshDatasets=refreshDatasets
viewDataset=viewDataset
active=(bs-eq tab.activeId search.tabId ) }}

</tab.pane>
Expand Down
85 changes: 85 additions & 0 deletions frontend/app/templates/components/panel/upload/blast-results.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@
</div>

{{!-- --------------------------------------------------------------------- --}}
{{!-- from data-csv --}}

{{elem/panel-message
successMessage=successMessage
warningMessage=warningMessage
errorMessage=errorMessage}}
{{#if nameWarning}}
{{elem/panel-message
warningMessage=nameWarning}}
{{/if}}

{{#if isProcessing}}
{{#elem/panel-form
Expand All @@ -38,5 +48,80 @@
{{/elem/panel-form}}
{{/if}}

<label>Dataset: </label>
<select
id="dataset"
onchange={{action (mut selectedDataset) value="target.value"}}
disabled={{isProcessing}}
>
<option selected>new</option>
{{#each datasets key="name" as |ds|}}
<option value={{ds.name}}>{{ds.name}}</option>
{{/each}}
</select>
<br>
{{#if (eq selectedDataset 'new')}}
<div id="new_dataset_options">
{{input
id="dataset_new"
type="text"
value=newDatasetName
class="form-control"
placeholder="New dataset name..."
disabled=isProcessing
}}

<br>
<label>Parent: </label>
{{selectedParent}}
{{!--
<select
id="parent"
onchange={{action (mut selectedParent) value="target.value"}}
disabled={{isProcessing}}
>
<option value='' selected>None</option>
{{#each datasets key="name" as |ds|}}
<option value={{ds.name}}>{{ds.name}}</option>
{{/each}}
</select>
--}}

<br>
<label>Type: </label>
<select
id="type"
onchange={{action (mut dataType) value="target.value"}}
disabled={{isProcessing}}
>
<option>linear</option>
<option>observational</option>
</select>
<br>
<label>Namespace: </label>
{{input id="namespace" type="text" value=namespace disabled=isProcessing }}
</div>
{{/if}}

{{!-- --------------------------------------------------------------------- --}}

<span class="filter-group-col">
{{input type="checkbox" name="viewDatasetFlag" checked=viewDatasetFlag }}
<label>View</label>
</span>


<hr>

<input
type="submit"
value="Add Dataset"
class="btn btn-primary {{if isProcessing 'disabled'}}"
onclick={{action 'submitFile'}}>

<hr>

{{!-- --------------------------------------------------------------------- --}}

<div id={{search.tableId}} class="actual-row">
</div>
79 changes: 79 additions & 0 deletions frontend/app/utils/panel/upload-base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@

/**
* factored from components/panel/upload/data-base.js
* May evolve this to a decorator or a sub-component.
*
* usage :
*
// file: null, // not required
isProcessing: false,
successMessage: null,
errorMessage: null,
warningMessage: null,
progressMsg: '',
*/

export default {


setProcessing() {
this.updateProgress(0, 'up');
this.setProperties({
isProcessing: true,
successMessage: null,
errorMessage: null,
warningMessage: null
});
},
setSuccess(msg) {
let response = msg ? msg : 'Uploaded successfully';
/** .file is undefined (null) when data is read from table instead of from file */
let file = this.get('file');
if (file)
response += ` from file "${file.name}"`;
this.setProperties({
isProcessing: false,
successMessage: response,
});
},
setError(msg) {
this.setProperties({
isProcessing: false,
errorMessage: msg,
});
},
setWarning(msg) {
this.setProperties({
isProcessing: false,
successMessage: null,
errorMessage: null,
warningMessage: msg,
});
},
clearMsgs() {
this.setProperties({
successMessage: null,
errorMessage: null,
warningMessage: null,
});
},

/** Callback used by data upload, to report progress percent updates */
updateProgress(percentComplete, direction) {
if (direction === 'up') {
if (percentComplete === 100) {
this.set('progressMsg', 'Please wait. Updating database.');
} else {
this.set('progressMsg',
'Please wait. File upload in progress (' +
percentComplete.toFixed(0) + '%)' );
}
} else {
this.set('progressMsg',
'Please wait. Receiving result (' + percentComplete.toFixed(0) + '%)' );
}
},


};
Loading

0 comments on commit 9640e0f

Please sign in to comment.