Skip to content

Commit b3023ff

Browse files
committed
Console log primitive meta stats for the query
Useful for basic debugging. Also lays groundwork for some more exciting features on SequenceServer Cloud version
1 parent db803cd commit b3023ff

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)