/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.autoplot.jythonsupport; import java.util.Map; import java.util.logging.Logger; import org.python.core.Py; import org.python.core.PyObject; import org.python.core.PyString; import org.das2.qds.QDataSet; import org.das2.datum.Units; import org.das2.qds.ops.Ops; import org.python.core.PyDictionary; import org.python.core.PyFloat; import org.python.core.PyInteger; import org.python.core.PyJavaInstance; /** * new implementation of the dataset command allows for keywords in the * Jython environment. *<blockquote><pre><small>{@code * dataset( [1,2,3,4,3], title='My Data' ) *}</small></pre></blockquote> * @see http://autoplot.org/help.datasetCommand * @author jbf */ public class DatasetCommand extends PyObject { private static final Logger logger= org.das2.util.LoggerManager.getLogger("jython.commands.dataset"); public static final PyString __doc__ = new PyString("<html><H2>dataset(ds,[named parameters])</H2>" + "dataset creates datasets from arrays and adds metadata.\n" + "See http://autoplot.org/help.datasetCommand<br>\n" + "<br><b>named parameters:</b>\n" + "<table>" + "<tr><td>title </td><td>title for the data, which could be used above a plot.</td></tr>\n" + "<tr><td>label </td><td>label for the data, which could be used as an axis label.</td></tr>\n" + "<tr><td>name </td><td>name for the data, which should be a legal Jython variable name.</td></tr>\n" + "<tr><td>units </td><td>units for the data, which string representing the units of the data.</td></tr>\n" + "<tr><td>resetUnits </td><td>assert units for the data, with no attempt to perform the units conversion.</td></tr>\n" + "<tr><td>validMin validMax</td><td>range of valid values for the data.</td></tr>\n" + "<tr><td>typicalMin typicalMax</td><td>typical range dataset, used for suggesting axis ranges.</td></tr>\n" + "<tr><td>scaleType</td><td>'log' or 'linear'</td></tr>\n" + "<tr><td>averageType</td><td>'linear', 'geometric', 'mod360', 'mod24', 'modpi', 'modtau'</td></tr>\n" + "<tr><td>format</td><td>format specifier, like %d or %.2f</td></tr>\n" + "<tr><td>cadence</td><td>nominal cadence, like 60s or 100Hz. Note this goes with the independent parameter (timetags).</td></tr>\n" + "</table></html>"); private static QDataSet datasetValue( PyObject arg0 ) { Object o = arg0.__tojava__(QDataSet.class); if (o == null || o == Py.NoConversion) { return JythonOps.dataset(arg0); } else { QDataSet ds = (QDataSet) o; if (ds.rank() == 0) { // QDataSet library handles coerce logic. return ds; } else { return ds; } } } private static boolean booleanValue( PyObject arg0 ) { if ( arg0.isNumberType() ) { return arg0.__nonzero__(); } else { String s= String.valueOf(arg0); return s.equals("True") || s.equals("T") || s.equals("1"); } } private static Number numberValue( PyObject arg0 ) { if ( arg0 instanceof PyInteger ) { return ((PyInteger)arg0).getValue(); } else if ( arg0 instanceof PyFloat ) { return ((PyFloat)arg0).getValue(); } else if ( arg0 instanceof PyString ) { return Double.parseDouble( String.valueOf(arg0) ); } else { return arg0.__float__().getValue(); } } /** * implement the python call. * @param args the "rightmost" elements are the keyword values. * @param keywords the names for the keywords. * @return Py.None */ @Override public PyObject __call__(PyObject[] args, String[] keywords) { FunctionSupport fs= new FunctionSupport( "dataset", new String[] { "ds", "ds1", "ds2", "ds3", "ds4", "title", "label", "name", "units", "format", "cadence", "resetUnits", "fillValue", "validMin", "validMax", "typicalMin", "typicalMax", "scaleType", "averageType", "renderType", "bins1", "bins0", "cacheTag", "userProperties", "deltaPlus", "deltaMinus", "binPlus", "binMinus", "binMin", "binMax", }, new PyObject[] { Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, Py.None, } ); fs.args( args, keywords ); int nparm= args.length - keywords.length; QDataSet result; Units units=null; boolean resetUnits=false; for ( int i=nparm; i<args.length; i++ ) { //HERE nargs String kw= keywords[i-nparm]; PyObject val= args[i]; if ( kw.equals("units") || kw.equals("resetUnits") ) { if ( kw.equals("resetUnits") ) resetUnits=true; if ( val.__tojava__(Units.class)!= Py.NoConversion ) { units= (Units)val.__tojava__(Units.class); } else { String svalue= val.toString(); units= Units.lookupUnits(svalue); } } } switch (nparm) { case 0: throw new IllegalArgumentException("dataset needs at least one argument"); case 1: if ( units!=null ) { if ( resetUnits && args[0] instanceof PyQDataSet ) { QDataSet ds= (QDataSet)args[0].__tojava__(QDataSet.class); ds= Ops.putProperty( ds, QDataSet.UNITS,null ); result= Ops.dataset( ds, units ); } else { result= JythonOps.dataset( args[0], units ); } } else { result= JythonOps.dataset( args[0] ); } break; case 2: { if ( args[1] instanceof PyJavaInstance ) { // legacy use allowed the second argument to be a units object. PyJavaInstance pji= (PyJavaInstance)args[1]; Object o= pji.__tojava__( Units.class ); if ( o!=Py.NoConversion ) { logger.info("legacy script uses second argument for units, use units=... instead"); result= Ops.dataset( args[0].__tojava__(Object.class), (Units)o ); break; } } result= JythonOps.dataset( args[1] ); QDataSet xds= JythonOps.dataset( args[0] ); result= Ops.link( xds, result ); break; } case 3: { result= JythonOps.dataset( args[2] ); QDataSet xds= JythonOps.dataset( args[0] ); QDataSet yds= JythonOps.dataset( args[1] ); result= Ops.link( xds, yds, result ); break; } case 4: { result= JythonOps.dataset( args[3] ); QDataSet ds0= JythonOps.dataset( args[0] ); QDataSet ds1= JythonOps.dataset( args[1] ); QDataSet ds2= JythonOps.dataset( args[2] ); result= Ops.link( ds0, ds1, ds2, result ); break; } case 5: { result= JythonOps.dataset( args[4] ); QDataSet ds0= JythonOps.dataset( args[0] ); QDataSet ds1= JythonOps.dataset( args[1] ); QDataSet ds2= JythonOps.dataset( args[2] ); QDataSet ds3= JythonOps.dataset( args[3] ); result= Ops.link( ds0, ds1, ds2, ds3, result ); break; } default: throw new IllegalArgumentException("dataset needs between one and four parameters."); } for ( int i=nparm; i<args.length; i++ ) { //HERE nargs String kw= keywords[i-nparm]; PyObject val= args[i]; String sval= (String) val.__str__().__tojava__(String.class); switch ( kw ) { case "description": case "title": case "label": case "name": case "format": result= Ops.putProperty( result, kw.toUpperCase(), sval ); break; case "units": case "resetUnits": if ( val.__tojava__(Units.class)!= Py.NoConversion ) { result= Ops.putProperty( result, QDataSet.UNITS, val.__tojava__(Units.class) ); } else { result= Ops.putProperty( result, QDataSet.UNITS, sval ); } break; case "validMin": result= Ops.putProperty( result, QDataSet.VALID_MIN, numberValue(val) ); break; case "validMax": result= Ops.putProperty( result, QDataSet.VALID_MAX, numberValue(val) ); break; case "typicalMin": result= Ops.putProperty( result, QDataSet.TYPICAL_MIN, numberValue(val) ); break; case "typicalMax": result= Ops.putProperty( result, QDataSet.TYPICAL_MAX, numberValue(val) ); break; case "fillValue": result= Ops.putProperty( result, QDataSet.FILL_VALUE, numberValue(val) ); break; case "scaleType": result= Ops.putProperty( result, QDataSet.SCALE_TYPE, sval ); break; case "averageType": result= Ops.putProperty( result, QDataSet.AVERAGE_TYPE, sval ); break; case "cadence": result= Ops.putProperty( result, QDataSet.CADENCE, sval ); break; case "renderType": result= Ops.putProperty( result, QDataSet.RENDER_TYPE, sval ); break; case "bins1": result= Ops.putProperty( result, QDataSet.BINS_1, sval ); break; case "bins0": result= Ops.putProperty( result, QDataSet.BINS_0, sval ); break; case "cacheTag": // 2019-02-03 @ 1s result= Ops.putProperty( result, QDataSet.CACHE_TAG, sval ); break; case "userProperties": if ( val instanceof PyDictionary ) { Map m= JythonUtil.pyDictionaryToMap((PyDictionary)val); result= Ops.putProperty( result, QDataSet.USER_PROPERTIES, m ); } else { result= Ops.putProperty( result, QDataSet.USER_PROPERTIES, val ); } break; case "deltaPlus": result= Ops.putProperty( result, QDataSet.DELTA_PLUS, JythonOps.dataset( val ) ); break; case "deltaMinus": result= Ops.putProperty( result, QDataSet.DELTA_MINUS, JythonOps.dataset( val ) ); break; case "binPlus": result= Ops.putProperty( result, QDataSet.BIN_PLUS, JythonOps.dataset( val ) ); break; case "binMinus": result= Ops.putProperty( result, QDataSet.BIN_MINUS, JythonOps.dataset( val ) ); break; case "binMin": result= Ops.putProperty( result, QDataSet.BIN_MIN, JythonOps.dataset( val ) ); break; case "binMax": result= Ops.putProperty( result, QDataSet.BIN_MAX, JythonOps.dataset( val ) ); break; default: throw new IllegalArgumentException("bad keyword"); } } return new PyQDataSet(result); } }