Mercurial > repos > uga-galaxy-group > webservice_toolsuite_v1_1
comparison WebServiceExtensionsV1.1/WebServiceToolWorkflow_REST_SOAP/generateClient.py @ 0:049760c677de default tip
Galaxy WSExtensions added successfully
| author | uga-galaxy-group |
|---|---|
| date | Tue, 05 Jul 2011 19:34:18 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:049760c677de |
|---|---|
| 1 ''' | |
| 2 @author Chaitanya Guttula, Sumedha Ganjoo | |
| 3 @see LICENSE (MIT style license file). | |
| 4 ''' | |
| 5 | |
| 6 import warnings | |
| 7 | |
| 8 with warnings.catch_warnings(): | |
| 9 warnings.simplefilter("ignore") | |
| 10 import platform | |
| 11 | |
| 12 from jpype._jpackage import JPackage | |
| 13 from jpype import * | |
| 14 import os.path | |
| 15 import sys | |
| 16 import string | |
| 17 from edit_tool_conf import * | |
| 18 from clientGenerator.msHandler import * | |
| 19 from clientGenerator.creatorEngineComplex import * | |
| 20 from clientGenerator.wsdl2path import * | |
| 21 from clientGenerator.paramConverter import * | |
| 22 | |
| 23 | |
| 24 class ClientGenerator(object): | |
| 25 | |
| 26 #instantiate a client for invocation of the selected method of a Web service in workflows | |
| 27 def __init__(self,webservice,operation,outputfile,servicetype): | |
| 28 with warnings.catch_warnings(): | |
| 29 warnings.simplefilter("ignore") | |
| 30 self.operation = operation | |
| 31 self.webservice = webservice | |
| 32 #self.inputs = outputfile | |
| 33 self.outputfile = outputfile | |
| 34 self.galaxyhome=os.environ.get('GALAXY_HOME') | |
| 35 self.clientfile = '' | |
| 36 self.servicetype = servicetype | |
| 37 if self.servicetype == 'SOAP': | |
| 38 wLoad=wsdlLoader() | |
| 39 sys.path.append(self.galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/clientGenerator') | |
| 40 os.chdir(self.galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/clientGenerator') | |
| 41 | |
| 42 a = str(self.webservice).split('/') | |
| 43 wsdlnamelist = a[len(a)-1].split(".") | |
| 44 if len(wsdlnamelist)==1: | |
| 45 wsdlnamelist=a[len(a)-1].split('?') | |
| 46 print wsdlnamelist | |
| 47 | |
| 48 foldername=wsdlnamelist[0] | |
| 49 | |
| 50 path =self.galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/clientGenerator/'+foldername | |
| 51 | |
| 52 #creates the client stubs | |
| 53 self.clientfile = wLoad.wsdlUrl2path(str(self.webservice),foldername) | |
| 54 | |
| 55 print 'Client file : ',self.clientfile | |
| 56 self.paramelement = None | |
| 57 | |
| 58 #replace '__tilda__' with '~' | |
| 59 #if(url.find('__tilda__')>-1): | |
| 60 # ulist = url.split('__tilda__') | |
| 61 #url = '~'.join(ulist) | |
| 62 #self.url = url | |
| 63 | |
| 64 ''' | |
| 65 Checks if a particular paramter is reuired or not. This is used only for soap web services. | |
| 66 ''' | |
| 67 def isRequired(self,param): | |
| 68 #j=0 | |
| 69 #for param in inputl: | |
| 70 cc = ClientCreator() | |
| 71 self.msinstance = cc.opname2inputClassOb(self.operation,self.clientfile) | |
| 72 test = MessageHandler() | |
| 73 required = False | |
| 74 if (param.find('|$|')>-1): | |
| 75 plist = param.split('|') | |
| 76 #root = test.getParameter(self.msinstance(),plist[0]) | |
| 77 k = 0 | |
| 78 iparam = '' | |
| 79 for p in plist: | |
| 80 if(k<len(plist)-3): | |
| 81 if(k==0): | |
| 82 iparam = iparam+(p) | |
| 83 else: | |
| 84 iparam = iparam+'|'+p | |
| 85 k=k+1 | |
| 86 paramelement = test.getParameter(self.msinstance(),iparam) | |
| 87 else: | |
| 88 paramelement = test.getParameter(self.msinstance(),param) | |
| 89 self.paramelement = paramelement | |
| 90 if (param.find('|')>-1): | |
| 91 plist = param.split('|') | |
| 92 root = test.getParameter(self.msinstance(),plist[0]) | |
| 93 if not getattr(root,'nillable') and not getattr(paramelement,'nillable'): | |
| 94 required = True | |
| 95 return True | |
| 96 elif getattr(root,'nillable') or (not getattr(root,'nillable') and getattr(paramelement,'nillable')): | |
| 97 required = False | |
| 98 return False | |
| 99 else: | |
| 100 if not getattr(paramelement,'nillable'): | |
| 101 required = True | |
| 102 return True | |
| 103 elif getattr(paramelement,'nillable'): | |
| 104 required = False | |
| 105 return False | |
| 106 return required | |
| 107 | |
| 108 | |
| 109 #replace '**' with ' ' | |
| 110 def formatString(self,string): | |
| 111 l = string.split(' ') | |
| 112 return '**'.join(l) | |
| 113 | |
| 114 ''' | |
| 115 Checks if the tool(operation) is already addded to Galaxy. It opens the tool_conf.xml file and loops through the "Web Service Workflow Tools" sections | |
| 116 Then opens each and every tool (xml file) and looks for the description tag for the "Web Service" and "Client for Method" values and then checks | |
| 117 if it same as the opearation and web service. As the code is dependent on description of the toool xml file, any change in the description tag | |
| 118 of the tool xml file can impact this function. | |
| 119 ''' | |
| 120 def isToolPresent(self): | |
| 121 | |
| 122 f = open(self.galaxyhome+'/tool_conf.xml','r') | |
| 123 line = f.readline() | |
| 124 linestripped = line.lstrip() | |
| 125 linestripped = linestripped.rstrip() | |
| 126 | |
| 127 while linestripped != '<section name="Select Web Service Workflow Tool" id="WebServiceWorkflow">': | |
| 128 line = f.readline() | |
| 129 linestripped = line.lstrip() | |
| 130 linestripped = linestripped.rstrip() | |
| 131 | |
| 132 line = f.readline() | |
| 133 linestripped = line.lstrip() | |
| 134 linestripped = linestripped.rstrip() | |
| 135 | |
| 136 while linestripped != '</section>': | |
| 137 if linestripped.find('<') >-1: | |
| 138 toolparts = linestripped.split('"') | |
| 139 print '\ntoolparts are : ',toolparts | |
| 140 f1 = open(self.galaxyhome+'/tools/'+toolparts[-2],'r') | |
| 141 line1 = f1.readline() | |
| 142 while not line1.find('<description>') >-1: | |
| 143 line1 = f1.readline() | |
| 144 | |
| 145 linestripped1 = line1.lstrip() | |
| 146 linestripped1 = linestripped1.rstrip() | |
| 147 descriptionparts=linestripped1.split(' ') | |
| 148 print '\nOperation : ',descriptionparts | |
| 149 service = '' | |
| 150 if(self.servicetype == 'SOAP'): | |
| 151 service = self.clientfile | |
| 152 elif self.servicetype == 'REST': | |
| 153 service = self.webservice | |
| 154 print 'Service : ',service | |
| 155 print 'operation : ',self.operation | |
| 156 if(descriptionparts[5]==self.operation and descriptionparts[10] == service): | |
| 157 f.close() | |
| 158 f1.close() | |
| 159 return True | |
| 160 f1.close() | |
| 161 line = f.readline() | |
| 162 linestripped = line.lstrip() | |
| 163 linestripped = linestripped.rstrip() | |
| 164 | |
| 165 f.close() | |
| 166 return False | |
| 167 | |
| 168 | |
| 169 | |
| 170 def wsdlClient(self): | |
| 171 with warnings.catch_warnings(): | |
| 172 warnings.simplefilter("ignore") | |
| 173 | |
| 174 wLoad=wsdlLoader() | |
| 175 galaxyhome=os.environ.get('GALAXY_HOME') | |
| 176 sys.path.append(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/clientGenerator') | |
| 177 os.chdir(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/clientGenerator') | |
| 178 | |
| 179 a = str(self.webservice).split('/') | |
| 180 wsdlnamelist = a[len(a)-1].split(".") | |
| 181 if len(wsdlnamelist)==1: | |
| 182 wsdlnamelist=a[len(a)-1].split('?') | |
| 183 print wsdlnamelist | |
| 184 | |
| 185 foldername=wsdlnamelist[0] | |
| 186 | |
| 187 path =galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/clientGenerator/'+foldername | |
| 188 | |
| 189 #creates the client stubs | |
| 190 clientfile=wLoad.wsdlUrl2path(str(self.webservice),foldername) | |
| 191 | |
| 192 webservice = clientfile | |
| 193 outputfile=open(self.outputfile,'w') | |
| 194 test = ClientCreator() | |
| 195 print 'inputs of '+self.operation+':\n' | |
| 196 inputs= test.opname2inputs(self.operation,webservice) | |
| 197 inputl = nested2flatDict(inputs) | |
| 198 inputlist = inputl.keys() | |
| 199 #noOfInputs=inputlist.count() | |
| 200 print '*',inputs,'\n' | |
| 201 count =0 | |
| 202 if (len(inputs)==0): | |
| 203 inputs={' ':' '} | |
| 204 #inputlist=inputs.keys() | |
| 205 for i in inputlist: | |
| 206 if count==0: | |
| 207 outputfile.write(i+'\t'+webservice+'\t'+self.operation+'\n') | |
| 208 else: | |
| 209 outputfile.write(i+'\n') | |
| 210 count=count+1 | |
| 211 | |
| 212 cc = ClientCreator() | |
| 213 self.msinstance = cc.opname2inputClassOb(self.operation,webservice) | |
| 214 galaxyhome=os.environ.get('GALAXY_HOME') | |
| 215 | |
| 216 clientCountFile=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/ClientCount.xml','r') | |
| 217 clientCountFile.readline() | |
| 218 clientCountStr = clientCountFile.readline() | |
| 219 | |
| 220 clientCount=string.atoi(clientCountStr) | |
| 221 clientCount=clientCount+1 | |
| 222 clientCountFile.close() | |
| 223 | |
| 224 clientCountFile=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/ClientCount.xml','w') | |
| 225 clientCountFile.write('<count> \n') | |
| 226 clientCountFile.write(str(clientCount)+'\n') | |
| 227 clientCountFile.write('</count> \n') | |
| 228 clientCountFile.close() | |
| 229 | |
| 230 | |
| 231 | |
| 232 | |
| 233 clientName = 'workflowclient_'+ str(clientCount) | |
| 234 | |
| 235 clientXml=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/'+clientName+'.xml','w') | |
| 236 clientXml.seek(0,0) | |
| 237 | |
| 238 clientXml.write('<tool id="' + clientName+'" name="'+foldername+'.' + self.operation +'">\n') | |
| 239 clientXml.write(' <description> Client for operation : '+self.operation+' , Web service : '+webservice+' </description>\n') | |
| 240 | |
| 241 | |
| 242 #clientXml.write(' <command interpreter="python">\n client_1.py \n' +' $output \n ' +self.webservice+' \n '+self.operation+'\n') | |
| 243 | |
| 244 test = MessageHandler() | |
| 245 | |
| 246 #the workflow tool/client for a REST Web service invokes ./workflowclients/client_1.py to invoke the Web service | |
| 247 #write the command tag to specify the arguments passed to this client_1.py | |
| 248 | |
| 249 clientXml.write(' <command interpreter="python">\n client_1.py\n'+' #if $cond_source.optional_param_source=="no":\n $output\n $servicetype\n $url\n $method\n' +webservice+ ' '+self.operation+'\n') | |
| 250 ##write such that the parameters passed to client_1.py(change name to clientName.py) are dependent on a for loop | |
| 251 | |
| 252 # The paramter having |$| means that it has an elemnet with maxoccurs='unbounded' (i.e. array of strings) | |
| 253 | |
| 254 j=0 | |
| 255 for param in inputl: | |
| 256 if self.isRequired(param): | |
| 257 clientXml.write(' "'+param+'"\n #if $source'+str(j)+'.source'+str(j)+'_source=="user":\n $source'+str(j)+'.user_param'+str(j)+'\n #else:\n fileInput\n $source' + str(j) + '.cached_param' + str(j)+'\n #end if\n') | |
| 258 j=j+1 | |
| 259 | |
| 260 clientXml.write(' #else:\n $output\n $servicetype\n $url\n $method\n' +webservice+' '+self.operation+'\n') | |
| 261 | |
| 262 j=0 | |
| 263 for param in inputl: | |
| 264 if self.isRequired(param): | |
| 265 clientXml.write(' "'+param+'"\n #if $source'+str(j)+'.source'+str(j)+'_source=="user":\n $source'+str(j)+'.user_param'+str(j)+'\n #else:\n fileInput\n $source' + str(j) + '.cached_param' + str(j)+'\n #end if\n') | |
| 266 j=j+1 | |
| 267 | |
| 268 for param in inputl: | |
| 269 if not self.isRequired(param): | |
| 270 clientXml.write(' "'+param+'"\n #if $cond_source.source'+str(j)+'.source'+str(j)+'_source=="user":\n $cond_source.source'+str(j)+'.user_param'+str(j)+'\n #else:\n fileInput\n $cond_source.source' + str(j) + '.cached_param' + str(j)+'\n #end if\n') | |
| 271 j=j+1 | |
| 272 | |
| 273 | |
| 274 clientXml.write(' #end if\n') | |
| 275 | |
| 276 clientXml.write('</command>\n') | |
| 277 | |
| 278 #start writing inputs | |
| 279 ##write inputs depending on required or not. if not required den dont display | |
| 280 ##if required- den check default value, and if options exist.Depending on that | |
| 281 ##decide the type of parameter and options | |
| 282 ##The input servicetype tells what type of webservice it is wether SOAP or REST - Useful during invocation of the web servcie | |
| 283 clientXml.write(' <inputs>\n') | |
| 284 clientXml.write(' <param name="servicetype" type="hidden" value="SOAP" />\n') | |
| 285 clientXml.write(' <param name="url" type="hidden" value="'+self.webservice+'" />\n') | |
| 286 clientXml.write(' <param name="method" type="hidden" value="'+self.operation+'" />\n') | |
| 287 | |
| 288 j=0 | |
| 289 for param in inputl: | |
| 290 if self.isRequired(param): | |
| 291 pName = getattr(self.paramelement,'pname') | |
| 292 | |
| 293 clientXml.write('<conditional name="source'+str(j)+'">\n <param name="source' + str(j)+'_source" type="select" label="'+pName+' Source"> \n <option value="cached" selected="true">Param value will be taken from previous step</option> \n <option value="user">User will enter the param value</option> \n </param>\n <when value="user">\n') | |
| 294 clientXml.write(' <param format="text" size = "150" name = "user_param'+str(j)+'" ') | |
| 295 clientXml.write('type="text" label="Enter '+pName+'" help="see tip below" />\n') | |
| 296 # clientXml.write(' </param> \n') | |
| 297 clientXml.write(' </when>\n') | |
| 298 clientXml.write(' <when value="cached">\n <param name = "cached_param'+ str(j)+'" type="data" label="' + pName + '"/> \n </when>\n </conditional>') | |
| 299 j=j+1 | |
| 300 | |
| 301 | |
| 302 clientXml.write(' <conditional name="cond_source">\n <param name="optional_param_source" type="select" label="Display Optional Parameters"> \n <option value="no" selected="true">no</option> \n <option value="yes">yes</option> \n </param> \n <when value="no"> \n </when>\n <when value="yes"> \n') | |
| 303 | |
| 304 for param in inputl: | |
| 305 if not self.isRequired(param): | |
| 306 pName = getattr(self.paramelement,'pname') | |
| 307 | |
| 308 clientXml.write('\n<conditional name="source'+str(j)+'">\n <param name="source' + str(j)+'_source" type="select" label="'+pName+' Source"> \n <option value="cached" selected="true">Param value will be taken from previous step</option> \n <option value="user">User will enter the param value</option> \n</param>\n <when value="user">') | |
| 309 clientXml.write('<param format="text" size = "150" name = "user_param'+str(j)+'" ') | |
| 310 clientXml.write('type="text" label="Enter '+pName+'" help="see tip below" />\n') | |
| 311 clientXml.write(' </when>\n') | |
| 312 clientXml.write('<when value="cached">\n <param name = "cached_param'+ str(j)+'" type="data" label="' + pName + '"/> \n </when>\n </conditional>\n') | |
| 313 j=j+1 | |
| 314 | |
| 315 | |
| 316 clientXml.write(' </when>\n </conditional>\n') | |
| 317 | |
| 318 clientXml.write('</inputs>\n <outputs>\n <data format="tabular" name="output" />\n </outputs>\n') | |
| 319 clientXml.write(' <help>\n') | |
| 320 paramtype = None | |
| 321 for param in inputl: | |
| 322 if self.isRequired(param): | |
| 323 if isinstance(self.paramelement,ZSI.TC.String): | |
| 324 paramtype = 'String' | |
| 325 elif isinstance(self.paramelement,ZSI.TCnumbers.FPfloat): | |
| 326 paramtype = 'Floating point' | |
| 327 elif isinstance(self.paramelement,ZSI.TC.Boolean): | |
| 328 paramtype = 'Boolean' | |
| 329 elif isinstance(self.paramelement,ZSI.TCnumbers.Iint): | |
| 330 paramtype = 'Integer' | |
| 331 clientXml.write('\n.. class:: infomark\n\n**TIP:** About '+ getattr(self.paramelement,'pname') +': type is ' + paramtype + '\n') | |
| 332 | |
| 333 | |
| 334 clientXml.write(' </help>\n</tool>') | |
| 335 | |
| 336 # clientXml.write(' <help>\n') | |
| 337 | |
| 338 # j=0 | |
| 339 # for param in params: | |
| 340 # clientXml.write('\n.. class:: infomark\n\n**TIP:** '+ param +' type is ' + paramTypes[j] +'\n') | |
| 341 | |
| 342 # clientXml.write(' </help>\n</tool>') | |
| 343 # clientXml.write('</tool>') | |
| 344 clientXml.close() | |
| 345 editor = editToolConfig() | |
| 346 editor.addTool(clientName) | |
| 347 | |
| 348 ##later add help feature | |
| 349 | |
| 350 | |
| 351 | |
| 352 def wadlClient(self): | |
| 353 | |
| 354 ##parse wadl | |
| 355 pkg=JPackage('lsdis') | |
| 356 urlToPass=java.net.URL(self.webservice) | |
| 357 wadlUrl = self.webservice | |
| 358 | |
| 359 webserviceId = ''#self.operation | |
| 360 resUrl = self.operation | |
| 361 | |
| 362 urls = [] | |
| 363 methods = [] | |
| 364 params = [] | |
| 365 | |
| 366 #invoke the WADL parser packaged with this tool. | |
| 367 WADLParserDriver=pkg.WADLParserDriver | |
| 368 wPD=WADLParserDriver() | |
| 369 wPD.parse(urlToPass) | |
| 370 urls = wPD.getUrl() | |
| 371 methods = wPD.getCompleteMethodList() | |
| 372 | |
| 373 a= str(self.webservice).split('/') | |
| 374 wadlnamelist = a[len(a)-1].split(".") | |
| 375 if len(wadlnamelist)==1: | |
| 376 wadlnamelist=a[len(a)-1].split('?') | |
| 377 print wadlnamelist | |
| 378 | |
| 379 wadlname=wadlnamelist[0] | |
| 380 | |
| 381 #write into the output file information about the method and Web service to be invoked. | |
| 382 f=open(self.outputfile,'w') | |
| 383 f.write(self.webservice+'\t') | |
| 384 f.write(resUrl+'\t') | |
| 385 | |
| 386 #get parameters for the selected method of the Web service | |
| 387 i=0 | |
| 388 for method in methods: | |
| 389 x = str(method.getId()) | |
| 390 y = str(urls.get(i)) | |
| 391 webserviceId = x | |
| 392 if y == resUrl : | |
| 393 params = method.getRequest().getParams() | |
| 394 break | |
| 395 i=i+1 | |
| 396 | |
| 397 f.write(webserviceId) | |
| 398 | |
| 399 galaxyhome=os.environ.get('GALAXY_HOME') | |
| 400 | |
| 401 methodname = resUrl.split('/') | |
| 402 | |
| 403 #./workflowclients/ClientCount.xml keeps the count of the clients/tools currently registered in Galaxy for Web service invocation. | |
| 404 #read the count and increment it. | |
| 405 clientCountFile=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/ClientCount.xml','r') | |
| 406 clientCountFile.readline() | |
| 407 clientCountStr = clientCountFile.readline() | |
| 408 clientCount=string.atoi(clientCountStr) | |
| 409 clientCount=clientCount+1 | |
| 410 clientCountFile.close() | |
| 411 | |
| 412 clientCountFile=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/ClientCount.xml','w') | |
| 413 clientCountFile.write('<count> \n') | |
| 414 clientCountFile.write(str(clientCount)+'\n') | |
| 415 clientCountFile.write('</count> \n') | |
| 416 | |
| 417 #include the count in the tool's name and id to uniquely identify it. | |
| 418 clientName = 'workflowclient_'+ str(clientCount) | |
| 419 | |
| 420 #create a new xml file under ./workflowclients/ | |
| 421 clientXml=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/'+clientName+'.xml','w') | |
| 422 clientXml.seek(0,0) | |
| 423 | |
| 424 #write the tool id, name and description | |
| 425 clientXml.write('<tool id="' + clientName+'" name="' + wadlname + '.' + methodname[-1] +'">\n') | |
| 426 clientXml.write(' <description> Client for method : '+self.operation+' , Web service : '+self.webservice+' </description>\n') | |
| 427 | |
| 428 #the workflow tool/client for a REST Web service invokes ./workflowclients/client_1.py to invoke the Web service | |
| 429 #write the command tag to specify the arguments passed to this client_1.py | |
| 430 clientXml.write(' <command interpreter="python">\n client_1.py\n'+' #if $cond_source.optional_param_source=="no":\n $output\n $servicetype\n $url\n $method\n ' +resUrl+'\n') | |
| 431 | |
| 432 | |
| 433 j=0 | |
| 434 for param in params: | |
| 435 if param.isRequired(): | |
| 436 clientXml.write(' '+self.formatString(param.getName())+'\n #if $source'+str(j)+'.source'+str(j)+'_source=="user":\n $source'+str(j)+'.user_param'+str(j)+'\n #else:\n fileInput\n $source' + str(j) + '.cached_param' + str(j)+'\n #end if\n') | |
| 437 j=j+1 | |
| 438 | |
| 439 clientXml.write(' #else:\n $output\n $servicetype\n $url\n $method\n' +resUrl+'\n') | |
| 440 j=0 | |
| 441 for param in params: | |
| 442 if param.isRequired(): | |
| 443 clientXml.write(' '+self.formatString(param.getName())+'\n #if $source'+str(j)+'.source'+str(j)+'_source=="user":\n $source'+str(j)+'.user_param'+str(j)+'\n #else:\n fileInput\n $source' + str(j) + '.cached_param' + str(j)+'\n #end if\n') | |
| 444 j=j+1 | |
| 445 | |
| 446 for param in params: | |
| 447 if not param.isRequired(): | |
| 448 clientXml.write(' '+self.formatString(param.getName())+'\n #if $cond_source.source'+str(j)+'.source'+str(j)+'_source=="user":\n $cond_source.source'+str(j)+'.user_param'+str(j)+'\n #else:\n fileInput\n $cond_source.source' + str(j) + '.cached_param' + str(j)+'\n #end if\n') | |
| 449 j=j+1 | |
| 450 | |
| 451 clientXml.write(' #end if\n') | |
| 452 clientXml.write('</command>\n') | |
| 453 | |
| 454 #start writing inputs | |
| 455 ##write inputs depending on required or not. if not required den dont display | |
| 456 ##if required- den check default value, and if options exist.Depending on that | |
| 457 ##decide the type of parameter and options | |
| 458 ##The input servicetype tells what type of webservice it is wether SOAP or REST - Useful during invocation of the web servcie | |
| 459 | |
| 460 clientXml.write(' <inputs>\n') | |
| 461 clientXml.write(' <param name="servicetype" type="hidden" value="REST"/>\n') | |
| 462 clientXml.write(' <param name="url" type="hidden" value="'+self.webservice+'" />\n') | |
| 463 clientXml.write(' <param name="method" type="hidden" value="'+self.operation+'" />\n') | |
| 464 | |
| 465 | |
| 466 #create a param for each required parameter described in the WADL. Check if defaults are specified. Create param such that | |
| 467 #it can either be given a value manually or the value can be taken from a previous step. | |
| 468 j=0 | |
| 469 for param in params: | |
| 470 if param.isRequired(): | |
| 471 pName = param.getName() | |
| 472 for doc in param.getDocs(): | |
| 473 if doc.getTitle()=="prompt" or doc.getTitle()=="Prompt" or doc.getTitle()=="PROMPT": | |
| 474 pName = doc.getInnerText() | |
| 475 | |
| 476 clientXml.write('<conditional name="source'+str(j)+'">\n <param name="source' + str(j)+'_source" type="select" label="'+pName+' Source"> \n <option value="cached" selected="true">Param value will be taken from previous step</option> \n <option value="user">User will enter the param value</option> \n </param>\n <when value="user">\n') | |
| 477 if param.getOptions().size()==0: | |
| 478 clientXml.write(' <param format="text" size = "150" name = "user_param'+str(j)+'" ') | |
| 479 if not param.getDefault1() == None: | |
| 480 clientXml.write('value="'+param.getDefault1()+'" ') | |
| 481 clientXml.write('type="text" label="Enter '+pName+'" help="see tip below" />\n') | |
| 482 else: | |
| 483 clientXml.write(' <param name="user_param'+str(j)+'" type="select" label="Select '+pName+'" help="see tip below">\n' ) | |
| 484 for option in param.getOptions(): | |
| 485 clientXml.write(' <option value="'+self.formatString(option.getName())+'" ') | |
| 486 if option.getName() == param.getDefault1(): | |
| 487 clientXml.write('selected="true"') | |
| 488 clientXml.write('>'+option.getName()+'</option>\n ') | |
| 489 clientXml.write(' </param> \n') | |
| 490 clientXml.write(' </when>\n') | |
| 491 clientXml.write(' <when value="cached">\n <param name = "cached_param'+ str(j)+'" type="data" label="' + pName + '"/> \n </when></conditional>') | |
| 492 j=j+1 | |
| 493 | |
| 494 #create a conditional param for each optional parameter described in the WADL. Again the param can be given a value manually or the value can be taken from | |
| 495 #a previous step. | |
| 496 clientXml.write(' <conditional name="cond_source">\n <param name="optional_param_source" type="select" label="Display Optional Parameters"> \n <option value="no" selected="true">no</option> \n <option value="yes">yes</option> \n </param> \n <when value="no"> \n </when>\n <when value="yes"> \n') | |
| 497 | |
| 498 for param in params: | |
| 499 if not param.isRequired(): | |
| 500 pName = param.getName() | |
| 501 for doc in param.getDocs(): | |
| 502 if doc.getTitle()=="prompt" or doc.getTitle()=="Prompt" or doc.getTitle()=="PROMPT": | |
| 503 pName = doc.getInnerText() | |
| 504 | |
| 505 clientXml.write('\n<conditional name="source'+str(j)+'">\n <param name="source' + str(j)+'_source" type="select" label="'+pName+' Source"> \n <option value="cached" selected="true">Param value will be taken from previous step</option> \n <option value="user">User will enter the param value</option> \n</param>\n <when value="user">') | |
| 506 if param.getOptions().size()==0: | |
| 507 clientXml.write('<param format="text" size = "150" name = "user_param'+str(j)+'" ') | |
| 508 if not param.getDefault1() == None: | |
| 509 clientXml.write('value="'+param.getDefault1()+'" ') | |
| 510 clientXml.write('type="text" label="Enter '+pName+'" help="see tip below" />\n') | |
| 511 else: | |
| 512 clientXml.write('<param name="user_param'+str(j)+'" type="select" label="Select '+pName+'" help="see tip below">\n' ) | |
| 513 for option in param.getOptions(): | |
| 514 clientXml.write(' <option value="'+self.formatString(option.getName())+'" ') | |
| 515 if option.getName() == param.getDefault1(): | |
| 516 clientXml.write('selected="true"') | |
| 517 clientXml.write('>'+option.getName()+'</option>\n ') | |
| 518 clientXml.write(' </param> \n') | |
| 519 clientXml.write(' </when>\n') | |
| 520 clientXml.write('<when value="cached">\n <param name = "cached_param'+ str(j)+'" type="data" label="' + pName + '"/> \n </when></conditional>\n') | |
| 521 j=j+1 | |
| 522 | |
| 523 | |
| 524 | |
| 525 clientXml.write(' </when>\n </conditional>\n</inputs>\n <outputs>\n <data format="tabular" name="output" />\n </outputs>\n') | |
| 526 | |
| 527 #write information about each parameter in the help section | |
| 528 clientXml.write(' <help>\n') | |
| 529 clientXml.write('Replace white space with ** in all parameter values\n') | |
| 530 | |
| 531 for param in params: | |
| 532 if param.isRequired(): | |
| 533 pName = param.getName() | |
| 534 for doc in param.getDocs(): | |
| 535 if doc.getTitle()=="prompt" or doc.getTitle()=="Prompt" or doc.getTitle()=="PROMPT": | |
| 536 pName = doc.getInnerText() | |
| 537 clientXml.write('\n.. class:: infomark\n\n**TIP:** '+ pName +' type is ' + param.getType()+'\n') | |
| 538 | |
| 539 clientXml.write(' </help>\n</tool>') | |
| 540 | |
| 541 #adds the newly created tool to tool_conf.xml in Galaxy under the 'Web Service Workflow Tools' section. | |
| 542 editor = editToolConfig() | |
| 543 editor.addTool(clientName) | |
| 544 | |
| 545 | |
| 546 | |
| 547 | |
| 548 def sawadlClient(self): | |
| 549 ##parse sawadl | |
| 550 | |
| 551 pkg=JPackage('edu.uga.cs.lsdis.meteors.wadls') | |
| 552 pkgModel =JPackage('org.semanticweb.owlapi.model') | |
| 553 pkgApiBinding =JPackage('org.semanticweb.owlapi.apibinding') | |
| 554 pkgVocab = JPackage('org.semanticweb.owlapi.vocab') | |
| 555 | |
| 556 DOCUMENT_IRI = "http://cs.uga.edu/~ganjoo/galaxy/EDAM.owl" | |
| 557 | |
| 558 sawadlUrl = self.webservice | |
| 559 | |
| 560 webserviceId = ''#self.inputs | |
| 561 resUrl = self.operation | |
| 562 | |
| 563 urls = [] | |
| 564 methods = [] | |
| 565 params = [] | |
| 566 annotationSet = [] | |
| 567 | |
| 568 SAWADLParserDriver=pkg.SAWADLParserDriver | |
| 569 sawPD=SAWADLParserDriver() | |
| 570 sawPD.parse(sawadlUrl) | |
| 571 urls = sawPD.getUrl() | |
| 572 methods = sawPD.getCompleteMethodList() | |
| 573 | |
| 574 IRI = pkgModel.IRI | |
| 575 OWLRDFVocabulary = pkgVocab.OWLRDFVocabulary | |
| 576 OWLManager = pkgApiBinding.OWLManager | |
| 577 OWLLiteral = pkgModel.OWLLiteral | |
| 578 owlOntManager = OWLManager.createOWLOntologyManager() | |
| 579 ontology = owlOntManager.loadOntologyFromOntologyDocument(IRI.create(DOCUMENT_IRI)) | |
| 580 dataFactory = owlOntManager.getOWLDataFactory() | |
| 581 propertyComment = dataFactory.getOWLAnnotationProperty(OWLRDFVocabulary.RDFS_COMMENT.getIRI()) | |
| 582 | |
| 583 #write into the output file information about the method and Web service to be invoked. | |
| 584 f=open(self.outputfile,'w') | |
| 585 f.write(self.webservice+'\t') | |
| 586 f.write(resUrl+'\t') | |
| 587 | |
| 588 i=0 | |
| 589 for method in methods: | |
| 590 x = str(method.getName()) | |
| 591 y = str(urls.get(i)) | |
| 592 webserviceId = x | |
| 593 #if x == webserviceId : | |
| 594 if y == resUrl : | |
| 595 params = method.getRequest().getParamList() | |
| 596 break | |
| 597 i=i+1 | |
| 598 | |
| 599 | |
| 600 f.write(webserviceId) | |
| 601 | |
| 602 ##generate client's xml | |
| 603 galaxyhome=os.environ.get('GALAXY_HOME') | |
| 604 | |
| 605 clientCountFile=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/ClientCount.xml','r') | |
| 606 clientCountFile.readline() | |
| 607 clientCountStr = clientCountFile.readline() | |
| 608 clientCount=string.atoi(clientCountStr) | |
| 609 clientCount=clientCount+1 | |
| 610 clientCountFile.close() | |
| 611 | |
| 612 clientCountFile=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/ClientCount.xml','w') | |
| 613 clientCountFile.write('<count> \n') | |
| 614 clientCountFile.write(str(clientCount)+'\n') | |
| 615 clientCountFile.write('</count> \n') | |
| 616 | |
| 617 | |
| 618 | |
| 619 | |
| 620 clientName = 'workflowclient_'+ str(clientCount) | |
| 621 | |
| 622 clientXml=open(galaxyhome+'/tools/WebServiceToolWorkflow/workflowclients/'+clientName+'.xml','w') | |
| 623 clientXml.seek(0,0) | |
| 624 | |
| 625 clientXml.write('<tool id="' + clientName+'" name="' + webserviceId +'">\n') | |
| 626 clientXml.write(' <description> Client for method: '+webserviceId+' , Web service: '+self.webservice+' </description>\n') | |
| 627 | |
| 628 | |
| 629 clientXml.write(' <command interpreter="python">\n #client_1.py \n'+' $output \n $servicetype\n $url\n $method\n ' +resUrl+'\n') | |
| 630 ##write such that the parameters passed to client1.py(change name to clientName.py) are dependent on a for loop | |
| 631 | |
| 632 | |
| 633 j=0 | |
| 634 for param in params: | |
| 635 if param.getRequired()=='true' or param.getRequired()=='True' or param.getRequired()=='TRUE': | |
| 636 clientXml.write(' '+self.formatString(param.getName())+'\n#if $source'+str(j)+'.source'+str(j)+'_source=="user" $source'+str(j)+'.user_param'+str(j)+' #else $source' + str(j) + '.cached_param' + str(j)+' #end if\n') | |
| 637 j=j+1 | |
| 638 clientXml.write('#if $cond_source.optional_param_source=="yes"') | |
| 639 | |
| 640 for param in params: | |
| 641 if not param.getRequired()=='true' and not param.getRequired()=='True' and not param.getRequired()=='TRUE': | |
| 642 clientXml.write(' '+self.formatString(param.getName())+'\n#if $cond_source.source'+str(j)+'.source'+str(j)+'_source=="user" $cond_source.source'+str(j)+'.user_param'+str(j)+' #else $cond_source.source' + str(j) + '.cached_param' + str(j)+' #end if\n') | |
| 643 j=j+1 | |
| 644 | |
| 645 clientXml.write('#else \n#end if\n') | |
| 646 clientXml.write('</command>\n') | |
| 647 | |
| 648 ##write inputs depending on required or not. if not required den dont display | |
| 649 ##if required- den check default value, and if options exist.Depending on that | |
| 650 ##decide the type of parameter and options | |
| 651 ##The input servicetype tells what type of webservice it is wether SOAP or REST - Useful during invocation of the web servcie | |
| 652 | |
| 653 clientXml.write(' <inputs>\n') | |
| 654 clientXml.write(' <param name="servicetype" type="hidden" value="REST" />') | |
| 655 clientXml.write(' <param name="url" type="hidden" value="'+self.webservice+'" />\n') | |
| 656 clientXml.write(' <param name="method" type="hidden" value="'+self.operation+'" />\n') | |
| 657 | |
| 658 | |
| 659 j=0 | |
| 660 for param in params: | |
| 661 if param.getRequired()=='true' or param.getRequired()=='True' or param.getRequired()=='TRUE': | |
| 662 clientXml.write('<conditional name="source'+str(j)+'">\n <param name="source' + str(j)+'_source" type="select" label="'+param.getName()+' Source"> \n <option value="cached" selected="true">Param value will be taken from previous step</option> \n <option value="user">User will enter the param value</option> \n </param>\n <when value="user">\n') | |
| 663 if param.getOptionvalue().size()==0: | |
| 664 clientXml.write(' <param format="text" size = "150" name = "user_param'+str(j)+'" ') | |
| 665 if not param.getDefault1() == None: | |
| 666 clientXml.write('value="'+param.getDefault1()+'" ') | |
| 667 clientXml.write('type="text" label="Enter '+param.getName()+'" help="see tip below" />\n') | |
| 668 j=j+1 | |
| 669 else: | |
| 670 clientXml.write(' <param name="user_param'+str(j)+'" type="select" label="Select '+param.getName()+'" help="see tip below">\n' ) | |
| 671 for option in param.getOptionvalue(): | |
| 672 clientXml.write(' <option value="'+self.formatString(option)+'" ') | |
| 673 if option == param.getDefault1(): | |
| 674 clientXml.write('selected="true"') | |
| 675 clientXml.write('>'+option+'</option>\n ') | |
| 676 clientXml.write(' </param> \n') | |
| 677 j=j+1 | |
| 678 clientXml.write(' </when>\n') | |
| 679 clientXml.write(' <when value="cached">\n <param name = "cached_param'+ str(j)+'" type="data" label="' + param.getName() + '"/> \n </when></conditional>') | |
| 680 | |
| 681 clientXml.write(' <conditional name="cond_source">\n <param name="optional_param_source" type="select" label="Display Additional Parameters"> \n <option value="no" selected="true">no</option> \n <option value="yes">yes</option> \n </param> \n <when value="no"> \n </when>\n <when value="yes"> \n') | |
| 682 | |
| 683 for param in params: | |
| 684 if not param.getRequired()=='true' and not param.getRequired()=='True' and not param.getRequired()=='TRUE': | |
| 685 clientXml.write('<conditional name="source'+str(j)+'">\n <param name="source' + str(j)+'_source" type="select" label="'+param.getName()+' Source"> \n <option value="cached" selected="true">Param value will be taken from previous step</option> \n <option value="user">User will enter the param value</option> \n </param>\n <when value="user">\n') | |
| 686 if param.getOptionvalue().size()==0: | |
| 687 clientXml.write(' <param format="text" size = "150" name = "user_param'+str(j)+'" ') | |
| 688 if not param.getDefault1() == None: | |
| 689 clientXml.write('value="'+param.getDefault1()+'" ') | |
| 690 clientXml.write('type="text" label="Enter '+param.getName()+'" help="see tip below" />\n') | |
| 691 j=j+1 | |
| 692 else: | |
| 693 clientXml.write(' <param name="user_param'+str(j)+'" type="select" label="Select'+param.getName()+'" help="see tip below">\n' ) | |
| 694 for option in param.getOptionvalue(): | |
| 695 clientXml.write(' <option value="'+self.formatString(option)+'" ') | |
| 696 if option == param.getDefault1(): | |
| 697 clientXml.write('selected="true"') | |
| 698 clientXml.write('>'+option+'</option>\n ') | |
| 699 clientXml.write(' </param> \n') | |
| 700 j=j+1 | |
| 701 clientXml.write(' </when>\n') | |
| 702 clientXml.write(' <when value="cached">\n <param name = "cached_param'+ str(j)+'" type="data" label="' + param.getName() + '"/> \n </when></conditional>') | |
| 703 | |
| 704 clientXml.write(' </when>\n </conditional>\n') | |
| 705 | |
| 706 clientXml.write('</inputs>\n <outputs>\n <data format="tabular" name="output" />\n </outputs>\n') | |
| 707 | |
| 708 clientXml.write(' <help>\n') | |
| 709 for param in params: | |
| 710 if param.getRequired()=='true' or param.getRequired()=='True' or param.getRequired()=='TRUE': | |
| 711 clientXml.write('\n.. class:: infomark\n\n**TIP:** About '+ param.getName() +': type is ' + param.getType()) | |
| 712 | |
| 713 modelRef = sawPD.getCompleteModelReference(param) | |
| 714 if not modelRef is None: | |
| 715 paramClass = dataFactory.getOWLClass(IRI.create(modelRef)); | |
| 716 annotationSet = paramClass.getAnnotations(ontology,propertyComment) | |
| 717 for annotation in annotationSet: | |
| 718 if isinstance(annotation.getValue(),OWLLiteral): | |
| 719 val = annotation.getValue() | |
| 720 if val.isOWLStringLiteral() and not val.isOWLTypedLiteral(): | |
| 721 print 'val.getLiteral()=' + val.getLiteral() | |
| 722 clientXml.write(', description from ontology is "' + val.getLiteral()+'"') | |
| 723 break | |
| 724 clientXml.write('\n') | |
| 725 clientXml.write(' </help>\n</tool>') | |
| 726 editor = editToolConfig() | |
| 727 editor.addTool(clientName) | |
| 728 | |
| 729 | |
| 730 def wsdlRestClient(self): | |
| 731 ##parse wadl | |
| 732 javahome = os.environ.get('JAVA_HOME') | |
| 733 galaxyhome=os.environ.get('GALAXY_HOME') | |
| 734 classpath= galaxyhome + '/tools/WebServiceToolWorkflow_REST_SOAP/WodenWSDLParser/bin' | |
| 735 jarpath = galaxyhome + '/tools/WebServiceToolWorkflow_REST_SOAP/WodenWSDLParser/lib/' | |
| 736 machine = platform.machine() | |
| 737 | |
| 738 if machine == 'x86_64' : | |
| 739 print 'a' | |
| 740 startJVM("%s/jre/lib/amd64/server/libjvm.so" % javahome,"-ea", "-Djava.class.path=%s" % classpath,"-Djava.ext.dirs=%s" % jarpath) | |
| 741 elif machine == 'i686' : | |
| 742 print 'b' | |
| 743 startJVM("%s/jre/lib/i386/server/libjvm.so" % javahome,"-ea", "-Djava.class.path=%s" % classpath,"-Djava.ext.dirs=%s" % jarpath) | |
| 744 elif machine == 'sun4u' : | |
| 745 startJVM("%s/jre/lib/sparc/server/libjvm.so" % javahome,"-ea", "-Djava.class.path=%s" % classpath,"-Djava.ext.dirs=%s" % jarpath) | |
| 746 else : | |
| 747 print 'c' | |
| 748 System.exit("Could not identify machine, please specify path to libjvm.so") | |
| 749 | |
| 750 | |
| 751 pkg=JPackage('lsdis') | |
| 752 wsdlUrl = self.webservice | |
| 753 | |
| 754 webserviceId = ''#self.methodName | |
| 755 resUrl = self.operation | |
| 756 | |
| 757 urls = [] | |
| 758 methods = [] | |
| 759 params = [] | |
| 760 paramTypes = [] | |
| 761 | |
| 762 WSDLParserDriver =pkg.WSDLParserDriver | |
| 763 wPD=WSDLParserDriver() | |
| 764 wPD.parse(wsdlUrl) | |
| 765 methods = wPD.getCompleteMethodList() | |
| 766 urls = wPD.getUrl() | |
| 767 | |
| 768 f=open(self.outputfile,'w') | |
| 769 f.write(wsdlUrl+'\t') | |
| 770 f.write(resUrl+'\t') | |
| 771 | |
| 772 | |
| 773 i=0 | |
| 774 for method in methods: | |
| 775 x = str(method.getName().getLocalPart()) | |
| 776 y = str(url.get(i)) | |
| 777 webserviceId = x | |
| 778 if y == resUrl : | |
| 779 wPD.getParameters(x) | |
| 780 f.write('method matched') | |
| 781 paramTypes = wPD.getParamTypeList() | |
| 782 params = wPD.getParamList() | |
| 783 break | |
| 784 i=i+1 | |
| 785 | |
| 786 f.write(webserviceId) | |
| 787 | |
| 788 | |
| 789 | |
| 790 ##generate client's xml | |
| 791 galaxyhome=os.environ.get('GALAXY_HOME') | |
| 792 | |
| 793 clientCountFile=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/ClientCount.xml','r') | |
| 794 clientCountFile.readline() | |
| 795 clientCountStr = clientCountFile.readline() | |
| 796 clientCount=string.atoi(clientCountStr) | |
| 797 clientCount=clientCount+1 | |
| 798 clientCountFile.close() | |
| 799 | |
| 800 clientCountFile=open(galaxyhome+'/tools/WebServiceToolWorkflow_REST_SOAP/workflowclients/ClientCount.xml','w') | |
| 801 clientCountFile.write('<count> \n') | |
| 802 clientCountFile.write(str(clientCount)+'\n') | |
| 803 clientCountFile.write('</count> \n') | |
| 804 | |
| 805 | |
| 806 | |
| 807 | |
| 808 clientName = 'workflowclient_'+ str(clientCount) | |
| 809 | |
| 810 clientXml=open(galaxyhome+'/tools/WebServiceToolWorkflow_RESTSOAP/workflowclients/'+clientName+'.xml','w') | |
| 811 clientXml.seek(0,0) | |
| 812 | |
| 813 clientXml.write('<tool id="' + clientName+'" name="' + webserviceId +'">\n') | |
| 814 clientXml.write(' <description> Client for method: '+webserviceId+' , Web service: '+self.webservice+' </description>\n') | |
| 815 | |
| 816 | |
| 817 clientXml.write(' <command interpreter="python">\n client_1.py \n'+' $output \n $servicetype\n $url \n $method' +resUrl+'\n') | |
| 818 ##write such that the parameters passed to client1.py(change name to clientName.py) are dependent on a for loop | |
| 819 | |
| 820 j=0 | |
| 821 for param in params: | |
| 822 clientXml.write(' '+self.formatString(param)+'\n') | |
| 823 clientXml.write(' $param' + str(j)+'\n') | |
| 824 j=j+1 | |
| 825 clientXml.write('</command>\n') | |
| 826 | |
| 827 ##write inputs depending on required or not. if not required den dont display | |
| 828 ##if required- den check default value, and if options exist.Depending on that | |
| 829 ##decide the type of parameter and options | |
| 830 clientXml.write(' <inputs>\n') | |
| 831 clientXml.write(' <param name="servicetype" type="hidden" value="REST" />') | |
| 832 clientXml.write(' <param name="url" type="hidden" value="'+self.webservice+'" />\n') | |
| 833 clientXml.write(' <param name="method" type="hidden" value="'+self.operation+'" />\n') | |
| 834 | |
| 835 | |
| 836 j=0 | |
| 837 for param in params: | |
| 838 clientXml.write('<param format="text" size = "150" name = "param'+str(j)+'" ') | |
| 839 clientXml.write('type="text" label="'+param+'" help="see tip below" />\n') | |
| 840 j=j+1 | |
| 841 | |
| 842 | |
| 843 | |
| 844 clientXml.write('</inputs>\n <outputs>\n <data format="tabular" name="output" />\n </outputs>\n') | |
| 845 | |
| 846 clientXml.write(' <help>\n') | |
| 847 | |
| 848 j=0 | |
| 849 for param in params: | |
| 850 clientXml.write('\n.. class:: infomark\n\n**TIP:** '+ param +' type is ' + paramTypes[j] +'\n') | |
| 851 | |
| 852 clientXml.write(' </help>\n</tool>') | |
| 853 editor = editToolConfig() | |
| 854 editor.addTool(clientName) | |
| 855 | |
| 856 ##later add help feature | |
| 857 | |
| 858 if __name__ == "__main__": | |
| 859 | |
| 860 test = ClientGenerator('http://eupathdb.org/eupathdb/webservices/GeneQuestions/GenesByGeneType.wadl','genesbygenetype',None,'REST') | |
| 861 present = test.isToolPresent() | |
| 862 # required = test.isRequired('_parameters|_sequence') | |
| 863 print 'Tool is present : ',present | |
| 864 |
