Skip to content

Commit b9f9a60

Browse files
author
walrus51
committed
#VFB-226 - Enhance query validation and improve query handling in frontend components
1 parent 0d5e3b0 commit b9f9a60

File tree

4 files changed

+82
-36
lines changed

4 files changed

+82
-36
lines changed

applications/virtual-fly-brain/backend/virtual_fly_brain/services/queries.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
def run_query(id, query_type):
55
# TODO: this will have to be extended to handle a list of ids as params
66
try:
7+
# Validate query_type - it must be provided and not None or 'undefined'
8+
if query_type is None or query_type == 'undefined' or query_type == '':
9+
raise ValueError("query_type parameter is required and must be a valid query type. Use get_term_info endpoint to retrieve available queries.")
10+
711
# Create a unique cache key for the query
812
cache_key = f"query_{id}_{query_type}"
913
cached_data = queries_cache.get(cache_key)
@@ -35,10 +39,7 @@ def run_query(id, query_type):
3539

3640
return data_queries
3741
else:
38-
# For queries list, we can also cache this
39-
result = dict({'queries': queries, 'name': data['Name']})
40-
queries_cache.set(cache_key, result)
41-
return result
42+
raise ValueError(f"Query type '{query_type}' not found for instance '{id}'")
4243

4344
except Exception as e:
4445
return str(e)

applications/virtual-fly-brain/frontend/src/network/query.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ export const get_queries = async (queryId) => {
2121
return response.json()
2222
})
2323
.then((data) => {
24-
return data;
24+
// Extract only the queries array and name to minimize response
25+
return {
26+
queries: data.query?.Queries || data.Queries || [],
27+
name: data.Name || data.query?.Name
28+
};
2529
});
2630
return response;
2731
}

applications/virtual-fly-brain/frontend/src/reducers/actions/queries.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import store from '../../store';
2-
import { get_query_results } from "../../network/query"
2+
import { get_query_results, get_queries } from "../../network/query"
33
import { getQueriesTypes } from './types/getQueriesTypes';
44

55
const getQueriesSuccess = (query, short_form, type) => ({
@@ -42,14 +42,40 @@ export const getQueriesFailure = ( error, id) => ({
4242
export const getQueries = async (short_form, type) => {
4343

4444
store.dispatch(_getQueriesStarted())
45+
46+
const state = store.getState();
47+
const allLoadedInstances = state.instances.allLoadedInstances;
48+
4549
let response;
4650
try {
47-
response = await get_query_results(short_form, type);
51+
// When type is provided - EXECUTE the specific query
52+
if (type) {
53+
response = await get_query_results(short_form, type);
54+
store.dispatch(getQueriesSuccess(response, short_form, type))
55+
}
56+
// When type is NOT provided - LOAD available queries list
57+
else {
58+
// Check if instance already loaded (CACHE CHECK)
59+
const existingInstance = allLoadedInstances?.find(
60+
i => i.metadata?.Id === short_form
61+
);
62+
63+
if (existingInstance?.metadata?.Queries) {
64+
// Use cached instance data - no network call needed!
65+
response = {
66+
queries: existingInstance.metadata.Queries,
67+
name: existingInstance.metadata.Name
68+
};
69+
} else {
70+
// Instance not loaded, fetch queries from backend using get_queries
71+
response = await get_queries(short_form);
72+
}
73+
74+
store.dispatch(getQueriesSuccess(response, short_form, undefined))
75+
}
4876
} catch (error) {
4977
store.dispatch(getQueriesFailure(error.message, short_form))
5078
}
51-
52-
store.dispatch(getQueriesSuccess(response, short_form, type))
5379
}
5480

5581
export const deleteQuery = async (instance) => {

applications/virtual-fly-brain/frontend/src/shared/header/index.jsx

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { getLayoutManagerInstance } from "@metacell/geppetto-meta-client/common/
2020

2121
const { primaryBg, headerBoxShadow } = vars;
2222

23-
const Header = ({setBottomNav}) => {
23+
const Header = ({ setBottomNav }) => {
2424
const classes = {
2525
root: {
2626
background: primaryBg,
@@ -38,6 +38,7 @@ const Header = ({setBottomNav}) => {
3838
const recentSearches = useSelector(state => state.globalInfo.recentSearches)
3939
const queries = useSelector(state => state.queries.queries)
4040
const allLoadedInstances = useSelector(state => state.instances.allLoadedInstances)
41+
const focusedInstance = useSelector(state => state.instances.focusedInstance)
4142
const widgets = useSelector(state => state.widgets);
4243
const firstIdLoaded = useSelector(state => state.globalInfo.firstIDLoaded);
4344
let autoSaveLayout = useSelector(state => state.globalInfo.autoSaveLayout);
@@ -55,8 +56,20 @@ const Header = ({setBottomNav}) => {
5556
}
5657

5758
const handleQueryStatsClick = () => {
58-
// TODO: what to do here?
59-
console.log('QueryStats Clicked!')
59+
// Open queries for the focused instance
60+
if (focusedInstance?.metadata?.Id) {
61+
// Ensure queries are loaded if not already
62+
const queriesForInstance = queries?.find(
63+
q => q.short_form === focusedInstance.metadata.Id
64+
);
65+
66+
if (!queriesForInstance) {
67+
getQueries(focusedInstance.metadata.Id);
68+
}
69+
70+
// Open the query component panel
71+
setBottomNav(2);
72+
}
6073
}
6174

6275
const handleMenuClick = () => {
@@ -67,7 +80,7 @@ const Header = ({setBottomNav}) => {
6780
* Handler function triggered when a Menu item is clicked.
6881
*/
6982
const menuHandler = (action, _component) => {
70-
switch (action.handlerAction){
83+
switch (action.handlerAction) {
7184
case ACTIONS.SHOW_WIDGET: {
7285
const newWidget = { ...widgets[action.parameters[0]] }
7386
const layoutManager = getLayoutManagerInstance();
@@ -100,7 +113,7 @@ const Header = ({setBottomNav}) => {
100113
case ACTIONS.SHOW_COMPONENT:
101114
setBottomNav(action.parameters[0])
102115
break;
103-
case ACTIONS.SHOW_TERM_INFO:{
116+
case ACTIONS.SHOW_TERM_INFO: {
104117
dispatch(setTermInfoOpened(true))
105118
break;
106119
}
@@ -109,38 +122,38 @@ const Header = ({setBottomNav}) => {
109122
window.open(item, '_blank');
110123
})
111124
break;
112-
case ACTIONS.SELECT_INSTANCE:{
113-
let matchInstance = allLoadedInstances?.find( q => q.metadata?.Id === action.parameters[0] );
114-
if (matchInstance ) {
125+
case ACTIONS.SELECT_INSTANCE: {
126+
let matchInstance = allLoadedInstances?.find(q => q.metadata?.Id === action.parameters[0]);
127+
if (matchInstance) {
115128
focusInstance(action.parameters[0])
116129
selectInstance(action.parameters[0])
117130
} else {
118131
getInstanceByID(action.parameters[0], true, true, true)
119132
}
120133
break;
121134
}
122-
case ACTIONS.RUN_QUERY:{
135+
case ACTIONS.RUN_QUERY: {
123136
let updatedQueries = [...queries];
124-
let matchQuery = updatedQueries?.find( q => q.short_form === action.parameters[0] );
125-
updatedQueries?.forEach( query => {
126-
if( query.queries ){
127-
Object.keys(query.queries)?.forEach( q => query.queries[q].active = false );
137+
let matchQuery = updatedQueries?.find(q => q.short_form === action.parameters[0]);
138+
updatedQueries?.forEach(query => {
139+
if (query.queries) {
140+
Object.keys(query.queries)?.forEach(q => query.queries[q].active = false);
128141
}
129142
});
130-
if ( matchQuery?.queries?.[action?.parameters[1]] ) {
143+
if (matchQuery?.queries?.[action?.parameters[1]]) {
131144
matchQuery.queries[action.parameters[1]].active = true;
132145
updateQueries(updatedQueries);
133146
setBottomNav(2)
134147
} else {
135-
getQueries(action.parameters[0],action.parameters[1])
148+
getQueries(action.parameters[0], action.parameters[1])
136149
setBottomNav(2)
137150
}
138151
break;
139152
}
140-
case ACTIONS.HISTORY_MENU_INJECTOR:{
153+
case ACTIONS.HISTORY_MENU_INJECTOR: {
141154
var historyList = [];
142155
// Add instances to history menu
143-
recentSearches?.reverse()?.forEach( i => {
156+
recentSearches?.reverse()?.forEach(i => {
144157
historyList.push(
145158
{
146159
label: i?.label,
@@ -154,12 +167,12 @@ const Header = ({setBottomNav}) => {
154167
})
155168
return historyList;
156169
}
157-
case ACTIONS.LOAD_LAYOUT:{
170+
case ACTIONS.LOAD_LAYOUT: {
158171
if (!firstIdLoaded) {
159172
dispatch(triggerInstanceFailure('No instance loaded, wait please ...'));
160173
break;
161174
}
162-
switch (action.parameters[0]){
175+
switch (action.parameters[0]) {
163176
case 'layout1':
164177
dispatch(loadCustomLayout(layout1));
165178
break;
@@ -263,7 +276,7 @@ const Header = ({setBottomNav}) => {
263276
</Box>
264277

265278
<Box
266-
sx={ {
279+
sx={{
267280
'& span': {
268281
display: {
269282
xs: 'block',
@@ -295,13 +308,15 @@ const Header = ({setBottomNav}) => {
295308
</Box>
296309

297310
<MediaQuery minWidth={1200}>
298-
{/* <Button
299-
onClick={() => setBottomNav((prev) => prev === 2 ? null : 2)}
300-
variant="outlined"
301-
>
302-
<QueryStats size={16} />
303-
Queries for V_ilpn (FlyEM-HB:2064165421)
304-
</Button> */}
311+
{focusedInstance?.metadata?.Id && (focusedInstance?.metadata?.Queries?.length > 0 || queries?.find(q => q.short_form === focusedInstance.metadata.Id)) && (
312+
<Button
313+
onClick={() => setBottomNav((prev) => prev === 2 ? null : 2)}
314+
variant="outlined"
315+
>
316+
<QueryStats size={16} />
317+
Queries for {focusedInstance.metadata?.Name || ''} ({focusedInstance.metadata?.Id || ''})
318+
</Button>
319+
)}
305320
</MediaQuery>
306321
</Box>
307322
)

0 commit comments

Comments
 (0)