Skip to content
Snippets Groups Projects
Commit 8c7b16b3 authored by Nico Vermaas's avatar Nico Vermaas
Browse files

working on query

parent 972a03df
No related branches found
No related tags found
No related merge requests found
......@@ -6,8 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="ADEX"
content="Astron ADEX"
name="ESAP-gui"
content="Astron ESAP"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<!--
......@@ -27,7 +27,7 @@
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
<title>Astron ADEX</title>
<title>Astron ESAP</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
......
src/assets/esap_logo.png

6.12 KiB

......@@ -11,6 +11,7 @@ import ArchivesPage from '../routes/archives/ArchivesPage';
import DataSetsPage from '../routes/datasets/DataSetsPage';
import TelescopesPage from '../routes/telescopes/TelescopesPage';
import ArchiveDetails from '../routes/details/ArchiveDetails';
import QueryPage from '../routes/query/QueryPage';
import { About } from './About';
......@@ -42,7 +43,7 @@ function Main () {
return (
// basename defines which path is is added before the routing urls.
<Router basename="adex">
<Router basename="esap-gui">
<div>
<NavigationBar/>
......@@ -71,6 +72,10 @@ function Main () {
<DataSetsPage />
</Route>
<Route path="/query">
<QueryPage />
</Route>
<Route path="/about">
<About />
</Route>
......@@ -79,7 +84,7 @@ function Main () {
</Switch>
</div>
<footer><small>ASTRON - version 0.3.0 - 23 jan 2020</small></footer>
<footer><small>ASTRON - version 0.3.3 - 28 jan 2020</small></footer>
</Router>
);
}
......
......@@ -17,6 +17,7 @@ export function NavigationBar() {
<Nav className="mr-auto">
<Nav.Link as={NavLink} to="/">Archives</Nav.Link>
<Nav.Link as={NavLink} to="/datasets">Datasets</Nav.Link>
<Nav.Link as={NavLink} to="/query">Query</Nav.Link>
<Nav.Link as={NavLink} to="/telescopes">Telescopes</Nav.Link>
<Nav.Link as={NavLink} to="/about">About</Nav.Link>
</Nav>
......
......@@ -13,7 +13,7 @@ import { useFetchDataSets } from '../../hooks/useFetchDataSets';
export default function DataSetsCard(props) {
const [ my_state , my_dispatch] = useGlobalReducer()
let query = "data_archive__uri="+props.archive.uri
let query = "dataset_archive__uri="+props.archive.uri
useFetchDataSets(get_backend_url("/esap-api/datasets-uri?"+query),{onMount:true})
let renderDataSetsGrid
......
import React, {useState, useEffect } from 'react';
import { useGlobalReducer } from '../Store';
import { SET_FETCHED_ARCHIVES, SET_STATUS } from '../reducers/GlobalStateReducer';
import { SET_FETCHED_ARCHIVES, SET_STATUS_ARCHIVES } from '../reducers/GlobalStateReducer';
export const useFetchArchives = (url, options) => {
// use global state
......@@ -11,7 +11,7 @@ export const useFetchArchives = (url, options) => {
const fetchData = async (url) => {
try {
// dispatch the status to the global state
my_dispatch({type: SET_STATUS, status: 'fetching'})
my_dispatch({type: SET_STATUS_ARCHIVES, status: 'fetching'})
const res = await fetch(url, options);
const json = await res.json();
......@@ -20,10 +20,10 @@ export const useFetchArchives = (url, options) => {
console.log('useFetchArchives - fetched_archives')
// dispatch the fetched data and the status to the global state
my_dispatch({type: SET_FETCHED_ARCHIVES, fetched_archives: json.results})
my_dispatch({type: SET_STATUS, status: 'fetched_archives'})
my_dispatch({type: SET_STATUS_ARCHIVES, status: 'fetched_archives'})
} catch (error) {
setError(error);
my_dispatch({type: SET_STATUS, status: 'error fetching archives'})
my_dispatch({type: SET_STATUS_ARCHIVES, status: 'error fetching archives'})
}
};
......
import React, {useState, useEffect } from 'react';
import { useGlobalReducer } from '../Store';
import { SET_FETCHED_QUERY, SET_STATUS_QUERY } from '../reducers/GlobalStateReducer';
export const useFetchQuery = (url, options) => {
// use global state
const [ my_state , my_dispatch] = useGlobalReducer()
const [response, setResponse] = React.useState(null);
const [error, setError] = React.useState(null);
const fetchData = async (url) => {
try {
// dispatch the status to the global state
my_dispatch({type: SET_STATUS_QUERY, status_query: 'fetching'})
const res = await fetch(url, options);
const json = await res.json();
// this sets the json in the response, which is in itself a useState hook
setResponse(json);
// dispatch the fetched data and the status to the global state
my_dispatch({type: SET_FETCHED_QUERY, fetched_query: json.query_input})
my_dispatch({type: SET_STATUS_QUERY, status_query: 'fetched_query'})
} catch (error) {
setError(error);
my_dispatch({type: SET_STATUS_QUERY, status_query: 'error fetching query'})
}
};
React.useEffect(() => {
if(options.onMount){
fetchData(url);
}
}, []);
return { response, error, fetchData };
};
\ No newline at end of file
......@@ -2,29 +2,35 @@
// This is the reducer for the global state providor.
// possible actions
export const SET_STATUS = 'SET_STATUS'
export const SET_STATUS_DATASETS = 'SET_STATUS_DATASETS'
export const SET_STATUS_ARCHIVES = 'SET_STATUS'
export const SET_ACTIVE_ARCHIVE = 'SET_ACTIVE_ARCHIVE'
export const SET_FETCHED_ARCHIVES = 'SET_FETCHED_ARCHIVES'
export const SET_STATUS_DATASETS = 'SET_STATUS_DATASETS'
export const SET_FETCHED_DATASETS = 'SET_FETCHED_DATASETS'
export const SET_ALTA_QUERY = 'SET_ALTA_QUERY'
export const SET_ESAP_QUERY = 'SET_ESAP_QUERY'
export const SET_STATUS_QUERY = 'SET_STATUS_QUERY'
export const SET_FETCHED_QUERY = 'SET_FETCHED_QUERY'
export const initialState = {
status: "unfetched",
status_datasets: "unfeched",
status_query: "unfetched",
id: undefined,
archive: undefined,
fetched_archives: undefined,
filtered_archives: undefined,
fetched_datasets: undefined,
filtered_datasets: undefined,
alta_query: ""
fetched_query: undefined,
esap_query: ""
}
export const reducer = (state, action) => {
switch (action.type) {
case SET_STATUS:
case SET_STATUS_ARCHIVES:
return {
...state,
status: action.status
......@@ -36,6 +42,12 @@ export const reducer = (state, action) => {
status_datasets: action.status_datasets
};
case SET_STATUS_QUERY:
return {
...state,
status_query: action.status_query
};
case SET_ACTIVE_ARCHIVE:
return {
...state,
......@@ -54,10 +66,17 @@ export const reducer = (state, action) => {
fetched_datasets: action.fetched_datasets
};
case SET_ALTA_QUERY:
case SET_FETCHED_QUERY:
return {
...state,
fetched_query: action.fetched_query
};
case SET_ESAP_QUERY:
return {
...state,
alta_query: action.alta_query
esap_query: action.esap_query
};
default:
......
......@@ -3,9 +3,6 @@ import { Container, Row, Col, Card, Table } from 'react-bootstrap';
import { useGlobalReducer } from '../../Store';
import DocumentationLink from '../../components/buttons/DocumentationLink'
import CatalogButton from '../../components/buttons/CatalogButton'
import ImageCard from '../../components/cards/ImageCard'
import DescriptionCard from '../../components/cards/DescriptionCard'
import RetrievalCard from '../../components/cards/RetrievalCard'
......@@ -53,11 +50,6 @@ export default function ArchiveDetails(props) {
<td className="key">Description</td>
<td className="value">{archive.short_description}</td>
</tr>
<tr>
<td className="key">Catalog</td>
<td className="value"><a href={archive.catalog_url_derived} target="_blank" rel="noopener noreferrer">{archive.catalog_name_derived}</a>
</td>
</tr>
</tbody>
</Table>
</Card>
......
import React, {useState } from 'react';
import { Redirect } from "react-router-dom"
import { Button } from 'react-bootstrap'
import Form from "react-jsonschema-form";
import LayoutField from "react-jsonschema-form-layout-grid"
import { get_backend_url } from '../../utils/web'
import { useGlobalReducer } from '../../Store';
import { useLocalStorage } from '../../hooks/useLocalStorage';
import { SET_ESAP_QUERY } from '../../reducers/GlobalStateReducer'
//import { useFetchArchives } from '../../hooks/useFetchArchives';
import { useFetchQuery } from '../../hooks/useFetchQuery';
import schema_archives from './schema_archives.json'
export default function QueryArchives(props) {
// react hooks
const [redirect, setRedirect] = useState(false)
const [storedQueryUrl, setStoredQueryUrl] = useLocalStorage('url_archives','')
const [storedQueryJson, setStoredQueryJson] = useLocalStorage('json_archives','')
// custom hooks
const [my_state, my_dispatch] = useGlobalReducer()
//const response = useFetchArchives(get_backend_url("/esap-api/archives"), {})
const response = useFetchQuery(get_backend_url("/esap-api/query"), {})
const log = (type) => console.log.bind(console, type);
let my_schema = schema_archives
const fields = {
layout: LayoutField
}
const uiSchema = {
"runId_start": {"ui:widget": {"color": "red"}},
}
function myObjectFieldTemplate({TitleField, properties, title, description}) {
return (
<div>
<TitleField title={title}/>
<div className="row">
{properties.map(prop => (
<div
className="col-lg-2 col-md-4 col-sm-6 col-xs-12"
key={prop.content.key}>
{prop.content}
</div>
))}
</div>
{description}
</div>
);
}
// construct the query.
// note: this is a bit of business logic where query keywords are used
const constructQuery = (formData) => {
let query = ""
let queryToStore = {}
let key
let value
if (formData.source) {
// using a specific esap keyword just to make it standout
// we may have to make this smarter or more generic at some point,
key = "esap_target"
value = formData.source.trim()
query = query + "&" + key + "=" + value
queryToStore[key]=value
}
if (formData.fov) {
if (formData.fov > 0) {
// RA, dec query is only useful when fov > 0
if (formData.RA) {
key = "ra"
value = formData.RA
query = query + "&" + key + "=" + value
queryToStore[key]=value
}
if (formData.dec) {
key = "dec"
value = formData.dec
query = query + "&" + key + "=" + value
queryToStore[key]=value
}
key = "fov"
value = formData.fov
query = query + "&" + key + "=" + value
queryToStore[key]=value
}
}
// cut off the leading &
query = query.substr(1)
// dispatch the query as state to the global store
my_dispatch({"type": SET_ESAP_QUERY, esap_query: query})
// store the query in localStorage for later retrieval
setStoredQueryJson(queryToStore)
return query
}
// handle the submit and dispatch an action accordingly
const queryESAPBackend = (formData) => {
// construct the query to the ESAP backend (REST API)
let query = constructQuery(formData)
// execute the useFetchReleases hook with the new url
let new_url = get_backend_url("/esap-api/query") + '?' + query
//alert(new_url)
response.fetchData(new_url)
// indicate that the query operation is finished and jump back to the obseration screen.
setRedirect(true)
}
// handle the submit and dispatch an action accordingly
const handleSubmit = ({formData}, e) => {
queryESAPBackend(formData)
}
const handleError = ({errors}, e) => {
alert('errors: '+errors)
}
const handleClearQuery = () => {
// clear the query by clearing the localStorage values
setStoredQueryUrl('')
setStoredQueryJson('')
// reload the values
injectCurrentQuery(schema_archives, storedQueryJson)
// dispatch the state....
// this also triggers the render, because changing a state with a hook rerenders
my_dispatch({type: SET_ESAP_QUERY, alta_query: ''})
}
// the current query parameters that are stored in localStorage are injected into the json that feeds the query
const injectCurrentQuery = (my_schema, my_values) => {
//alert('injectCurrentQuery('+my_values+')')
if (my_values) {
if (my_values.target__icontains) {
my_schema.properties.source.default = my_values.target__icontains
}
if (my_values.view_ra) {
my_schema.properties.RA.default = Number(my_values.view_ra)
}
if (my_values.view_dec) {
my_schema.properties.dec.default = Number(my_values.view_dec)
}
if (my_values.view_fov) {
my_schema.properties.fov.default = Number(my_values.view_fov)
}
} else {
// my_values is empty, empty the fields and reset to the original defaults
my_schema.properties.source.default = ''
my_schema.properties.RA.default = 0
my_schema.properties.dec.default = 0
my_schema.properties.fov.default = 0
}
}
// --- RENDER ---
// the current query parameters that are stored in localStorage are injected into the json that feeds the query
injectCurrentQuery(schema_archives, storedQueryJson)
// if redirect is set (by the submit button) then redirect to the results page
let renderRedirect
if (redirect) {
// todo: redirect this to a results page
renderRedirect = <Redirect to="/query"/>
}
// <Button type="submit" variant="primary" onClick={showQuery}>Show QueryPage</Button>&nbsp;
return (
<div >
{renderRedirect}
<Form schema={my_schema}
uiSchema={uiSchema}
onChange={log("changed")}
ObjectFieldTemplate={myObjectFieldTemplate}
onSubmit={handleSubmit}
onError={handleError} >
<p>{storedQueryUrl}</p>
<Button type="submit" variant="outline-primary">Execute Query</Button>&nbsp;
<Button variant="outline-primary" onClick={() => handleClearQuery()}>Clear Query</Button>&nbsp;
</Form>
</div>
);
}
\ No newline at end of file
import React, {useState } from 'react';
import {Card } from 'react-bootstrap'
import { useGlobalReducer } from '../../Store';
import LoadingSpinner from '../../components/LoadingSpinner';
import QueryArchives from './QueryArchives'
function QueryInputList(props) {
return (
<Card>
<table>
<thead>
<th>dataset</th>
<th>url</th>
<th>query</th>
</thead>
<tbody>
{props.items.map((item, index) => (
<tr>
<td>{item.dataset}</td>
<td>{item.service_url}</td>
<td>{item.query}</td>
</tr>
))}
</tbody>
</table>
</Card>
);
}
export default function QueryPage(props) {
// use the global state
const [ my_state , my_dispatch] = useGlobalReducer()
let renderQueryScreen
if (my_state.fetched_archives!==undefined) {
renderQueryScreen = <div>
<Card className="card-query">
<Card.Body>
<QueryArchives />
</Card.Body>
</Card>
</div>
}
let renderSpinner
if (my_state.status === "fetching") {
renderSpinner = <LoadingSpinner/>
}
let renderQueryInputResults
if (my_state.fetched_query!==undefined) {
renderQueryInputResults = <QueryInputList items = {my_state.fetched_query} />
//alert(renderQueryInputResults)
}
return (
<div >
{renderSpinner}
{renderQueryScreen}
{renderQueryInputResults}
</div>
);
}
\ No newline at end of file
{
"title": "Search Archives",
"type": "object",
"properties": {
"source": {
"type": "string",
"title": "Target"
},
"RA": {
"type": "number",
"title": "RA (degrees)"
},
"dec": {
"type": "number",
"title": "dec (degrees)"
},
"fov": {
"type": "number",
"title": "search radius (degrees)"
}
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment