diff --git a/src/components/Main.js b/src/components/Main.js index e8662d3e139d79e5ec589bb4bb800b3ded328a87..bbb911b5f360cf7220a3190ced81cf7c02cb7cc1 100644 --- a/src/components/Main.js +++ b/src/components/Main.js @@ -79,7 +79,7 @@ function Main () { </Switch> </div> - <footer><small>ASTRON - version 0.6.4 - 2 apr 2020</small></footer> + <footer><small>ASTRON - version 0.6.5 - 16 apr 2020</small></footer> </Router> ); } diff --git a/src/components/buttons/RunQueriesButton.js b/src/components/buttons/RunQueriesButton.js index 821156439d160b56f57b42266aad609eb5553331..3599293c7fd1c8b177def4bdef54691cb2e9a170 100644 --- a/src/components/buttons/RunQueriesButton.js +++ b/src/components/buttons/RunQueriesButton.js @@ -8,7 +8,7 @@ import { GetBackendHost } from '../../utils/web' import { useGlobalReducer } from '../../Store'; import { useFetchRunQuery } from '../../hooks/useFetchRunQuery'; -import { SET_DATASETS_TO_QUERY, SET_QUERIES_HAVE_RESULTS } from '../../reducers/GlobalStateReducer'; +import { SET_QUERIES_TO_RUN, SET_QUERIES_HAVE_RESULTS } from '../../reducers/GlobalStateReducer'; // show a clickable icon export default function RunQueriesButton(props) { @@ -25,31 +25,45 @@ export default function RunQueriesButton(props) { const runQuery = (input_query) => { let query = input_query.query + let access_url = query.split('?')[0] // if this is a VO query, then cut off everything but the ADQL itself so that the backend can parse it. if (input_query.esap_service.toUpperCase()==='VO') { //let query = input_query.query let s = query.split('&QUERY=') query = s[1] + } else + + if (input_query.esap_service.toUpperCase()==='VO_REG') { + //let query = input_query.query + let s = query.split('&QUERY=') + query = s[1] } - let url = getBackendUrl("run-query") + "?dataset_uri=" + input_query.dataset + "&query=" + query + let url = getBackendUrl("run-query") + + "?dataset_uri=" + input_query.dataset + + "&dataset_name=" + input_query.dataset_name + + "&access_url=" + access_url + + "&query=" + query + // encode it - fetchRunQuery.fetchData(encodeURI(url), input_query.dataset) + // fetchRunQuery.fetchData(encodeURI(url), input_query.dataset) + fetchRunQuery.fetchData(encodeURI(url), input_query.dataset_name) + } // run all the queries in the selected list of queries const runQueries = (queries) => { - let datasets_to_query = [] + let queries_to_run = [] for (var i=0; i< queries.length; i++) { - //datasets_to_query.push(queries[i].dataset) // uri only - datasets_to_query.push(queries[i]) + //alert(queries[i].dataset_name) + queries_to_run.push(queries[i]) } // store the list of datasets to query. This can later be used as keys to iterate over the results - my_dispatch({type: SET_DATASETS_TO_QUERY, datasets_to_query: datasets_to_query}) + my_dispatch({type: SET_QUERIES_TO_RUN, queries_to_run: queries_to_run}) my_dispatch({type: SET_QUERIES_HAVE_RESULTS, queries_have_results: true}) // run the queries by firing a asyc request per query diff --git a/src/components/cards/QueryCard.js b/src/components/cards/QueryCard.js new file mode 100644 index 0000000000000000000000000000000000000000..0c3e019767c35318acb3f27103981e3364d69f8a --- /dev/null +++ b/src/components/cards/QueryCard.js @@ -0,0 +1,52 @@ +import React from 'react'; +import {Card, Table } from 'react-bootstrap' + +import { useGlobalReducer } from '../../Store'; + +import DocumentationLink from '../buttons/DocumentationLink' + + +// display a single query on a card +export default function QueryCard(props) { + + const [ my_state , my_dispatch] = useGlobalReducer() + + // only show if there is content + if (!props.query) { + return null + } + + return ( + + <Card className="card-description"> + <Card.Body> + <Card.Title>{props.query.dataset_name}</Card.Title> + <Card.Text> + <Table striped bordered hover size="sm"> + <tbody> + <tr> + <td className="key">ID</td> + <td className="value">{props.query.query_id}</td> + </tr> + <tr> + <td className="key">Query</td> + <td className="value">{props.query.query}</td> + </tr> + <tr> + <td className="key">Description</td> + <td className="value">{props.query.res_description}</td> + </tr> + </tbody> + </Table> + + </Card.Text> + <DocumentationLink url={props.query.reference_url}/> + </Card.Body> + + </Card> + + ); + +} + + diff --git a/src/hooks/useFetchRunQuery.js b/src/hooks/useFetchRunQuery.js index 303e7ef3886c4f0852fc073d87b67203fe71b569..b157b4522a7cdb1f2421697e0f991df5dd6a39ac 100644 --- a/src/hooks/useFetchRunQuery.js +++ b/src/hooks/useFetchRunQuery.js @@ -25,15 +25,15 @@ export const useFetchRunQuery = (url, options) => { const [response, setResponse] = React.useState(null); const [error, setError] = React.useState(null); - const fetchData = async (url, dataset, current_fetched_query_results) => { + const fetchData = async (url, query) => { let query_status_object={} - let query_status = dataset+":fetching..." - query_status_object[dataset]=query_status + let query_status = query+":fetching..." + query_status_object[query]=query_status try { // dispatch the status to the global state - my_dispatch({type: SET_STATUS_RUN_QUERY, status_run_query: query_status_object[dataset]}) + my_dispatch({type: SET_STATUS_RUN_QUERY, status_run_query: {}}) const res = await fetch(url, options); const json = await res.json(); @@ -41,21 +41,14 @@ export const useFetchRunQuery = (url, options) => { // this sets the json in the response, which is in itself a useState hook setResponse(json); - let query_results = {} - query_results[dataset] = json.query_results - - // dispatch the fetched data (per dataset) and the status to the global state - my_dispatch({type: SET_FETCHED_QUERY_RESULTS, fetched_query_results: query_results[dataset]}) - - query_status = dataset+":fetched" - query_status_object[dataset]=query_status - my_dispatch({type: SET_STATUS_RUN_QUERY, status_run_query: query_status_object[dataset]}) + // dispatch the fetched data (per query) and the status to the global state + my_dispatch({type: SET_FETCHED_QUERY_RESULTS, fetched_query_results: json.query_results}) + my_dispatch({type: SET_STATUS_RUN_QUERY, status_run_query: query+":fetched"}) } catch (error) { - alert('ERROR: ' + error.toString()) setError(error); - query_status = dataset+":error" + query_status = query+":error" my_dispatch({type: SET_STATUS_RUN_QUERY, status_run_query: query_status}) } }; diff --git a/src/reducers/GlobalStateReducer.js b/src/reducers/GlobalStateReducer.js index 2194a8fd7abc454c6bf56a8545e2267ed863faeb..2dca66ad74acf9d08ccf666b91a6019e6862fcc8 100644 --- a/src/reducers/GlobalStateReducer.js +++ b/src/reducers/GlobalStateReducer.js @@ -16,7 +16,7 @@ export const SET_ESAP_QUERY = 'SET_ESAP_QUERY' export const SET_QUERIES_HAVE_RESULTS = 'SET_QUERIES_HAVE_RESULTS' export const SET_STATUS_QUERY = 'SET_STATUS_QUERY' export const SET_STATUS_RUN_QUERY = 'SET_STATUS_RUN_QUERY' -export const SET_DATASETS_TO_QUERY = 'SET_DATASETS_TO_QUERY' +export const SET_QUERIES_TO_RUN = 'SET_QUERIES_TO_RUN' export const SET_FETCHED_QUERY_INPUT = 'SET_FETCHED_QUERY_INPUT' export const SET_FETCHED_QUERY_RESULTS = 'SET_FETCHED_QUERY_RESULTS' @@ -33,7 +33,7 @@ export const initialState = { filtered_archives: undefined, fetched_datasets: undefined, filtered_datasets: undefined, - datasets_to_query: undefined, + queries_to_run: undefined, fetched_query: undefined, fetched_query_results: undefined, queries_have_results: false, @@ -99,10 +99,10 @@ export const reducer = (state, action) => { }; - case SET_DATASETS_TO_QUERY: + case SET_QUERIES_TO_RUN: return { ...state, - datasets_to_query: action.datasets_to_query + queries_to_run: action.queries_to_run }; case SET_QUERIES_HAVE_RESULTS: @@ -112,24 +112,30 @@ export const reducer = (state, action) => { }; case SET_STATUS_RUN_QUERY: - // construct the status_run_query object in such a way that it stores results per dataset. + // construct the status_run_query object in such a way that it stores results per query. var state_to_return = {...state} try { - let array = action.status_run_query.split(':') - let key = "status_run_query." + array[0] + // example: "UCL Astro TAP:fetched" + let status = action.status_run_query.split(':') + let key = "status_run_query." + status[0] - state_to_return[key]= array[1] + state_to_return[key]= status[1] } finally { return state_to_return } case SET_FETCHED_QUERY_RESULTS: - // construct the fetched_query_results object in such a way that it stores results per dataset. + // construct the fetched_query_results object in such a way that it stores results per query. var state_to_return = {...state} try { - let dataset = action.fetched_query_results[0].dataset - let key = "fetched_query_results." + dataset + + // the dataset_name is used in the key for the state, so that the state holds results per dataset_name + // because 'dataset' isn't unique in case of the 'vo_reg' dataset which yields many dataset names. + + let dataset_name = action.fetched_query_results[0].dataset_name + let key = "fetched_query_results." + dataset_name + state_to_return[key]= action.fetched_query_results } finally { diff --git a/src/routes/query/QueryArchives.js b/src/routes/query/QueryArchives.js index 78c74431c62a342b83e361745855deca520234f7..7d30b85ab97714ccadd6c8570848d2db80854218 100644 --- a/src/routes/query/QueryArchives.js +++ b/src/routes/query/QueryArchives.js @@ -17,7 +17,8 @@ import { SET_ESAP_QUERY, SET_FETCHED_QUERY_INPUT, SET_FETCHED_QUERY_RESULTS, - SET_STATUS_RUN_QUERY + SET_STATUS_RUN_QUERY, + SET_QUERIES_HAVE_RESULTS } from '../../reducers/GlobalStateReducer' import { useFetchCreateQuery } from '../../hooks/useFetchCreateQuery'; @@ -237,15 +238,19 @@ export default function QueryArchives(props) { // clear previous results my_dispatch({type: SET_FETCHED_QUERY_INPUT, fetched_query: undefined}) + //my_dispatch({type: SET_FETCHED_QUERY_RESULTS, fetched_query_results: {}}) - my_dispatch({type: SET_FETCHED_QUERY_RESULTS, fetched_query_results: {}}) - - // this clears the output results grid/tiles of the executed queries + // this clears the output results grid/tiles of the executed queries my_dispatch({type: SET_STATUS_RUN_QUERY, status_run_query: undefined}) + my_dispatch({type: SET_QUERIES_HAVE_RESULTS, queries_have_results: false}) + let queries = my_state.queries_to_run + if (queries) { + for (var i = 0; i < queries.length; i++) { + my_dispatch({type: SET_STATUS_RUN_QUERY, status_run_query: queries[i].dataset_name + ":n/a"}) - //my_dispatch({type: SET_DATASETS_TO_QUERY, datasets_to_query:undefined}) - + } + } queryESAPBackend(formData) } diff --git a/src/routes/query/QueryInputResultsGrid.js b/src/routes/query/QueryInputResultsGrid.js index a1e00ea6f4a9894ccf736740d308e4e580ab2a8b..e21ffeb7a1fee6ee86b5f807e1db08e2a77d10ba 100644 --- a/src/routes/query/QueryInputResultsGrid.js +++ b/src/routes/query/QueryInputResultsGrid.js @@ -4,7 +4,7 @@ import { Button, Card } from 'react-bootstrap'; import DataTable from 'react-data-table-component'; import { useGlobalReducer } from '../../Store'; -import { SET_DATASETS_TO_QUERY, } from '../../reducers/GlobalStateReducer' +import { SET_QUERIES_TO_RUN, } from '../../reducers/GlobalStateReducer' import RunQueriesButton from '../../components/buttons/RunQueriesButton' @@ -23,13 +23,13 @@ export default function QueryInputResultsGrid(props) { setSelectedRows(state.selectedRows); - let datasets_to_query = [] + let queries_to_run = [] for (var i=0; i< state.selectedRows.length; i++) { - datasets_to_query.push(state.selectedRows[i]) + queries_to_run.push(state.selectedRows[i]) } // store the list of datasets to query. This can later be used as keys to iterate over the results - my_dispatch({type: SET_DATASETS_TO_QUERY, datasets_to_query: datasets_to_query}) + my_dispatch({type: SET_QUERIES_TO_RUN, queries_to_run: queries_to_run}) // invalidate the current selection @@ -42,18 +42,23 @@ export default function QueryInputResultsGrid(props) { let value = row.dataset try { - let key = "status_run_query." + row.dataset + let key = "status_run_query." + row.dataset_name let status = my_state[key] let count = '' if (status.includes('fetched')) { - key = "fetched_query_results." + row.dataset - count = " (" + my_state[key].length.toString() + ")" + key = "fetched_query_results." + row.dataset_name + try { + count = " (" + my_state[key].length.toString() + ")" + } catch (e) { + count = " (0)" + } } value = status + count } catch (e) { + //alert(e.toString()) value = 'n/a' } @@ -65,17 +70,25 @@ export default function QueryInputResultsGrid(props) { const columns = [ { - name: 'dataset uri', - selector: 'dataset', + name: 'ID', + selector: 'query_id', sortable: true, width: "10%" }, { - name: 'dataset name', + name: 'dataset', selector: 'dataset_name', sortable: true, width: "15%" }, +/* + { + name: 'Waveband', + selector: 'waveband', + sortable: true, + width: "10%" + }, +*/ /* { name: 'service', @@ -83,20 +96,26 @@ export default function QueryInputResultsGrid(props) { sortable: true, width: "5%" }, -*/ + { name: 'service connector', selector: 'service_connector', sortable: true, width: "10%" }, - +*/ { name: 'status', sortable: true, width: "10%", cell: row => getStatus(row), }, + { + name: 'access_url', + sortable: true, + width: "20%", + selector: 'service_url', + }, /* { name: 'resource', @@ -105,6 +124,7 @@ export default function QueryInputResultsGrid(props) { width: "10%" }, */ +/* { name: 'query', selector: 'query', @@ -114,6 +134,16 @@ export default function QueryInputResultsGrid(props) { {row.query} </a>, }, +*/ + { + name: 'Reference', + selector: 'reference_url', + sortable: true, + cell: row => + <a href={row.reference_url} target="_blank"> + {row.reference_url} + </a>, + }, ]; @@ -162,9 +192,43 @@ export default function QueryInputResultsGrid(props) { } ]; + + const countQueryStatus = (search_status) => { + let queries = my_state.queries_to_run + let count = 0 + + if (queries) { + for (var i = 0; i < queries.length; i++) { + try { + let key = "status_run_query." + queries[i].dataset_name + let status = my_state[key] + + if (status.includes(search_status)) { + count = count + 1 + } + } catch (e) { + // alert(e) + } + } + } + return count + } + + + let number_selected = 0 + if (my_state.queries_to_run) { + number_selected = my_state.queries_to_run.length + } + + let subtitle = "" + if (number_selected > 0) { + subtitle = " fetched " + countQueryStatus('fetched').toString() + " of " + number_selected.toString() + } + let title = "Generated Queries: " + subtitle + return ( <div> - <h4> Created Queries</h4> + <h4> {title}</h4> <DataTable //title="Created Queries" columns={columns} diff --git a/src/routes/query/QueryOutputResultsGrid.js b/src/routes/query/QueryOutputResultsGrid.js index 41bb0a5b789deaae27dfe91558ec400a4b49b8e0..e590b44bafb6fc11991a2172276fa091d2ffe597 100644 --- a/src/routes/query/QueryOutputResultsGrid.js +++ b/src/routes/query/QueryOutputResultsGrid.js @@ -9,6 +9,7 @@ import { getBackendUrl } from '../../utils/web' import AddToBasketButton from '../../components/buttons/AddToBasketButton' import DataProductImageCard from '../../components/cards/DataProductImageCard' +import QueryCard from '../../components/cards/QueryCard' // the definition of the columns // moved outside of the main function so that it will not rerender @@ -80,13 +81,12 @@ export default function QueryOutputResultsGrid(props) { sortable: true, width: "20%" }, - { name: 'Result', selector: 'result', sortable: true, cell: row => drawResult(row), - width: "20%" + //width: "20%" }, { name: 'Link to data', @@ -119,27 +119,26 @@ export default function QueryOutputResultsGrid(props) { }, [selectedRows, toggleCleared]); // get the query results from the state - let datasetsToQuery = my_state.datasets_to_query + let datasetsToQuery = my_state.queries_to_run let data - let title = "Query Results (List)" - if (props.dataset) { - // filter the data on dataset - title = "Query Results for " + props.dataset.dataset - let key = "fetched_query_results." + props.dataset.dataset - data = my_state[key] + if (props.query) { + // dataset is given as a parameter (current situation) + data = my_state["fetched_query_results." + props.query.dataset_name] } else if (props.data) { + // data is given as a parameter (currently not used) data = props.data } else { + // combine the results from different queries. // iterate through the results of the several queries data = [] datasetsToQuery.forEach(dataset => { - let key = "fetched_query_results." + dataset.dataset + let key = "fetched_query_results." + dataset.dataset_name let resultsPerQuery = my_state[key] if (resultsPerQuery!==undefined) { @@ -152,11 +151,12 @@ export default function QueryOutputResultsGrid(props) { // alert('total length = ' + data.length.toString()) } + let queryCard = <QueryCard query={props.query}/> + return ( <div> - <h4> {title}</h4> + {queryCard} <DataTable - // title="Query Results" columns={columns} data={data} striped={true} diff --git a/src/routes/query/QueryOutputResultsTiles.js b/src/routes/query/QueryOutputResultsTiles.js index dbafbd8697a0d6ac6864a7ef576cf93760b616ab..70b8f86c9f734c6b3a8911d483ecee531ce19e39 100644 --- a/src/routes/query/QueryOutputResultsTiles.js +++ b/src/routes/query/QueryOutputResultsTiles.js @@ -6,6 +6,7 @@ import { useGlobalReducer } from '../../Store'; import { SET_ACTIVE_DATASET } from '../../reducers/GlobalStateReducer' import { getBackendUrl } from '../../utils/web' +import QueryCard from '../../components/cards/QueryCard' import AddToBasketButton from '../../components/buttons/AddToBasketButton' import Tiles from '../../components/Tiles' @@ -20,10 +21,10 @@ export default function QueryOutputResultsTiles(props) { // iterate through the results of the several queries data = [] - if (props.dataset) { + if (props.query) { // filter the data on dataset - title = "Query Results for " + props.dataset.dataset - let key = "fetched_query_results." + props.dataset.dataset + title = "Query Results: " + props.query.dataset_name + let key = "fetched_query_results." + props.query.dataset_name data = my_state[key] } else @@ -34,8 +35,8 @@ export default function QueryOutputResultsTiles(props) { } else { // get the list of datasets to query from the global state // (currently this option is no longer used) - my_state.datasets_to_query.forEach(dataset => { - let key = "fetched_query_results." + dataset.dataset + my_state.queries_to_run.forEach(dataset => { + let key = "fetched_query_results." + dataset.dataset_name let resultsPerQuery = my_state[key] if (resultsPerQuery !== undefined) { @@ -51,9 +52,11 @@ export default function QueryOutputResultsTiles(props) { renderData = <Tiles data = {data} /> } + let queryCard = <QueryCard query={props.query}/> + return ( <div> - <h4> {title}</h4> + {queryCard} <div className="App"> <Container fluid> diff --git a/src/routes/query/QueryPage.js b/src/routes/query/QueryPage.js index e2715aa9383befcc4a7616a9bc48a033fb0d2fea..a5b2503bf8bfd7d81b4611608a4b7c84b88e9490 100644 --- a/src/routes/query/QueryPage.js +++ b/src/routes/query/QueryPage.js @@ -40,7 +40,7 @@ export default function QueryPage(props) { let renderQueryResults //alert(my_state.queries_are_running) if (my_state.queries_have_results) { - if (my_state.datasets_to_query.length > 0) { + if (my_state.queries_to_run.length > 0) { renderQueryResults = <QueryResultsPage/> } } diff --git a/src/routes/query/QueryResultsPage.js b/src/routes/query/QueryResultsPage.js index 6d757c3996cae5057a1b032b269ae23624a187be..7f880c0925fad73ba64f9140181b0260835cd3e1 100644 --- a/src/routes/query/QueryResultsPage.js +++ b/src/routes/query/QueryResultsPage.js @@ -21,30 +21,31 @@ export default function QueryResultsPage(props) { // get the query results from the state let renderOutputResults - const renderDataset = (dataset) => { - if (dataset.output_format.toUpperCase()==='TILES') { - return <QueryOutputResultsTiles dataset={dataset}/> + const renderQueryResults = (query) => { + if (query.output_format.toUpperCase()==='TILES') { + return <QueryOutputResultsTiles query={query}/> } else { - return <QueryOutputResultsGrid dataset={dataset}/> + return <QueryOutputResultsGrid query={query}/> } } if (my_state.queries_have_results) { - if (my_state.datasets_to_query.length > 0) { + if (my_state.queries_to_run.length > 0) { - let datasetsToQuery = my_state.datasets_to_query + let datasetsToQuery = my_state.queries_to_run let data = [] // combine the results from different queries. // iterate through the results of the several queries - renderOutputResults = my_state.datasets_to_query.map(dataset => { - let key = "fetched_query_results." + dataset.dataset + renderOutputResults = my_state.queries_to_run.map(query => { + let key = "fetched_query_results." + query.dataset_name let resultsPerQuery = my_state[key] if (resultsPerQuery !== undefined) { if (resultsPerQuery.length > 0) { - return renderDataset(dataset) + //alert(key+" : " +resultsPerQuery) + return renderQueryResults(query) } } }) diff --git a/src/utils/web.js b/src/utils/web.js index 8b47b4fb088e84e2678ca309b4b005b44a683e89..8df2d677e130cf5d64d51e86bb6c3ce3be099148 100644 --- a/src/utils/web.js +++ b/src/utils/web.js @@ -20,7 +20,6 @@ export function GetBackendHost() { let protocol = window.location.protocol; let slashes = protocol.concat("//"); let host = slashes.concat(window.location.hostname)+"/esap-api/"; - //let host = "http://uilennest.net/esap-api/" return host }