From f4ef14a5f844ffa8ba1440f77f87f70fee906a96 Mon Sep 17 00:00:00 2001 From: roby <roby@ipac.caltech.edu> Date: Sat, 3 Jun 2023 14:28:48 -0600 Subject: [PATCH 1/3] Firefly-1252: fixed issues with there are not spacial columns - changed the processing of tap.additional.services to better reflex the config orider - added hide property to tap.additional.services - temporal column check --- src/firefly/js/ui/tap/SpatialSearch.jsx | 6 ++++- src/firefly/js/ui/tap/TableSearchHelpers.jsx | 3 +++ src/firefly/js/ui/tap/TapUtil.js | 21 ++++++++++------ src/firefly/js/ui/tap/TemportalSearch.jsx | 25 +++++++++++++++----- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/firefly/js/ui/tap/SpatialSearch.jsx b/src/firefly/js/ui/tap/SpatialSearch.jsx index 907f39e863..91620d510b 100644 --- a/src/firefly/js/ui/tap/SpatialSearch.jsx +++ b/src/firefly/js/ui/tap/SpatialSearch.jsx @@ -127,13 +127,14 @@ export function SpatialSearch({cols, serviceUrl, serviceLabel, columnsModel, tab } }, [searchParams.radiusInArcSec, searchParams.wp, searchParams.corners, searchParams.uploadInfo]); - useEffect(() => { const {lon,lat} = formCenterColumns(columnsModel); setVal(CenterLonColumns, lon, {validator: getColValidator(cols, true, false), valid: true}); setVal(CenterLatColumns, lat, {validator: getColValidator(cols, true, false), valid: true}); const noDefaults= !lon || !lat; setVal(posOpenKey, (noDefaults) ? 'open' : 'closed'); + if (noDefaults) checkHeaderCtl.setPanelActive(false); + checkHeaderCtl.setPanelOpen(!noDefaults); setPosOpenMsg(noDefaults?'':TAB_COLUMNS_MSG); }, [columnsModel, obsCoreEnabled]); @@ -717,6 +718,9 @@ function makeSpatialConstraints(columnsModel, obsCoreEnabled, fldObj, uploadInfo const cenLat= cenLatField?.value; errList.checkForError(cenLonField); errList.checkForError(cenLatField); + if (!cenLon && !cenLat) errList.addError('Lon and Lat columns are not set'); + else if (!cenLon) errList.addError('Lon column is not set'); + else if (!cenLat) errList.addError('Lat column is not set'); const ucdCoord = getUCDCoord(columnsModel, cenLon); const worldSys = posCol[ucdCoord.key].coord; const adqlCoordSys = posCol[ucdCoord.key].adqlCoord; diff --git a/src/firefly/js/ui/tap/TableSearchHelpers.jsx b/src/firefly/js/ui/tap/TableSearchHelpers.jsx index 8b6db7d06d..13d0b50891 100644 --- a/src/firefly/js/ui/tap/TableSearchHelpers.jsx +++ b/src/firefly/js/ui/tap/TableSearchHelpers.jsx @@ -160,9 +160,12 @@ export function makeCollapsibleCheckHeader(base) { retObj.CollapsibleCheckHeader= ({title,helpID,message,initialStateOpen, initialStateChecked,children}) => { const [getPanelActive, setPanelActive] = useFieldGroupValue(panelCheckKey);// eslint-disable-line react-hooks/rules-of-hooks + const [getPanelOpenStatus, setPanelOpenStatus] = useFieldGroupValue(panelKey);// eslint-disable-line react-hooks/rules-of-hooks const isActive= getPanelActive() === panelValue; retObj.isPanelActive= () => getPanelActive() === panelValue; retObj.setPanelActive= (active) => setPanelActive(active ? panelValue : ''); + retObj.isPanelOpen= () => getPanelOpenStatus() === 'open'; + retObj.setPanelOpen= (open) => setPanelOpenStatus(open?'open':'close'); return ( <InternalCollapsibleCheckHeader {...{title, helpID, checkKey:panelCheckKey, fieldKey:panelKey, message: isActive ? message:'', initialStateChecked, panelValue, diff --git a/src/firefly/js/ui/tap/TapUtil.js b/src/firefly/js/ui/tap/TapUtil.js index 1f217a1045..2589723e90 100644 --- a/src/firefly/js/ui/tap/TapUtil.js +++ b/src/firefly/js/ui/tap/TapUtil.js @@ -287,13 +287,20 @@ export const getAsEntryForTableName= (tableName) => tableName?.[0] ?? 'x'; function mergeAdditionalServices(tapServices, additional) { if (!hasElements(additional)) return tapServices; - const modifiedOriginal= tapServices.map( (t) => { - const match= additional.find( (a) => a.label===t.label); - return match ? {...t,...match} : t; - }); - const trulyAdditional= additional.filter( (a) => !tapServices.find( (t) => t.label===a.label) ); - - return [...trulyAdditional,...modifiedOriginal]; + const mergeAdditional= additional.map( (a) => { + if (a.hide) return false; + const originalEntry= tapServices.find( (t) => a.label===t.label); + return originalEntry ? {...originalEntry, ...a} : a; + }).filter( (s) => s); + + const unmodifiedOriginal= tapServices + .map( (t) => { + const match= additional.find( (a) => a.label===t.label); + return !match && t; + }) + .filter( (s) => s); + + return [...mergeAdditional,...unmodifiedOriginal]; } export function getTapServices(webApiUserAddedService) { diff --git a/src/firefly/js/ui/tap/TemportalSearch.jsx b/src/firefly/js/ui/tap/TemportalSearch.jsx index d27a264343..d456f78af9 100644 --- a/src/firefly/js/ui/tap/TemportalSearch.jsx +++ b/src/firefly/js/ui/tap/TemportalSearch.jsx @@ -1,6 +1,7 @@ import React, {useContext, useEffect, useState} from 'react'; import PropTypes from 'prop-types'; import {ColsShape, ColumnFld, getColValidator} from '../../charts/ui/ColumnOrExpression.jsx'; +import {getColumnIdx} from '../../tables/TableUtil.js'; import {FieldGroupCtx, ForceFieldGroupValid} from '../FieldGroup.jsx'; import {useFieldGroupRerender, useFieldGroupWatch} from '../SimpleComponent.jsx'; import {checkExposureTime} from '../TimeUIUtil.js'; @@ -76,6 +77,18 @@ const {CollapsibleCheckHeader, collapsibleCheckHeaderKeys}= checkHeaderCtl; const fldAry=[TimeTo,TimeFrom,TemporalColumns]; +export function findTimeColumn(columnsTable) { + const ucdIdx= getColumnIdx(columnsTable,'ucd',true); + const nIdx= getColumnIdx(columnsTable,'column_name',true); + const unitIdx= getColumnIdx(columnsTable,'unit',true); + if (ucdIdx===-1 || nIdx===-1) return; + const timeRows= columnsTable.tableData.data.filter( (row) => row[ucdIdx]?.includes('time.epoch') && row[unitIdx].startsWith('d')); + if (!timeRows.length) return; + const mainRows= timeRows.filter( (row) => row[ucdIdx]?.includes('meta.main') && row[unitIdx].startsWith('d')); + return mainRows.length ? mainRows[0][nIdx] : timeRows[0][nIdx]; +} + + /** * * @param props @@ -86,7 +99,7 @@ const fldAry=[TimeTo,TimeFrom,TemporalColumns]; export function TemporalSearch({cols, columnsModel}) { const [constraintResult, setConstraintResult] = useState({}); - const {setFld,getVal,makeFldObj}= useContext(FieldGroupCtx); + const {setFld,setVal,getVal,makeFldObj}= useContext(FieldGroupCtx); const {setConstraintFragment}= useContext(ConstraintContext); useFieldGroupRerender([...fldAry, ...collapsibleCheckHeaderKeys]); // force rerender on any change const timeCol= getVal(TemporalColumns); @@ -97,10 +110,7 @@ export function TemporalSearch({cols, columnsModel}) { ([tcVal],isInit) => { if (!tcVal) return; const timeColumns = tcVal.split(',').map( (c) => c.trim()) ?? []; - if (timeColumns.length === 1) { - if (!isInit) checkHeaderCtl.setPanelActive(true); - } - else { + if (timeColumns.length > 1) { setFld(TemporalColumns, {value:tcVal, valid:false, message: 'you may only choose one column'}); } }); @@ -117,7 +127,10 @@ export function TemporalSearch({cols, columnsModel}) { useEffect(() => { - setFld(TemporalColumns, {validator: getColValidator(cols, true, false), valid: true}); + const timeCol= findTimeColumn(columnsModel) ?? ''; + setVal(TemporalColumns, timeCol, {validator: getColValidator(cols, true, false), valid: true}); + checkHeaderCtl.setPanelOpen(Boolean(timeCol)); + // checkHeaderCtl.setPanelActive(Boolean(timeCol)); }, [columnsModel]); From 907f99edc86d0dbb767119a109e3f54eb8653ff6 Mon Sep 17 00:00:00 2001 From: loi <loi@ipac.caltech.edu> Date: Tue, 6 Jun 2023 14:51:09 -0700 Subject: [PATCH 2/3] - fixed regression bug when TAP result is not the first item of /results. It should use /results/result instead. --- .../caltech/ipac/firefly/server/query/AsyncTapQuery.java | 7 +++++++ .../caltech/ipac/firefly/server/query/UwsJobProcessor.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/AsyncTapQuery.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/AsyncTapQuery.java index 56fa49934e..ab96b8ecf2 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/query/AsyncTapQuery.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/AsyncTapQuery.java @@ -3,9 +3,11 @@ */ package edu.caltech.ipac.firefly.server.query; +import edu.caltech.ipac.firefly.core.background.JobInfo; import edu.caltech.ipac.firefly.data.TableServerRequest; import edu.caltech.ipac.firefly.server.ServerContext; import edu.caltech.ipac.firefly.server.network.HttpServiceInput; +import edu.caltech.ipac.firefly.server.util.QueryUtil; import edu.caltech.ipac.table.DataGroup; import edu.caltech.ipac.table.DataType; import edu.caltech.ipac.table.TableUtil; @@ -79,4 +81,9 @@ public HttpServiceInput createInput(TableServerRequest request) throws DataAcces } return inputs; } + + @Override + public DataGroup getResult(TableServerRequest request) throws DataAccessException { + return getTableResult(getJobUrl() + "/results/result", QueryUtil.getTempDir(request)); + } } diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/UwsJobProcessor.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/UwsJobProcessor.java index 8475956ab9..28b608ab76 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/query/UwsJobProcessor.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/UwsJobProcessor.java @@ -171,7 +171,7 @@ static String getFilename(String urlStr) { //==================================================================== // UWS utils //==================================================================== - private static DataAccessException createDax(String url, String title, String errMsg) { + static DataAccessException createDax(String url, String title, String errMsg) { String msg = String.format("%s from the URL: [%s]", title, url); if (errMsg != null) msg += "\n\t with exception: " + errMsg; return new DataAccessException(msg); From 5bbc10a650ef08be07d287650be4974d64f5b0ae Mon Sep 17 00:00:00 2001 From: roby <roby@ipac.caltech.edu> Date: Wed, 7 Jun 2023 09:30:00 -0600 Subject: [PATCH 3/3] Firefly-1252: column validator now accepts an error message parameter - also set the error messages for spatial and temporal --- src/firefly/js/charts/ui/ColumnOrExpression.jsx | 8 ++++---- src/firefly/js/ui/tap/SpatialSearch.jsx | 10 ++++++---- src/firefly/js/ui/tap/TemportalSearch.jsx | 4 ++-- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/firefly/js/charts/ui/ColumnOrExpression.jsx b/src/firefly/js/charts/ui/ColumnOrExpression.jsx index 7566aea1bb..e6eb40af58 100644 --- a/src/firefly/js/charts/ui/ColumnOrExpression.jsx +++ b/src/firefly/js/charts/ui/ColumnOrExpression.jsx @@ -46,14 +46,14 @@ function parseSuggestboxContent(text) { return {token, priorContent}; } -export function getColValidator(cols, required=true, canBeExpression=true) { +const DEFAULT_MSG= 'Can not be empty. Please provide value or expression'; + +export function getColValidator(cols, required=true, canBeExpression=true, message=DEFAULT_MSG) { const colNames = cols.map((colVal) => {return colVal.name;}); return (val) => { let retval = {valid: true, message: ''}; if (!val) { - if (required) { - return {valid: false, message: 'Can not be empty. Please provide value or expression'}; - } + if (required) return {valid: false, message}; } else if (colNames.indexOf(val) < 0) { if (canBeExpression) { const expr = new Expression(val, colNames); diff --git a/src/firefly/js/ui/tap/SpatialSearch.jsx b/src/firefly/js/ui/tap/SpatialSearch.jsx index 91620d510b..459a64e2ac 100644 --- a/src/firefly/js/ui/tap/SpatialSearch.jsx +++ b/src/firefly/js/ui/tap/SpatialSearch.jsx @@ -120,8 +120,9 @@ export function SpatialSearch({cols, serviceUrl, serviceLabel, columnsModel, tab const columns = searchParams.uploadInfo.columns; const centerCols = findTableCenterColumns({tableData:{columns}}) ?? {}; const {lonCol='', latCol=''}= centerCols; - setVal(UploadCenterLonColumns, lonCol, {validator: getColValidator(searchParams.uploadInfo.columns, true, false), valid: true}); - setVal(UploadCenterLatColumns, latCol, {validator: getColValidator(searchParams.uploadInfo.columns, true, false), valid: true}); + const errMsg= 'Upload tables require identifying spatial columns containing equatorial coordinates. Please provide column names.'; + setVal(UploadCenterLonColumns, lonCol, {validator: getColValidator(searchParams.uploadInfo.columns, true, false,errMsg), valid: true}); + setVal(UploadCenterLatColumns, latCol, {validator: getColValidator(searchParams.uploadInfo.columns, true, false,errMsg), valid: true}); setUploadInfo(searchParams.uploadInfo); checkHeaderCtl.setPanelActive(true); } @@ -129,8 +130,9 @@ export function SpatialSearch({cols, serviceUrl, serviceLabel, columnsModel, tab useEffect(() => { const {lon,lat} = formCenterColumns(columnsModel); - setVal(CenterLonColumns, lon, {validator: getColValidator(cols, true, false), valid: true}); - setVal(CenterLatColumns, lat, {validator: getColValidator(cols, true, false), valid: true}); + const errMsg= 'Spatial searches require identifying table columns containing equatorial coordinates. Please provide column names.'; + setVal(CenterLonColumns, lon, {validator: getColValidator(cols, true, false, errMsg), valid: true}); + setVal(CenterLatColumns, lat, {validator: getColValidator(cols, true, false, errMsg), valid: true}); const noDefaults= !lon || !lat; setVal(posOpenKey, (noDefaults) ? 'open' : 'closed'); if (noDefaults) checkHeaderCtl.setPanelActive(false); diff --git a/src/firefly/js/ui/tap/TemportalSearch.jsx b/src/firefly/js/ui/tap/TemportalSearch.jsx index d456f78af9..854073d866 100644 --- a/src/firefly/js/ui/tap/TemportalSearch.jsx +++ b/src/firefly/js/ui/tap/TemportalSearch.jsx @@ -128,9 +128,9 @@ export function TemporalSearch({cols, columnsModel}) { useEffect(() => { const timeCol= findTimeColumn(columnsModel) ?? ''; - setVal(TemporalColumns, timeCol, {validator: getColValidator(cols, true, false), valid: true}); + const errMsg= 'Temporal searches require identifying a table column containing a time in MJD. Please provide a column name.'; + setVal(TemporalColumns, timeCol, {validator: getColValidator(cols, true, false, errMsg), valid: true}); checkHeaderCtl.setPanelOpen(Boolean(timeCol)); - // checkHeaderCtl.setPanelActive(Boolean(timeCol)); }, [columnsModel]);