diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/IrsaLightCurveHandler.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/IrsaLightCurveHandler.java new file mode 100644 index 0000000000..0b386fe55c --- /dev/null +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/IrsaLightCurveHandler.java @@ -0,0 +1,144 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ +package edu.caltech.ipac.firefly.server.query.lc; + +import edu.caltech.ipac.astro.IpacTableWriter; +import edu.caltech.ipac.firefly.server.ServerContext; +import edu.caltech.ipac.util.DataGroup; +import edu.caltech.ipac.util.VoTableUtil; +import edu.caltech.ipac.util.download.FailedRequestException; +import edu.caltech.ipac.util.download.URLDownload; +import org.apache.commons.lang.NotImplementedException; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + + +/** + * . + * Should handle the LC transformations to get files out of the API result VOtable xml + * + * @author ejoliet + * @see PeriodogramAPIRequest + */ +public class IrsaLightCurveHandler implements LightCurveHandler { + + + /** + * TODO Update with correct API root url + */ + public final String rootApiUrl = "http://irsa.ipac.caltech.edu/periodogram"; + + public File getPeriodogramTable(PeriodogramAPIRequest request) { + + return ipacTableFromAPI(request, RESULT_TABLES_IDX.PERIODOGRAM); + + } + + + /** + * @return peaks table (default: 50 rows) + */ + public File getPeaksTable(PeriodogramAPIRequest request) { + + return ipacTableFromAPI(request, RESULT_TABLES_IDX.PEAKS); + } + + /** + * Return a phase folded curve form original light-curve + * + * @param tbl orginal lc table + * @param period period for phase folding the lc curve + * @return phase folded curve (x2 original input table 0,2 phase) + */ + public File toPhaseFoldedTable(File tbl, float period) { + //get raw lcTable and phase fold on time/period + //for now, return same table. + //TODO change it with the implementation DM-7165 + return tbl; + } + + /** + * Return the API URL to be called to get VOTable from Nexsci, which will contain 2 tables: periodogram and peaks tables. + *
+ * TODO need to be implemented + * + * @param request object to contain the required paramter to make the API call + * @return the URL object + * @throws MalformedURLException + * @see LightCurveProcessor#computePeriodogram(PeriodogramAPIRequest, java.lang.String) + * @see IrsaLightCurveHandler#buildUrl(PeriodogramAPIRequest) + */ + protected URL buildUrl(PeriodogramAPIRequest request) throws MalformedURLException { + //loop other request and append to rootApiUrl + throw new NotImplementedException("Not yet implemented"); + } + + protected File extractTblFrom(File votableResult, RESULT_TABLES_IDX resultTable) { + File resultTblFile = null; + try { + resultTblFile = makeResultTempFile(resultTable); + DataGroup[] dataGroups = VoTableUtil.voToDataGroups(votableResult.getAbsolutePath()); + + IpacTableWriter.save(resultTblFile, dataGroups[resultTable.ordinal()]); + return resultTblFile; + } catch (IOException e) { + e.printStackTrace(); + } + return resultTblFile; + } + + protected File ipacTableFromAPI(PeriodogramAPIRequest request, RESULT_TABLES_IDX resultTable) { + File tempFile = null; + try { + /** + * @see LightCurveProcessor#computePeriodogram(PeriodogramAPIRequest, java.lang.String) + */ + URL url = buildUrl(request); + + File apiResult = apiDownlaod(url); + + tempFile = extractTblFrom(apiResult, resultTable); + + } catch (IOException e) { + e.printStackTrace(); + } catch (FailedRequestException e) { + e.printStackTrace(); + } + return tempFile; + } + + protected File apiDownlaod(URL url) throws IOException, FailedRequestException { + + File apiResultTempFile = makeApiResultTempFile(); + + URLConnection aconn = URLDownload.makeConnection(url); + aconn.setRequestProperty("Accept", "*/*"); + URLDownload.getDataToFile(aconn, apiResultTempFile); //TODO Get from cache + + return apiResultTempFile; + } + + + protected File makeResultTempFile(RESULT_TABLES_IDX resultTable) throws IOException { + String prefix = "error"; + switch (resultTable) { + case PERIODOGRAM: + prefix = "periodogram-"; + break; + case PEAKS: + prefix = "peaks-"; + break; + } + + return File.createTempFile(prefix, ".tbl", ServerContext.getTempWorkDir()); + } + + protected File makeApiResultTempFile() throws IOException { + return File.createTempFile("lc-api-result-", ".xml", ServerContext.getTempWorkDir()); + } +} diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/LightCurveHandler.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/LightCurveHandler.java new file mode 100644 index 0000000000..0e8b7ae25f --- /dev/null +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/LightCurveHandler.java @@ -0,0 +1,45 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ +package edu.caltech.ipac.firefly.server.query.lc; + +import java.io.File; + +/** + * Class should handle the input user parameter to + * call an API to + * get at least the periodogram and peaks table + * out of a raw time changin flux curve + * + * @author ejoliet + * @see PeriodogramAPIRequest + */ +public interface LightCurveHandler { + /** + * TODO check ordinals when API call is implemented + * Represent the tables in votable result from API: 0 for periodogram, 1 for peaks + */ + enum RESULT_TABLES_IDX { + PERIODOGRAM, PEAKS + } + + + /** + * return a periodogram table from a request + * + * @return periodogram (power vs period) file + */ + public File getPeriodogramTable(PeriodogramAPIRequest request); + + /** + * Return the table which contains N peaks, N integer from request object + * + * @return peaks table + * @see PeriodogramAPIRequest#getNumberPeaks() + */ + public File getPeaksTable(PeriodogramAPIRequest request); + + /** + * TODO add extra output parameters getter that might be interesting + */ +} diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/LightCurveProcessor.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/LightCurveProcessor.java new file mode 100644 index 0000000000..68ceded935 --- /dev/null +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/LightCurveProcessor.java @@ -0,0 +1,155 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ +package edu.caltech.ipac.firefly.server.query.lc; + +import edu.caltech.ipac.firefly.core.EndUserException; +import edu.caltech.ipac.firefly.data.TableServerRequest; +import edu.caltech.ipac.firefly.server.ServerContext; +import edu.caltech.ipac.firefly.server.query.DataAccessException; +import edu.caltech.ipac.firefly.server.query.IpacTablePartProcessor; +import edu.caltech.ipac.firefly.server.query.SearchProcessorImpl; +import edu.caltech.ipac.firefly.server.util.Logger; +import edu.caltech.ipac.firefly.server.util.QueryUtil; +import edu.caltech.ipac.util.AppProperties; +import edu.caltech.ipac.util.StringUtils; +import edu.caltech.ipac.util.download.FailedRequestException; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * This class takes care of the LC api call and return result IpacTable Data. + */ + +@SearchProcessorImpl(id = "LightCurveProcessor") + +public class LightCurveProcessor extends IpacTablePartProcessor { + + private static final String PERIODOGRAM_API_URL = AppProperties.getProperty("periodogram.host", "default_periodogram_host_url"); + + private static final Logger.LoggerImpl _log = Logger.getLogger(); + + // API will return votable, depending on the request, return either peaks or periodogram table, which names are predefined here: + private static final String PERIODOGRAM_TABLE_NAME = "periodogram_table.tbl"; + private static final String PEAKS_TABLE_NAME = "peaks_table.tbl"; + + /** + * Class handling the API call and returning LC result table + */ + public LightCurveProcessor() { + + // TODO enable the nadler in constructor when the NexsciHandler is ready + // LightCurveHandler h = new IrsaLightCurveHandler() { + } + + /** + * This method is defined as an abstract in the IpacTablePartProcessor and it is implemented here. + * The TableServerRequest is passed here and processed. Only when the "searchRequest" is set, the request + * is processed. + * + * @return File with statistics on a table + * @throws IOException + * @throws DataAccessException + */ + protected File loadDataFile(TableServerRequest request) throws IOException, DataAccessException { + + PeriodogramAPIRequest req = QueryUtil.assureType(PeriodogramAPIRequest.class, request); + String tblType = req.getParam(PeriodogramAPIRequest.TABLE_NAME); + String tblName = (tblType != null && tblType.equalsIgnoreCase(PeriodogramAPIRequest.RESULT_TABLE)) + ? PERIODOGRAM_TABLE_NAME : PEAKS_TABLE_NAME; + + //In order to get any of those tables, computing the periodogram need to happen: + // Result is a Votable containing 2 tables:periodogram and peaks + File resTable = null; + try { + resTable = computePeriodogram(req, tblName); + } catch (FailedRequestException e) { + e.printStackTrace(); + } + +// if (tblType.equalsIgnoreCase(PeriodogramAPIRequest.PEAKS_TABLE)) { +// +// } + + return resTable; + } + + /** + * From the request, get the file and the algorithm to compute the peridogram by calling external API + * + * @param req request + * @param tblName table name to distinguish them + * @return table file result either peaks por periodogram + * @throws FailedRequestException + */ + public File computePeriodogram(PeriodogramAPIRequest req, String tblName) throws FailedRequestException { + + //Fake call API, parse VOTable result. See for example QueryMOS + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + //TODO this is used with overwritten method. Once API known, remove and use the handler directly + LightCurveHandler h = new IrsaLightCurveHandler() { + + /** + * For testing purposes returned periodogram from here: + * PeriodogramAPIRequest.RESULT_TABLE = "http://web.ipac.caltech.edu/staff/ejoliet/demo/vo-nexsci-result-sample.xml" + * TODO remove after implementing NexsciHandler + * @param req + * @return url api + * @throws MalformedURLException + */ + @Override + protected URL buildUrl(PeriodogramAPIRequest req) throws MalformedURLException { + /** + * For now just download the file from the url from req.getResultTable() + * and stream it out + */ + String SAMPLE_URL = req.getResultTable(); + return new URL(SAMPLE_URL); + } + }; + +// LightCurveHandler h = new IrsaLightCurveHandler(); + if (tblName.equalsIgnoreCase(PERIODOGRAM_TABLE_NAME)) { + return h.getPeriodogramTable(req); + } else if (tblName.equalsIgnoreCase(PEAKS_TABLE_NAME)) { + return h.getPeaksTable(req); + } else { + throw new FailedRequestException("Unable to deal with the request table name " + tblName); + } + } + + private static File makeFileName(PeriodogramAPIRequest req) throws IOException { + return File.createTempFile("lc-result", ".xml", ServerContext.getPermWorkDir()); + } + + private URL createURL(PeriodogramAPIRequest req) throws EndUserException, IOException { + PeriodogramAPIRequest request = (PeriodogramAPIRequest) req.cloneRequest(); + String url = req.getUrl(); + if (url == null || url.length() < 5) { + url = PERIODOGRAM_API_URL; + } + String paramStr = buildParamFrom(request); + if (paramStr.startsWith("&")) { + paramStr = paramStr.substring(1); + } + url += "?" + paramStr; + + return new URL(url); + } + + private String buildParamFrom(PeriodogramAPIRequest request) { + String outputMode = request.getParam(PeriodogramAPIRequest.OUTPUT_MODE); + if (StringUtils.isEmpty(outputMode)) { + outputMode = "VOTable"; + } + return "min_period=0&n_peaks=50"; + } +} diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/PeriodogramAPIRequest.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/PeriodogramAPIRequest.java new file mode 100644 index 0000000000..2066070ac1 --- /dev/null +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/PeriodogramAPIRequest.java @@ -0,0 +1,90 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ +package edu.caltech.ipac.firefly.server.query.lc; + +import edu.caltech.ipac.firefly.data.TableServerRequest; + +/** + * Request class to transport and gets the parameters related to LC + * @author ejoliet + */ +public class PeriodogramAPIRequest extends TableServerRequest { + + public final static String OUTPUT_MODE = "output_`mode"; + + public final static String TABLE_NAME = "table_name"; + // API url + public final static String URL = "url"; + + public final static String LC_FILE = "original_table"; + // Periodogram table + public static final String RESULT_TABLE = "result_table"; + + public static final String FOLDED_TABLE = "folded_table"; + public final static String PERIOD_IN_DAYS = "period_days"; + public final static String TIME_COLUMN_NAME = "time_col_name"; + + // Input table raw LC flux vs time, should at least contain flux and time! + public static final String TIME_DEPENDENT_TABLE = "input_table"; + + public final static String ALGO_NAME = "algo_name"; + + // number of peaks (default = 50) + public final static String NUMBER_PEAKS = "n_peaks"; + public final static String MINIMUM_PERIOD = "min_period"; + public final static String MAXIMUM_PERIOD = "max_period"; + + public final static String STEP_METHOD_NAME = "step_method"; + public final static String STEP_SIZE = "step_size"; + + + public PeriodogramAPIRequest() { + super(PeriodogramAPIRequest.class.getSimpleName()); + } + + public String getUrl() { + return getParam(URL); + } + + /** + * Period value + * + * @return period in days + */ + public float getPeriod() { + return getFloatParam(PERIOD_IN_DAYS); + } + + /** + * @return lc original table file path + */ + public String getLcSource() { + return getParam(LC_FILE); + } + + /** + * Usually 'mjd', but who knows... + * + * @return name of the time column + */ + public String getTimeColName() { + return getParam(TIME_COLUMN_NAME); + } + + public float getMinimumPeriod() { + return getFloatParam(MINIMUM_PERIOD); + } + + public float getMaximumPeriod() { + return getFloatParam(MAXIMUM_PERIOD); + } + + public int getNumberPeaks() { + return getIntParam(NUMBER_PEAKS); + } + + public String getResultTable() { + return getParam(RESULT_TABLE); + } +} diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/PhaseFoldedCurveProcessor.java b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/PhaseFoldedCurveProcessor.java new file mode 100644 index 0000000000..11c03cc5cf --- /dev/null +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/query/lc/PhaseFoldedCurveProcessor.java @@ -0,0 +1,123 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ +package edu.caltech.ipac.firefly.server.query.lc; + +import edu.caltech.ipac.firefly.data.TableServerRequest; +import edu.caltech.ipac.firefly.server.ServerContext; +import edu.caltech.ipac.firefly.server.query.DataAccessException; +import edu.caltech.ipac.firefly.server.query.IpacTablePartProcessor; +import edu.caltech.ipac.firefly.server.query.SearchProcessorImpl; +import edu.caltech.ipac.firefly.server.util.Logger; +import edu.caltech.ipac.firefly.server.util.QueryUtil; +import edu.caltech.ipac.util.FileUtil; +import edu.caltech.ipac.util.StringUtils; +import edu.caltech.ipac.util.download.URLDownload; + +import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Created by zhang on 10/14/15. + * This class calculates the statistics of a IpacTable Data. + */ + +@SearchProcessorImpl(id = "PhaseFoldedProcessor") + +public class PhaseFoldedCurveProcessor extends IpacTablePartProcessor { + + private static final Logger.LoggerImpl _log = Logger.getLogger(); + + private static final String FOLDED_TABLE_NAME = "folded_table.tbl"; + private final IrsaLightCurveHandler irsaLcHandler; + + /** + * Class handling the API call and returning LC result table + */ + public PhaseFoldedCurveProcessor() { + irsaLcHandler = new IrsaLightCurveHandler(); + } + + /** + * This method is defined as an abstract in the IpacTablePartProcessor and it is implemented here. + * The TableServerRequest is passed here and processed. Only when the "searchRequest" is set, the request + * is processed. + * + * @return File with statistics on a table + * @throws IOException + * @throws DataAccessException + */ + protected File loadDataFile(TableServerRequest request) throws IOException, DataAccessException { + + PeriodogramAPIRequest req = QueryUtil.assureType(PeriodogramAPIRequest.class, request); + String tblType = req.getParam(PeriodogramAPIRequest.TABLE_NAME); + String tblName = (tblType != null && tblType.equalsIgnoreCase(PeriodogramAPIRequest.FOLDED_TABLE)) + ? FOLDED_TABLE_NAME : "ERROR.tbl"; + + float period = req.getFloatParam("period"); + String lcTable = req.getParam("original_table"); + + + //Votable containing 2 tables:periodogram and peaks + File resTable = null; + try { + resTable = phaseFoldedTable(req); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + return resTable; + } + + protected File phaseFoldedTable(PeriodogramAPIRequest req) throws InterruptedException { + //Fake building phase folded + Thread.sleep(5000); + + + File phaseFoldedTable = irsaLcHandler.toPhaseFoldedTable(getSourceFile(req.getLcSource(), req), req.getPeriod()); + + + return phaseFoldedTable; + } + + private File getSourceFile(String source, TableServerRequest request) { + File inf = null; + try { + URL url = makeUrl(source); + if (url == null) { + inf = ServerContext.convertToFile(source); + } else { + HttpURLConnection conn = (HttpURLConnection) URLDownload.makeConnection(url); + int rcode = conn.getResponseCode(); + if (rcode >= 200 && rcode < 400) { + String sfname = URLDownload.getSugestedFileName(conn); + if (sfname == null) { + sfname = url.getPath(); + } + String ext = sfname == null ? null : FileUtil.getExtension(sfname); + ext = StringUtils.isEmpty(ext) ? ".ul" : "." + ext; + inf = createFile(request, ext); + URLDownload.getDataToFile(conn, inf, null, false, true, true, Long.MAX_VALUE); + } + } + } catch (Exception ex) { + inf = null; + } + if (inf != null && inf.canRead()) { + return inf; + } + + return null; + } + + private URL makeUrl(String source) { + try { + return new URL(source); + } catch (MalformedURLException e) { + return null; + } + } +} diff --git a/src/firefly/js/ui/TestQueriesPanel.jsx b/src/firefly/js/ui/TestQueriesPanel.jsx index 238e8ad178..8162f488ab 100644 --- a/src/firefly/js/ui/TestQueriesPanel.jsx +++ b/src/firefly/js/ui/TestQueriesPanel.jsx @@ -36,7 +36,7 @@ import {RadioGroupInputField} from './RadioGroupInputField.jsx'; import {ListBoxInputField} from './ListBoxInputField.jsx'; import {FileUpload} from '../ui/FileUpload.jsx'; import {parseWorldPt} from '../visualize/Point.js'; -import {makeTblRequest, makeIrsaCatalogRequest} from '../tables/TableUtil.js'; +import {makeTblRequest, makeFileRequest, makeIrsaCatalogRequest} from '../tables/TableUtil.js'; import {dispatchAddImages,getAViewFromMultiView,getMultiViewRoot} from '../visualize/MultiViewCntlr.js'; import WebPlotRequest from '../visualize/WebPlotRequest.js'; import {dispatchPlotImage} from '../visualize/ImagePlotCntlr.js'; @@ -44,10 +44,10 @@ import {getDS9Region} from '../rpc/PlotServicesJson.js'; import {Region} from '../visualize/region/Region.js'; import {RegionFactory} from '../visualize/region/RegionFactory.js'; -const options= [ - {label: 'AllWISE Source Catalog', value:'wise_allwise_p3as_psd', proj:'WISE'}, - {label: '2MASS All-Sky Point Source Catalog (PSC)', value:'fp_psc', proj:'2MASS'}, - {label: 'IRAS Point Source Catalog v2.1 (PSC)', value:'iraspsc', proj: 'IRAS'} +const options = [ + {label: 'AllWISE Source Catalog', value: 'wise_allwise_p3as_psd', proj: 'WISE'}, + {label: '2MASS All-Sky Point Source Catalog (PSC)', value: 'fp_psc', proj: '2MASS'}, + {label: 'IRAS Point Source Catalog v2.1 (PSC)', value: 'iraspsc', proj: 'IRAS'} ]; @@ -59,18 +59,18 @@ export class TestQueriesPanel extends Component { componentWillUnmount() { if (this.removeListener) this.removeListener(); - this.iAmMounted= false; + this.iAmMounted = false; } componentDidMount() { - this.iAmMounted= true; - this.removeListener= FieldGroupUtils.bindToStore('TEST_CAT_PANEL', (fields) => { + this.iAmMounted = true; + this.removeListener = FieldGroupUtils.bindToStore('TEST_CAT_PANEL', (fields) => { if (this.iAmMounted) this.setState(fields); }); } render() { - const fields= this.state; + const fields = this.state; return (