Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test images from NeuroVault #26

Merged
merged 11 commits into from
Apr 16, 2017
30 changes: 25 additions & 5 deletions client/src/routes/Dashboard/TestList.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { isEmpty, zipObject, identity, keys, pick, some, values } from 'lodash';
import moment from 'moment';

import React, { PropTypes } from 'react';
import { Button } from 'react-bootstrap';
import { Button, ButtonToolbar } from 'react-bootstrap';

import { Link } from 'react-router';
import { connect } from 'react-redux';
import { fetchJSON, deleteItemList } from '../../state/fetched';

import { resetModelTestData } from '../../state/testModel';

import TaskStateLabel from '../../components/TaskStateLabel';
import DashboardNav from '../../components/DashboardNav';
import Table from '../../components/Table';
Expand Down Expand Up @@ -40,6 +42,8 @@ class TestList extends React.Component {
this.state = {
selectedRows: {}
};

(this:any).handleNewTest = this.handleNewTest.bind(this);
(this:any).handleToggleRow = this.handleToggleRow.bind(this);
(this:any).handleToggleAll = this.handleToggleAll.bind(this);
(this:any).handleDeleteSelected = this.handleDeleteSelected.bind(this);
Expand All @@ -58,6 +62,12 @@ class TestList extends React.Component {
clearInterval(this.interval);
}

handleNewTest() {
const { router } = this.context;
resetModelTestData(this.props.dispatch);
router.push('/tests/new');
}

handleToggleRow(key, value) {
const { selectedRows } = this.state;
this.setState({
Expand Down Expand Up @@ -125,10 +135,20 @@ class TestList extends React.Component {
return (
<div className="container">
<DashboardNav>
<Button
disabled={someSelected}
onClick={this.handleDeleteSelected}
><i className="fa fa-trash"></i> Delete</Button>
<ButtonToolbar className="pull-right">
<Button
bsStyle="primary"
onClick={this.handleNewTest}
>
<i className="fa fa-plus"></i> New Test
</Button>
<Button
disabled={someSelected}
onClick={this.handleDeleteSelected}
>
<i className="fa fa-trash"></i> Delete
</Button>
</ButtonToolbar>
</DashboardNav>

<div className="row">
Expand Down
52 changes: 40 additions & 12 deletions client/src/routes/TestModel.js → client/src/routes/NewTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import values from 'lodash/object/values';
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import classNames from 'classnames';
import { Button } from 'react-bootstrap';
import { Button, FormControl } from 'react-bootstrap';
import { Link } from 'react-router';

import SearchContainer from '../components/search/SearchContainer';
import SelectImagesModal from '../components/SelectImagesModal';
import SelectedCollectionList from '../components/SelectedCollectionList';

import { testModel } from '../state/testModel';
import { testModel, inputNVImageId } from '../state/testModel';

import {
showSelectImagesModal,
Expand All @@ -28,7 +28,7 @@ import {
inputSearchQuery
} from '../state/search';

class TestModel extends React.Component {
class NewTest extends React.Component {
static propTypes = {
search: PropTypes.object,
testModel: PropTypes.object,
Expand All @@ -45,11 +45,18 @@ class TestModel extends React.Component {
super(props);
(this:any).handleImageToggle = this.handleImageToggle.bind(this);
(this:any).handleImageListToggle = this.handleImageListToggle.bind(this);
(this:any).handleNeuroVaultImageIdChange = this.handleNeuroVaultImageIdChange.bind(this);
(this:any).handleTestModelClick = this.handleTestModelClick.bind(this);
(this:any).handleCollectionClick = this.handleCollectionClick.bind(this);
}

componentDidMount() {
const neurovaultImageId = this.props.location.query['neurovault-image-id'];

if (neurovaultImageId) {
this.props.dispatch(inputNVImageId(neurovaultImageId));
}

if (!this.props.search.results) {
this.props.dispatch(loadSearchResults(inputSearchQuery('')));
}
Expand Down Expand Up @@ -105,15 +112,39 @@ class TestModel extends React.Component {
this.props.dispatch(toggleImageList({collection, images, checked}));
}

handleNeuroVaultImageIdChange(e) {
this.props.dispatch(inputNVImageId(e.target.value));
}

handleTestModelClick(e) {
e.preventDefault();
const { router } = this.context;
const { selectedImages } = this.props;
const { model, neurovaultImageId } = this.props.testModel;

const name = values(selectedImages.collectionsById).map(c => c.name).join(', ');
this.props.dispatch(testModel(name,
this.props.testModel.model.id,
selectedImages.images,
router));

const params = {
name,
modelId: model && model.id,
neurovaultImageId: neurovaultImageId,
selectedImages: selectedImages.images
};

this.props.dispatch(testModel(params, router));
}

renderInputModel(testModel) {
return testModel.model ? (
<p><Link to={`/models/${testModel.model.id}`}>{testModel.model.name}</Link></p>
) : (
<FormControl
type="text"
placeholder="NeuroVault Image Id"
value={testModel.neurovaultImageId}
onChange={this.handleNeuroVaultImageIdChange}
/>
);
}

render() {
Expand Down Expand Up @@ -143,10 +174,7 @@ class TestModel extends React.Component {
<h3 className="panel-title">Model</h3>
</div>
<div className="panel-body">
{testModel.model
? <p><Link to={`/models/${testModel.model.id}`}>{testModel.model.name}</Link></p>
: <p>No test model has been selected.</p>
}
{this.renderInputModel(testModel)}
</div>
</div>

Expand Down Expand Up @@ -188,4 +216,4 @@ class TestModel extends React.Component {
}
}

export default connect(state => state)(TestModel);
export default connect(state => state)(NewTest);
9 changes: 7 additions & 2 deletions client/src/routes/Test.js → client/src/routes/TestDetail.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,16 @@ class Test extends React.Component {

renderTest(test) {
const {correlation, groups, collections} = test.output_data;
const { modelId } = test.input_data;
const { modelId, neurovaultImageId, neurovaultImageName } = test.input_data;

const imageDisplayName = neurovaultImageName || neurovaultImageId;

return (
<div className="col-md-12">
<p>Model: <Link to={`/models/${modelId}`}>{test.model.name}</Link></p>
{test.model
? <p>Model: <Link to={`/models/${modelId}`}>{test.model.name}</Link></p>
: <p>NeuroVault Image: <a target="_blank" href={`http://neurovault.org/images/${neurovaultImageId}`}>{imageDisplayName}</a></p>
}
<ImageBarChart images={correlation}
groups={groups}
collections={collections}
Expand Down
8 changes: 4 additions & 4 deletions client/src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import Explore from './Explore';
import ModelList from './Dashboard/ModelList';
import TestList from './Dashboard/TestList';
import ModelDetail from './ModelDetail';
import Test from './Test';
import TestDetail from './TestDetail';
import NewModel from './NewModel';
import InputData from './NewModel/InputData';
import TrainingLabel from './NewModel/TrainingLabel';
import ModelPreferences from './NewModel/ModelPreferences';
import TestModel from './TestModel';
import NewTest from './NewTest';
import FAQ from './FAQ';
import NotFound from '../components/NotFound';

Expand Down Expand Up @@ -64,8 +64,8 @@ export default function renderRoutes(store: Object) {
<Route path="model-preferences" component={ModelPreferences} />
</Route>
<Route path="/models/:id" component={ModelDetail} />
<Route path="/tests/new" component={TestModel} onEnter={requireAuth} />
<Route path="/tests/:id" component={Test} />
<Route path="/tests/new" component={NewTest} onEnter={requireAuth} />
<Route path="/tests/:id" component={TestDetail} />
<Route path="*" component={NotFound} />
</Route>
);
Expand Down
35 changes: 22 additions & 13 deletions client/src/state/testModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import { hideSelectImagesModal } from './selectImagesModal';
export const SET_TEST_MODEL = 'SET_TEST_MODEL';
export const REQUEST_TEST_MODEL = 'REQUEST_TEST_MODEL';
export const RESET_TEST_MODEL = 'RESET_TEST_MODEL';
export const INPUT_NV_IMAGE_ID = 'INPUT_NV_IMAGE_ID';

export const setTestModel = createAction(SET_TEST_MODEL);
export const resetTestModel = createAction(RESET_TEST_MODEL);
export const requestTestModel = createAction(REQUEST_TEST_MODEL);
export const inputNVImageId = createAction(INPUT_NV_IMAGE_ID);

function extractId(url) {
return parseInt(url.match(/images\/(\d+)/)[1]);
Expand All @@ -35,27 +37,30 @@ function listImageIds(
);
}

function resetModelTestData(dispatch) {
[resetSearch,
resetSelectedImages,
resetTestModel,
hideSelectImagesModal].map(action => dispatch(action()));
export function resetModelTestData(dispatch) {
[
resetSearch,
resetSelectedImages,
resetTestModel,
hideSelectImagesModal
].map(action => dispatch(action()));
}

export function testModel(
name: string,
modelId: number,
selectedImages: Object,
params: {
name: string,
modelId: number,
neurovaultImageId: number,
selectedImages: Object,
},
router: Object
) {
return (dispatch: Function, getState: Function) => {
dispatch(requestTestModel());

const payload = {
selectedImages: listImageIds(selectedImages),
modelId,
name
};
const payload = Object.assign({}, params, {
selectedImages: listImageIds(params.selectedImages)
});

return api.post('/api/tests', payload, getState().auth.token)
.then(
Expand Down Expand Up @@ -88,6 +93,10 @@ export default function reducer(state: TestModelState = initialState, action: Ac
return Object.assign({}, state, {
isFetching: true
});
case INPUT_NV_IMAGE_ID:
return Object.assign({}, state, {
neurovaultImageId: action.payload
});
case RESET_TEST_MODEL:
return initialState;
default:
Expand Down
8 changes: 6 additions & 2 deletions server/nlweb/image_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,13 @@ def fetch_collection_images(collection_id):
def fetch_collection(collection_id):
url = "%s/api/collections/%s/"

r = requests.get(url % (BASE_NEUROVAULT_URL, collection_id))
return fetch_json(url % (BASE_NEUROVAULT_URL, collection_id))

return r.json()

def fetch_image(image_id):
url = "%s/api/images/%s/"

return fetch_json(url % (BASE_NEUROVAULT_URL, image_id))


def image_media_url(image):
Expand Down
10 changes: 9 additions & 1 deletion server/nlweb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm.attributes import flag_modified

from flask.ext.security import UserMixin, RoleMixin

Expand Down Expand Up @@ -183,7 +184,14 @@ def get_public(cls):
cls.state == cls.STATE_SUCCESS)

def model(self):
return MLModel.query.filter_by(id=self.input_data['modelId']).one()
model_id = self.input_data.get('modelId')
if model_id:
return MLModel.query.filter_by(id=self.input_data['modelId']).one()
else:
return None

def flag_modified(self, attribute):
flag_modified(self, attribute)

def __unicode__(self):
return self.name
Loading