001/*
002 * Copyright (c) 2017 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2017-08-24 12:45:52 -0700 (Thu, 24 Aug 2017) $' 
007 * '$Revision: 1388 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029package org.kepler.webview.server.handler;
030
031import java.util.HashMap;
032import java.util.Map;
033import java.util.Set;
034
035import org.kepler.util.WorkflowRun;
036import org.kepler.webview.server.WebViewServer;
037
038import io.vertx.core.MultiMap;
039import io.vertx.core.json.JsonArray;
040import io.vertx.core.json.JsonObject;
041import io.vertx.ext.web.RoutingContext;
042import ptolemy.data.BooleanToken;
043import ptolemy.data.DoubleToken;
044import ptolemy.data.StringToken;
045import ptolemy.data.Token;
046import ptolemy.data.expr.ASTPtRootNode;
047import ptolemy.data.expr.ParseTreeEvaluator;
048import ptolemy.data.expr.ParserScope;
049import ptolemy.data.expr.PtParser;
050import ptolemy.data.expr.UndefinedConstantOrIdentifierException;
051import ptolemy.data.type.Type;
052import ptolemy.graph.InequalityTerm;
053import ptolemy.kernel.util.IllegalActionException;
054
055/** Handler to get information about workflow runs.
056 * 
057 * @author Daniel Crawl
058 * @version $Id: RunsHandler.java 1388 2017-08-24 19:45:52Z crawl $
059 * 
060 */
061public class RunsHandler extends ProvenanceHandler {
062
063    public RunsHandler(WebViewServer server) {
064        super(server);
065    }
066
067    @Override
068    public void handle(RoutingContext context) {
069        
070        /*
071        System.out.println("in runs handler");
072        for(Entry<String, String> e : context.request().params()) {            
073            System.out.println(e.getKey() + " -> " + e.getValue());
074        }
075        */
076
077        _server.getVertx().<JsonObject>executeBlocking(future -> {
078            try {
079                JsonArray jsonArray = new JsonArray();
080                
081                MultiMap params = context.request().params();
082                
083                String name = params.get("name");
084                String parameters = params.get("parameters");
085
086                PtParser parser;
087                ASTPtRootNode parseTree = null;
088                ParseTreeEvaluator parseTreeEvaluator = null;   
089                ParametersScope scope = null;
090
091                // if evaluating parameters expression, create parser objects
092                if(parameters != null) {
093                    parser = new PtParser();
094                    parseTree = parser.generateParseTree(parameters);
095                    parseTreeEvaluator = new ParseTreeEvaluator();
096                    scope = new ParametersScope();
097                }
098                
099                for(WorkflowRun run : _queryable.getWorkflowRunsForUser(context.user().principal().getString("username"))) {
100
101                    // if search for name, check if run name matches
102                    // TODO perform this in queryable
103                    if(name != null && !name.equals(run.getWorkflowName())) {
104                        continue;
105                    }
106                    
107                    if(parameters != null) {
108                        
109                        // add the run's parameter names and values to the parser's scope
110                        Map<String,String> map = _queryable.getParameterNameValuesOfSpecificTypeForExecution(run.getExecId());                        
111                        scope.setParametersValues(map);
112
113                        // attempt to evaluate the expression.
114                        Token result = null;
115                        try {
116                            result = parseTreeEvaluator.evaluateParseTree(parseTree, scope);
117                        } catch(UndefinedConstantOrIdentifierException e) {
118                            // ignore undefined identifiers since the workflow may not
119                            // have a parameter with that name.
120                            continue;
121                        }
122                        
123                        // make sure result is a boolean
124                        if(!(result instanceof BooleanToken)) {
125                           throw new Exception("Parameter search expression must evaluate to true or false."); 
126                        }
127                        
128                        // if expression evaluates to false, do not add this run to the results.
129                        if(!((BooleanToken)result).booleanValue()) {
130                            continue;
131                        }
132                    }
133                    
134                    jsonArray.add(new JsonObject().put("id", run.getExecLSID().toString())
135                       .put("start", run.getStartTimeISO8601())
136                       .put("status", run.getType())
137                       .put("workflowName", run.getWorkflowName()));
138                }
139                future.complete(new JsonObject().put("runs", jsonArray));
140
141            } catch(Exception e) {
142                future.fail(e);
143            }
144        }, false, result -> {
145            if(result.succeeded()) {
146                _sendResponseWithSuccessJson(context.request(), result.result());
147            } else {
148                _sendResponseWithError(context.request(), "Could not get runs: " + result.cause().getMessage());
149            }
150        });   
151    }
152
153    /** A scope for expressions that references parameters from a workflow run. */
154    private static class ParametersScope implements ParserScope {
155
156        /** Get the value from an identifier in the expression. */
157        @Override
158        public Token get(String name) throws IllegalActionException {
159            
160            // see if the identifier was a parameter in the workflow run
161            String value = _map.get(name);
162            if(value != null) {
163                //System.out.println("found " + name + " = " + value);
164                // first try casting value as a double
165                try {
166                    return new DoubleToken(value);
167                } catch(Throwable t) {
168                    // default to a string
169                    return new StringToken(value);
170                }
171            }
172            
173            return null;
174        }
175
176        /** Set the parameters from the workflow run. */
177        public void setParametersValues(Map<String, String> map) {
178            _map.clear();
179            for(Map.Entry<String, String> entry : map.entrySet()) {
180                // add the parameter name and value.
181                // NOTE: remove the first character of the name since it begins with "."
182                _map.put(entry.getKey().substring(1), entry.getValue());
183            }
184        }
185
186        @Override
187        public Type getType(String name) throws IllegalActionException {
188            // TODO Auto-generated method stub
189            return null;
190        }
191
192        @Override
193        public InequalityTerm getTypeTerm(String name) throws IllegalActionException {
194            // TODO Auto-generated method stub
195            return null;
196        }
197
198        @Override
199        public Set<?> identifierSet() throws IllegalActionException {
200            // TODO Auto-generated method stub
201            return null;
202        }
203        
204        private Map<String, String> _map = new HashMap<>();
205    }
206    
207}