#!/usr/bin/python
import sys
import getopt
import os
import xml.etree.ElementTree as ET
import logging
logging.basicConfig(filename='example.log',format = "%(levelname) -10s %(module)s:%(lineno)s %(message)s", level = logging.DEBUG)
class FlowItem:
def __init__(self, stagename, input_entries, output_entries):
self.stagename = stagename
#These are lists of dicts. The object tag signifies the actual in the file system and is used for mapping
#the dataflow, whereas the port is used in binding to the tool
self.input_entries = input_entries
self.output_entries = output_entries
def validate(self):
#Checking if output names and input names do not occur more than once
#TODO
return True
def portpresent(self, isInput, portname):
if isInput:
for input_entry in self.input_entries:
if input_entry["port"] == portname:
return True
return False
else:
for output_entry in self.output_entries:
if output_entry["port"] == portname:
return True
return False
class ToolItem:
def __init__(self, toolname, toolpath, input_entries, output_entries):
self.toolname = toolname
self.input_entries = input_entries
self.output_entries = output_entries
self.toolpath = toolpath
def validate(self):
#TODO
return True;
def parameterpresent(self, isInput, parametername):
if isInput:
for input_entry in self.input_entries:
if input_entry["name"] == parametername:
return True
return False
else:
for output_entry in self.output_entries:
if output_entry["name"] == parametername:
return True
return False
class BindingItem:
def __init__(self, flowname, toolname, input_entries, output_entries):
self.toolname = toolname
self.flowname = flowname
self.input_entries = input_entries
self.output_entries = output_entries
def validate(self):
#TODO
return True;
class Workflow:
def __init__(self, flow_xml_filename, binding_xml_name, tool_xml_name):
self.error_list = []
self.exemptflownames = ["begin", "end"]
self.flows_list = self.parseflow(flow_xml_filename)
self.tools_list = self.parsetool(tool_xml_name)
self.binding_list = self.parseBinding(binding_xml_name)
self.createaccessmaps()
def printerrors(self):
print "======================Error List=============================="
logging.debug("======================Error List==============================")
for error_item in self.error_list:
print error_item
logging.debug(error_item)
#Creating easy lookups for binding objects
def createaccessmaps(self):
#Bindings
binding_tool_map = {}
binding_flow_map = {}
for binding in self.binding_list:
binding_tool_map[binding.toolname] = binding
binding_flow_map[binding.flowname] = binding
print binding_flow_map
self.binding_tool_map = binding_tool_map
self.binding_flow_map = binding_flow_map
#Flows
flow_map = {}
for flow in self.flows_list:
flow_map[flow.stagename] = flow
self.flow_map = flow_map
#tools
tool_map = {}
for tool in self.tools_list:
tool_map[tool.toolname] = tool
self.tool_map = tool_map
#Parsing the Flow.xml
def parseflow(self, flow_xml_filename):
flows_list = []
tree = ET.parse(flow_xml_filename)
root = tree.getroot()
for child in root:
#Looking for only actions
if(child.tag == "action"):
print child.tag + "\t" + str(child.attrib)
#Constructing parameters for flow
stage_name = child.attrib["name"]
input_entries = []
output_entries = []
#we can now create input and output for each flow object
for dataflow in child:
print dataflow.tag
if dataflow.tag == "input":
input_entries.append(dataflow.attrib)
print dataflow.attrib
if dataflow.tag == "output":
output_entries.append(dataflow.attrib)
print dataflow.attrib
flow_item = FlowItem(stage_name, input_entries, output_entries)
flows_list.append(flow_item)
return flows_list
#Parsing the Tool.xml
def parsetool(self, tool_xml_name):
tools_list = []
tree = ET.parse(tool_xml_name)
root = tree.getroot()
#First Getting All Tool Paths
tool_path_present = {}
for child in root:
print child.tag + "\t" + str(child.attrib)
if(child.tag == "pathSet"):
for toolPathItem in child:
if toolPathItem.tag == "toolPath":
toolname = toolPathItem.attrib["tool"]
if toolname in tool_path_present:
print "Tool Redefinition";
self.error_list.append("Tool Path Redefinition in Tool.xml: " + toolname)
else:
tool_path_present[toolname] = child.attrib["base"] + "/" + toolPathItem.attrib["path"]
print tool_path_present
#Getting actual tool input and outputs
for child in root:
if(child.tag == "tool"):
toolname = child.attrib["name"]
tool_input = []
tool_output = []
for tool_parameters in child:
if tool_parameters.tag == "require":
tool_input.append(tool_parameters.attrib)
if tool_parameters.tag == "produce":
tool_output.append(tool_parameters.attrib)
toolpath = ""
#Checking if tool has path
if toolname in tool_path_present:
toolpath = tool_path_present[toolname]
else:
self.error_list.append("Missing path for tool: " + toolname)
tool = ToolItem(toolname, toolpath, tool_input, tool_output)
tools_list.append(tool)
return tools_list
#Parsing the binding xml file
def parseBinding(self, binding_xml_name):
binding_list = []
tree = ET.parse(binding_xml_name)
root = tree.getroot()
for child in root :
print child.tag + "\t" + str(child.attrib)
if child.tag == "bind":
flow_name = child.attrib["action"]
if flow_name in self.exemptflownames:
continue
tool_name = child.attrib["tool"]
input_list = []
output_list = []
for binding_parameters in child:
if binding_parameters.tag == "inputAsRequirement":
input_list.append(binding_parameters.attrib)
if binding_parameters.tag == "productionToOutput":
output_list.append(binding_parameters.attrib)
bindingitem = BindingItem(flow_name, tool_name, input_list, output_list)
binding_list.append(bindingitem)
return binding_list
@staticmethod
def validate_flow_to_binding(flow_item, binding_item):
output_errors = []
#Checking the input entries
binding_input_list = binding_item.input_entries
binding_output_list = binding_item.output_entries
#Checking Binding Import
for binding_input in binding_input_list:
binding_port_value = binding_input["port"]
#Check if this is present
if flow_item.portpresent(True, binding_port_value):
print "PORT FOUND IN INPUT FLOW " + flow_item.stagename
else:
output_errors.append("Port in binding not found in flow: " + flow_item.stagename)
#Checking Binding Output
print "CHECKING OUTPUT"
for binding_output in binding_output_list:
binding_port_value = binding_output["port"]
#Check if this is present
if flow_item.portpresent(False, binding_port_value):
print "PORT FOUND IN OUTPUT FLOW " + flow_item.stagename
else:
output_errors.append("Port in binding not found in flow: " + flow_item.stagename)
return output_errors
@staticmethod
def validate_binding_to_tool(binding_item, tool_item):
output_errors = []
#Checking from binding to tool
binding_input_list = binding_item.input_entries
binding_output_list = binding_item.output_entries
for binding_input in binding_input_list:
binding_requirement_name = binding_input["requirement"]
#Check if this is present
if tool_item.parameterpresent(True, binding_requirement_name):
print "INPUT FOUND IN INPUT Tool " + tool_item.toolname + " " + binding_requirement_name
else:
output_errors.append("Tool parameter in binding: " + binding_requirement_name + " not found in tool: " + tool_item.toolname)
#Checking from tool to binding
#TODO
return output_errors
#Validating the Tools, requires that the acccess maps are created
def validate(self):
#First validate Flow
for flow_item in self.flows_list:
if(flow_item.validate() == False):
self.error_list.append("Flow Validation Error: " + flow_item.stagename)
#Validating Tool
for tool_item in self.tools_list:
if(tool_item.validate() == False):
self.error_list.append("Tool Validation Error: " + tool_item.toolname)
#Validating no cycles in data flow
#We'll punt on this for a while, we'll only check that it doesn't go from input to output on the same node
#Validating the connection from tool to flow through binding
for flow in self.flows_list:
flowname = flow.stagename
if flowname in self.exemptflownames:
continue
if flowname in self.binding_flow_map:
binding_item = self.binding_flow_map[flowname]
self.error_list.extend(Workflow.validate_flow_to_binding(flow, binding_item))
#Validate Binding to Tool
tool_name = binding_item.toolname
if tool_name in self.tool_map:
self.error_list.extend(Workflow.validate_binding_to_tool(binding_item, self.tool_map[tool_name]))
else:
self.error_list.append("Tool not found for flow: " + flowname + " should be named: " + tool_name)
else:
self.error_list.append("Binding not found for flow: " + flowname)
def usage():
print " "
def main():
usage()
workflow = Workflow(sys.argv[1], sys.argv[2], sys.argv[3])
workflow.validate() == True
workflow.printerrors();
if __name__ == "__main__":
main()