Skip to content

Commit 9faa2c4

Browse files
authored
Merge pull request #741 from tadast/tt/query-complexity
Console log primitive meta stats for the query
2 parents db803cd + b3023ff commit 9faa2c4

11 files changed

Lines changed: 176 additions & 47 deletions

jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ module.exports = {
4444
'circos$': '<rootDir>/public/js/tests/mocks/circos.js',
4545
'grapher': '<rootDir>/public/js/grapher.js',
4646
'histogram': '<rootDir>/public/js/null_plugins/grapher/histogram.js',
47+
'query_stats': '<rootDir>/public/js/null_plugins/query_stats.js'
4748
},
4849
watchPlugins: [
4950
'jest-watch-typeahead/filename',

public/css/app.min.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/js/databases.js

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,43 @@ import _ from 'underscore';
44
export class Databases extends Component {
55
constructor(props) {
66
super(props);
7-
this.state = { type: '' };
7+
this.state = {
8+
type: '',
9+
currentlySelectedDatabases: [],
10+
};
11+
812
this.preSelectedDbs = this.props.preSelectedDbs;
913
this.databases = this.databases.bind(this);
1014
this.nselected = this.nselected.bind(this);
1115
this.categories = this.categories.bind(this);
12-
this.handleClick = this.handleClick.bind(this);
1316
this.handleToggle = this.handleToggle.bind(this);
1417
this.renderDatabases = this.renderDatabases.bind(this);
1518
this.renderDatabase = this.renderDatabase.bind(this);
1619
}
17-
componentDidUpdate() {
20+
21+
componentDidUpdate(_prevProps, prevState) {
22+
// If there's only one database, select it.
1823
if (this.databases() && this.databases().length === 1) {
19-
$('.databases').find('input').prop('checked', true);
20-
this.handleClick(this.databases()[0]);
24+
this.setState({currentlySelectedDatabases: this.databases()});
2125
}
2226

23-
if (this.preSelectedDbs) {
24-
var selectors = this.preSelectedDbs.map((db) => `input[value=${db.id}]`);
25-
$(selectors.join(',')).prop('checked', true);
26-
this.handleClick(this.preSelectedDbs[0]);
27+
if (this.preSelectedDbs && this.preSelectedDbs.length !== 0) {
28+
this.setState({currentlySelectedDatabases: this.preSelectedDbs});
2729
this.preSelectedDbs = null;
2830
}
29-
this.props.onDatabaseTypeChanged(this.state.type);
31+
const type = this.state.currentlySelectedDatabases[0] ? this.state.currentlySelectedDatabases[0].type : '';
32+
if (type != this.state.type) {
33+
this.setState({ type: type });
34+
this.props.onDatabaseTypeChanged(type);
35+
}
36+
37+
if (prevState.currentlySelectedDatabases !== this.state.currentlySelectedDatabases) {
38+
// Call the prop function with the new state
39+
this.props.onDatabaseSelectionChanged(this.state.currentlySelectedDatabases);
40+
}
41+
3042
}
43+
3144
databases(category) {
3245
var databases = this.props.databases;
3346
if (category) {
@@ -38,29 +51,24 @@ export class Databases extends Component {
3851
}
3952

4053
nselected() {
41-
return $('input[name="databases[]"]:checked').length;
54+
return this.state.currentlySelectedDatabases.length;
4255
}
4356

4457
categories() {
4558
return _.uniq(_.map(this.props.databases, _.iteratee('type'))).sort();
4659
}
4760

48-
handleClick(database) {
49-
var type = this.nselected() ? database.type : '';
50-
if (type != this.state.type) this.setState({ type: type });
51-
}
52-
5361
handleToggle(toggleState, type) {
5462
switch (toggleState) {
5563
case '[Select all]':
56-
$(`.${type} .database input:not(:checked)`).click();
64+
this.setState({ currentlySelectedDatabases: this.databases(type) });
5765
break;
5866
case '[Deselect all]':
59-
$(`.${type} .database input:checked`).click();
67+
this.setState({ currentlySelectedDatabases: [] });
6068
break;
6169
}
62-
this.forceUpdate();
6370
}
71+
6472
renderDatabases(category) {
6573
// Panel name and column width.
6674
var panelTitle = category[0].toUpperCase() + category.substring(1).toLowerCase() + ' databases';
@@ -117,20 +125,37 @@ export class Databases extends Component {
117125
);
118126
}
119127

128+
handleDatabaseSelectionClick(database) {
129+
const isSelected = this.state.currentlySelectedDatabases.some(db => db.id === database.id);
130+
131+
if (isSelected) {
132+
this.setState(prevState => ({
133+
currentlySelectedDatabases: prevState.currentlySelectedDatabases.filter(db => db.id !== database.id)
134+
}));
135+
} else {
136+
this.setState(prevState => ({
137+
currentlySelectedDatabases: [...prevState.currentlySelectedDatabases, database]
138+
}));
139+
}
140+
}
141+
120142
renderDatabase(database) {
121-
var disabled = this.state.type && this.state.type !== database.type;
143+
const isDisabled = this.state.type && this.state.type !== database.type;
144+
const isChecked = this.state.currentlySelectedDatabases.some(db => db.id === database.id);
122145

123146
return (
124-
<label className={(disabled && 'database text-gray-400') || 'database text-seqblue'}>
147+
<label className={(isDisabled && 'database text-gray-400') || 'database text-seqblue'}>
125148
<input
126149
type="checkbox"
127150
name="databases[]"
128151
value={database.id}
129152
data-type={database.type}
130-
disabled={disabled}
153+
disabled={isDisabled}
154+
checked={isChecked}
131155
onChange={_.bind(function () {
132-
this.handleClick(database);
156+
this.handleDatabaseSelectionClick(database);
133157
}, this)}
158+
134159
/>
135160
{' ' + (database.title || database.name)}
136161
</label>
@@ -144,4 +169,4 @@ export class Databases extends Component {
144169
</div>
145170
);
146171
}
147-
}
172+
}

public/js/form.js

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import DatabasesTree from './databases_tree';
55
import { Databases } from './databases';
66
import _ from 'underscore';
77
import { Options } from './options';
8+
import QueryStats from 'query_stats';
89

910
/**
1011
* Search form.
@@ -16,15 +17,24 @@ export class Form extends Component {
1617
constructor(props) {
1718
super(props);
1819
this.state = {
19-
databases: [], preDefinedOpts: {}, tree: {}
20+
databases: [],
21+
preSelectedDbs: [],
22+
currentlySelectedDbs: [],
23+
preDefinedOpts: {},
24+
tree: {},
25+
residuesInQuerySequence: 0,
26+
blastMethod: ''
2027
};
2128
this.useTreeWidget = this.useTreeWidget.bind(this);
22-
this.determineBlastMethod = this.determineBlastMethod.bind(this);
29+
this.determineBlastMethods = this.determineBlastMethods.bind(this);
2330
this.handleSequenceTypeChanged = this.handleSequenceTypeChanged.bind(this);
31+
this.handleSequenceChanged = this.handleSequenceChanged.bind(this);
2432
this.handleDatabaseTypeChanged = this.handleDatabaseTypeChanged.bind(this);
33+
this.handleDatabaseSelectionChanged = this.handleDatabaseSelectionChanged.bind(this);
2534
this.handleAlgoChanged = this.handleAlgoChanged.bind(this);
2635
this.handleFormSubmission = this.handleFormSubmission.bind(this);
2736
this.formRef = createRef();
37+
this.setButtonState = this.setButtonState.bind(this);
2838
}
2939

3040
componentDidMount() {
@@ -103,7 +113,7 @@ export class Form extends Component {
103113
});
104114
}
105115

106-
determineBlastMethod() {
116+
determineBlastMethods() {
107117
var database_type = this.databaseType;
108118
var sequence_type = this.sequenceType;
109119

@@ -138,24 +148,34 @@ export class Form extends Component {
138148
return [];
139149
}
140150

151+
handleSequenceChanged(residuesInQuerySequence) {
152+
if(residuesInQuerySequence !== this.state.residuesInQuerySequence)
153+
this.setState({ residuesInQuerySequence: residuesInQuerySequence});
154+
}
155+
141156
handleSequenceTypeChanged(type) {
142157
this.sequenceType = type;
143-
this.refs.button.setState({
144-
hasQuery: !this.refs.query.isEmpty(),
145-
hasDatabases: !!this.databaseType,
146-
methods: this.determineBlastMethod()
147-
});
158+
this.setButtonState();
148159
}
149160

150161
handleDatabaseTypeChanged(type) {
151162
this.databaseType = type;
163+
this.setButtonState();
164+
}
165+
166+
setButtonState() {
152167
this.refs.button.setState({
153168
hasQuery: !this.refs.query.isEmpty(),
154169
hasDatabases: !!this.databaseType,
155-
methods: this.determineBlastMethod()
170+
methods: this.determineBlastMethods()
156171
});
157172
}
158173

174+
handleDatabaseSelectionChanged(selectedDbs) {
175+
if (!_.isEqual(selectedDbs, this.state.currentlySelectedDbs))
176+
this.setState({ currentlySelectedDbs: selectedDbs });
177+
}
178+
159179
handleAlgoChanged(algo) {
160180
if (algo in this.state.preDefinedOpts) {
161181
var preDefinedOpts = this.state.preDefinedOpts[algo];
@@ -165,12 +185,18 @@ export class Form extends Component {
165185
value: (preDefinedOpts['last search'] ||
166186
preDefinedOpts['default']).join(' ')
167187
});
188+
this.setState({ blastMethod: algo });
168189
}
169190
else {
170191
this.refs.opts.setState({ preOpts: {}, value: '', method: '' });
192+
this.setState({ blastMethod: '' });
171193
}
172194
}
173195

196+
residuesInSelectedDbs() {
197+
return this.state.currentlySelectedDbs.reduce((sum, db) => sum + parseInt(db.ncharacters, 10), 0);
198+
}
199+
174200
render() {
175201
return (
176202
<div>
@@ -184,17 +210,19 @@ export class Form extends Component {
184210
</div>
185211

186212
<form id="blast" ref={this.formRef} onSubmit={this.handleFormSubmission}>
187-
<SearchQueryWidget ref="query" onSequenceTypeChanged={this.handleSequenceTypeChanged} />
213+
<SearchQueryWidget ref="query" onSequenceTypeChanged={this.handleSequenceTypeChanged} onSequenceChanged={this.handleSequenceChanged} />
188214

189215
{this.useTreeWidget() ?
190216
<DatabasesTree ref="databases"
191217
databases={this.state.databases} tree={this.state.tree}
192218
preSelectedDbs={this.state.preSelectedDbs}
193-
onDatabaseTypeChanged={this.handleDatabaseTypeChanged} />
219+
onDatabaseTypeChanged={this.handleDatabaseTypeChanged}
220+
onDatabaseSelectionChanged={this.handleDatabaseSelectionChanged} />
194221
:
195222
<Databases ref="databases" databases={this.state.databases}
196223
preSelectedDbs={this.state.preSelectedDbs}
197-
onDatabaseTypeChanged={this.handleDatabaseTypeChanged} />
224+
onDatabaseTypeChanged={this.handleDatabaseTypeChanged}
225+
onDatabaseSelectionChanged={this.handleDatabaseSelectionChanged} />
198226
}
199227

200228
<div className="md:flex flex-row md:space-x-4 items-center my-6">
@@ -204,6 +232,10 @@ export class Form extends Component {
204232
</label>
205233
<SearchButton ref="button" onAlgoChanged={this.handleAlgoChanged} />
206234
</div>
235+
<QueryStats
236+
residuesInQuerySequence={this.state.residuesInQuerySequence} numberOfDatabasesSelected={this.state.currentlySelectedDbs.length} residuesInSelectedDbs={this.residuesInSelectedDbs()}
237+
currentBlastMethod={this.state.blastMethod}
238+
/>
207239
</form>
208240
</div>
209241
);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from 'react';
2+
3+
class QueryStats extends React.Component{
4+
render () {}
5+
6+
componentDidUpdate() {
7+
console.log("Query stats:", this.props)
8+
}
9+
}
10+
11+
export default QueryStats;

public/js/query.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export class SearchQueryWidget extends Component {
124124
componentDidUpdate() {
125125
this.hideShowButton();
126126
this.preProcessSequence();
127+
this.props.onSequenceChanged(this.residuesCount());
127128

128129
var type = this.type();
129130
if (!type || type !== this._type) {
@@ -156,6 +157,19 @@ export class SearchQueryWidget extends Component {
156157
}
157158
}
158159

160+
residuesCount() {
161+
const sequence = this.value();
162+
const lines = sequence.split('\n');
163+
const residuesCount = lines.reduce((count, line) => {
164+
if (!line.startsWith('>')) {
165+
return count + line.length;
166+
}
167+
return count;
168+
}, 0);
169+
170+
return residuesCount;
171+
}
172+
159173
/**
160174
* Clears textarea. Returns `this`.
161175
*

public/js/tests/database.spec.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable no-unused-vars */
22
/* eslint-disable no-undef */
3-
import { render, screen, fireEvent, waitFor, findByText, getByText } from '@testing-library/react';
3+
import { render, screen, fireEvent } from '@testing-library/react';
44
import { Databases } from '../databases';
55
import data from './mock_data/databases.json';
66

@@ -21,13 +21,13 @@ describe('DATABASES COMPONENT', () => {
2121
});
2222

2323
test('clicking select all on a database should select all its children', () => {
24-
const { container } = render(<Databases databases={data.database} onDatabaseTypeChanged={() => { }} />);
25-
24+
const { container } = render(<Databases databases={data.database} onDatabaseTypeChanged={() => { }} onDatabaseSelectionChanged={() => { }} />);
25+
2626
// select all nucleotide databases
2727
const nucleotideSelectAllBtn = screen.getByRole('heading', { name: /nucleotide databases/i }).parentElement.querySelector('button');
2828
fireEvent.click(nucleotideSelectAllBtn);
2929
const nucleotideCheckboxes = container.querySelector('.databases.nucleotide').querySelectorAll('input[type=checkbox]');
30-
30+
3131
// all nucleotide databases should be checked
3232
nucleotideCheckboxes.forEach((checkbox) => {
3333
expect(checkbox).toBeChecked();
@@ -45,7 +45,7 @@ describe('DATABASES COMPONENT', () => {
4545

4646
test('checking any item of a database type should disable other database type', () => {
4747
const mockFunction = jest.fn(() => { });
48-
const { container } = render(<Databases databases={data.database} onDatabaseTypeChanged={mockFunction} />);
48+
const { container } = render(<Databases databases={data.database} onDatabaseTypeChanged={mockFunction} onDatabaseSelectionChanged={mockFunction}/>);
4949

5050
//select a proteinn database
5151
fireEvent.click(screen.getByRole('checkbox', { name: /2020-11 Swiss-Prot insecta/i }));

0 commit comments

Comments
 (0)