model_file.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. """!
  2. @package gmodeler.model_file
  3. @brief wxGUI Graphical Modeler - model definition file
  4. Classes:
  5. - ProcessModelFile
  6. - WriteModelFile
  7. - WritePythonFile
  8. (C) 2010-2011 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. @author Martin Landa <landa.martin gmail.com>
  12. """
  13. import os
  14. import time
  15. import re
  16. from gui_core.forms import GUI
  17. from core.gcmd import GWarning, EncodeString
  18. class ProcessModelFile:
  19. """!Process GRASS model file (gxm)"""
  20. def __init__(self, tree):
  21. """!A ElementTree handler for the GXM XML file, as defined in
  22. grass-gxm.dtd.
  23. """
  24. self.tree = tree
  25. self.root = self.tree.getroot()
  26. # list of actions, data
  27. self.properties = dict()
  28. self.variables = dict()
  29. self.actions = list()
  30. self.data = list()
  31. self.loops = list()
  32. self.conditions = list()
  33. self._processWindow()
  34. self._processProperties()
  35. self._processVariables()
  36. self._processItems()
  37. self._processData()
  38. def _filterValue(self, value):
  39. """!Filter value
  40. @param value
  41. """
  42. value = value.replace('&lt;', '<')
  43. value = value.replace('&gt;', '>')
  44. return value
  45. def _getNodeText(self, node, tag, default = ''):
  46. """!Get node text"""
  47. p = node.find(tag)
  48. if p is not None:
  49. if p.text:
  50. return utils.normalize_whitespace(p.text)
  51. else:
  52. return ''
  53. return default
  54. def _processWindow(self):
  55. """!Process window properties"""
  56. node = self.root.find('window')
  57. if node is None:
  58. self.pos = self.size = None
  59. return
  60. self.pos, self.size = self._getDim(node)
  61. def _processProperties(self):
  62. """!Process model properties"""
  63. node = self.root.find('properties')
  64. if node is None:
  65. return
  66. for key in ('name', 'description', 'author'):
  67. self._processProperty(node, key)
  68. for f in node.findall('flag'):
  69. name = f.get('name', '')
  70. if name == 'overwrite':
  71. self.properties['overwrite'] = True
  72. def _processProperty(self, pnode, name):
  73. """!Process given property"""
  74. node = pnode.find(name)
  75. if node is not None:
  76. self.properties[name] = node.text
  77. else:
  78. self.properties[name] = ''
  79. def _processVariables(self):
  80. """!Process model variables"""
  81. vnode = self.root.find('variables')
  82. if vnode is None:
  83. return
  84. for node in vnode.findall('variable'):
  85. name = node.get('name', '')
  86. if not name:
  87. continue # should not happen
  88. self.variables[name] = { 'type' : node.get('type', 'string') }
  89. for key in ('description', 'value'):
  90. self._processVariable(node, name, key)
  91. def _processVariable(self, pnode, name, key):
  92. """!Process given variable"""
  93. node = pnode.find(key)
  94. if node is not None:
  95. if node.text:
  96. self.variables[name][key] = node.text
  97. def _processItems(self):
  98. """!Process model items (actions, loops, conditions)"""
  99. self._processActions()
  100. self._processLoops()
  101. self._processConditions()
  102. def _processActions(self):
  103. """!Process model file"""
  104. for action in self.root.findall('action'):
  105. pos, size = self._getDim(action)
  106. disabled = False
  107. task = action.find('task')
  108. if task is not None:
  109. if task.find('disabled') is not None:
  110. disabled = True
  111. task = self._processTask(task)
  112. else:
  113. task = None
  114. aId = int(action.get('id', -1))
  115. self.actions.append({ 'pos' : pos,
  116. 'size' : size,
  117. 'task' : task,
  118. 'id' : aId,
  119. 'disabled' : disabled })
  120. def _getDim(self, node):
  121. """!Get position and size of shape"""
  122. pos = size = None
  123. posAttr = node.get('pos', None)
  124. if posAttr:
  125. posVal = map(int, posAttr.split(','))
  126. try:
  127. pos = (posVal[0], posVal[1])
  128. except:
  129. pos = None
  130. sizeAttr = node.get('size', None)
  131. if sizeAttr:
  132. sizeVal = map(int, sizeAttr.split(','))
  133. try:
  134. size = (sizeVal[0], sizeVal[1])
  135. except:
  136. size = None
  137. return pos, size
  138. def _processData(self):
  139. """!Process model file"""
  140. for data in self.root.findall('data'):
  141. pos, size = self._getDim(data)
  142. param = data.find('data-parameter')
  143. prompt = value = None
  144. if param is not None:
  145. prompt = param.get('prompt', None)
  146. value = self._filterValue(self._getNodeText(param, 'value'))
  147. if data.find('intermediate') is None:
  148. intermediate = False
  149. else:
  150. intermediate = True
  151. rels = list()
  152. for rel in data.findall('relation'):
  153. defrel = { 'id' : int(rel.get('id', -1)),
  154. 'dir' : rel.get('dir', 'to'),
  155. 'name' : rel.get('name', '') }
  156. points = list()
  157. for point in rel.findall('point'):
  158. x = self._filterValue(self._getNodeText(point, 'x'))
  159. y = self._filterValue(self._getNodeText(point, 'y'))
  160. points.append((float(x), float(y)))
  161. defrel['points'] = points
  162. rels.append(defrel)
  163. self.data.append({ 'pos' : pos,
  164. 'size': size,
  165. 'prompt' : prompt,
  166. 'value' : value,
  167. 'intermediate' : intermediate,
  168. 'rels' : rels })
  169. def _processTask(self, node):
  170. """!Process task
  171. @return grassTask instance
  172. @return None on error
  173. """
  174. cmd = list()
  175. parameterized = list()
  176. name = node.get('name', None)
  177. if not name:
  178. return None
  179. cmd.append(name)
  180. # flags
  181. for f in node.findall('flag'):
  182. flag = f.get('name', '')
  183. if f.get('parameterized', '0') == '1':
  184. parameterized.append(('flag', flag))
  185. if f.get('value', '1') == '0':
  186. continue
  187. if len(flag) > 1:
  188. cmd.append('--' + flag)
  189. else:
  190. cmd.append('-' + flag)
  191. # parameters
  192. for p in node.findall('parameter'):
  193. name = p.get('name', '')
  194. if p.find('parameterized') is not None:
  195. parameterized.append(('param', name))
  196. cmd.append('%s=%s' % (name,
  197. self._filterValue(self._getNodeText(p, 'value'))))
  198. task, err = GUI(show = None, checkError = True).ParseCommand(cmd = cmd)
  199. if err:
  200. GWarning(os.linesep.join(err))
  201. for opt, name in parameterized:
  202. if opt == 'flag':
  203. task.set_flag(name, True, element = 'parameterized')
  204. else:
  205. task.set_param(name, True, element = 'parameterized')
  206. return task
  207. def _processLoops(self):
  208. """!Process model loops"""
  209. for node in self.root.findall('loop'):
  210. pos, size = self._getDim(node)
  211. text = self._filterValue(self._getNodeText(node, 'condition')).strip()
  212. aid = list()
  213. for anode in node.findall('item'):
  214. try:
  215. aid.append(int(anode.text))
  216. except ValueError:
  217. pass
  218. self.loops.append({ 'pos' : pos,
  219. 'size' : size,
  220. 'text' : text,
  221. 'id' : int(node.get('id', -1)),
  222. 'items' : aid })
  223. def _processConditions(self):
  224. """!Process model conditions"""
  225. for node in self.root.findall('if-else'):
  226. pos, size = self._getDim(node)
  227. text = self._filterValue(self._getNodeText(node, 'condition')).strip()
  228. aid = { 'if' : list(),
  229. 'else' : list() }
  230. for b in aid.keys():
  231. bnode = node.find(b)
  232. if bnode is None:
  233. continue
  234. for anode in bnode.findall('item'):
  235. try:
  236. aid[b].append(int(anode.text))
  237. except ValueError:
  238. pass
  239. self.conditions.append({ 'pos' : pos,
  240. 'size' : size,
  241. 'text' : text,
  242. 'id' : int(node.get('id', -1)),
  243. 'items' : aid })
  244. class WriteModelFile:
  245. """!Generic class for writing model file"""
  246. def __init__(self, fd, model):
  247. self.fd = fd
  248. self.model = model
  249. self.properties = model.GetProperties()
  250. self.variables = model.GetVariables()
  251. self.items = model.GetItems()
  252. self.indent = 0
  253. self._header()
  254. self._window()
  255. self._properties()
  256. self._variables()
  257. self._items()
  258. dataList = list()
  259. for action in model.GetItems(objType = ModelAction):
  260. for rel in action.GetRelations():
  261. dataItem = rel.GetData()
  262. if dataItem not in dataList:
  263. dataList.append(dataItem)
  264. self._data(dataList)
  265. self._footer()
  266. def _filterValue(self, value):
  267. """!Make value XML-valid"""
  268. value = value.replace('<', '&lt;')
  269. value = value.replace('>', '&gt;')
  270. return value
  271. def _header(self):
  272. """!Write header"""
  273. self.fd.write('<?xml version="1.0" encoding="UTF-8"?>\n')
  274. self.fd.write('<!DOCTYPE gxm SYSTEM "grass-gxm.dtd">\n')
  275. self.fd.write('%s<gxm>\n' % (' ' * self.indent))
  276. self.indent += 4
  277. def _footer(self):
  278. """!Write footer"""
  279. self.indent -= 4
  280. self.fd.write('%s</gxm>\n' % (' ' * self.indent))
  281. def _window(self):
  282. """!Write window properties"""
  283. canvas = self.model.GetCanvas()
  284. if canvas is None:
  285. return
  286. win = canvas.parent
  287. pos = win.GetPosition()
  288. size = win.GetSize()
  289. self.fd.write('%s<window pos="%d,%d" size="%d,%d" />\n' % \
  290. (' ' * self.indent, pos[0], pos[1], size[0], size[1]))
  291. def _properties(self):
  292. """!Write model properties"""
  293. self.fd.write('%s<properties>\n' % (' ' * self.indent))
  294. self.indent += 4
  295. if self.properties['name']:
  296. self.fd.write('%s<name>%s</name>\n' % (' ' * self.indent, self.properties['name']))
  297. if self.properties['description']:
  298. self.fd.write('%s<description>%s</description>\n' % (' ' * self.indent,
  299. EncodeString(self.properties['description'])))
  300. if self.properties['author']:
  301. self.fd.write('%s<author>%s</author>\n' % (' ' * self.indent,
  302. EncodeString(self.properties['author'])))
  303. if 'overwrite' in self.properties and \
  304. self.properties['overwrite']:
  305. self.fd.write('%s<flag name="overwrite" />\n' % (' ' * self.indent))
  306. self.indent -= 4
  307. self.fd.write('%s</properties>\n' % (' ' * self.indent))
  308. def _variables(self):
  309. """!Write model variables"""
  310. if not self.variables:
  311. return
  312. self.fd.write('%s<variables>\n' % (' ' * self.indent))
  313. self.indent += 4
  314. for name, values in self.variables.iteritems():
  315. self.fd.write('%s<variable name="%s" type="%s">\n' % \
  316. (' ' * self.indent, name, values['type']))
  317. self.indent += 4
  318. if 'value' in values:
  319. self.fd.write('%s<value>%s</value>\n' % \
  320. (' ' * self.indent, values['value']))
  321. if 'description' in values:
  322. self.fd.write('%s<description>%s</description>\n' % \
  323. (' ' * self.indent, values['description']))
  324. self.indent -= 4
  325. self.fd.write('%s</variable>\n' % (' ' * self.indent))
  326. self.indent -= 4
  327. self.fd.write('%s</variables>\n' % (' ' * self.indent))
  328. def _items(self):
  329. """!Write actions/loops/conditions"""
  330. for item in self.items:
  331. if isinstance(item, ModelAction):
  332. self._action(item)
  333. elif isinstance(item, ModelLoop):
  334. self._loop(item)
  335. elif isinstance(item, ModelCondition):
  336. self._condition(item)
  337. def _action(self, action):
  338. """!Write actions"""
  339. self.fd.write('%s<action id="%d" name="%s" pos="%d,%d" size="%d,%d">\n' % \
  340. (' ' * self.indent, action.GetId(), action.GetName(), action.GetX(), action.GetY(),
  341. action.GetWidth(), action.GetHeight()))
  342. self.indent += 4
  343. self.fd.write('%s<task name="%s">\n' % (' ' * self.indent, action.GetLog(string = False)[0]))
  344. self.indent += 4
  345. if not action.IsEnabled():
  346. self.fd.write('%s<disabled />\n' % (' ' * self.indent))
  347. for key, val in action.GetParams().iteritems():
  348. if key == 'flags':
  349. for f in val:
  350. if f.get('value', False) or f.get('parameterized', False):
  351. if f.get('parameterized', False):
  352. if f.get('value', False) == False:
  353. self.fd.write('%s<flag name="%s" value="0" parameterized="1" />\n' %
  354. (' ' * self.indent, f.get('name', '')))
  355. else:
  356. self.fd.write('%s<flag name="%s" parameterized="1" />\n' %
  357. (' ' * self.indent, f.get('name', '')))
  358. else:
  359. self.fd.write('%s<flag name="%s" />\n' %
  360. (' ' * self.indent, f.get('name', '')))
  361. else: # parameter
  362. for p in val:
  363. if not p.get('value', '') and not p.get('parameterized', False):
  364. continue
  365. self.fd.write('%s<parameter name="%s">\n' %
  366. (' ' * self.indent, p.get('name', '')))
  367. self.indent += 4
  368. if p.get('parameterized', False):
  369. self.fd.write('%s<parameterized />\n' % (' ' * self.indent))
  370. self.fd.write('%s<value>%s</value>\n' %
  371. (' ' * self.indent, self._filterValue(p.get('value', ''))))
  372. self.indent -= 4
  373. self.fd.write('%s</parameter>\n' % (' ' * self.indent))
  374. self.indent -= 4
  375. self.fd.write('%s</task>\n' % (' ' * self.indent))
  376. self.indent -= 4
  377. self.fd.write('%s</action>\n' % (' ' * self.indent))
  378. def _data(self, dataList):
  379. """!Write data"""
  380. for data in dataList:
  381. self.fd.write('%s<data pos="%d,%d" size="%d,%d">\n' % \
  382. (' ' * self.indent, data.GetX(), data.GetY(),
  383. data.GetWidth(), data.GetHeight()))
  384. self.indent += 4
  385. self.fd.write('%s<data-parameter prompt="%s">\n' % \
  386. (' ' * self.indent, data.GetPrompt()))
  387. self.indent += 4
  388. self.fd.write('%s<value>%s</value>\n' %
  389. (' ' * self.indent, self._filterValue(data.GetValue())))
  390. self.indent -= 4
  391. self.fd.write('%s</data-parameter>\n' % (' ' * self.indent))
  392. if data.IsIntermediate():
  393. self.fd.write('%s<intermediate />\n' % (' ' * self.indent))
  394. # relations
  395. for ft in ('from', 'to'):
  396. for rel in data.GetRelations(ft):
  397. if ft == 'from':
  398. aid = rel.GetTo().GetId()
  399. else:
  400. aid = rel.GetFrom().GetId()
  401. self.fd.write('%s<relation dir="%s" id="%d" name="%s">\n' % \
  402. (' ' * self.indent, ft, aid, rel.GetName()))
  403. self.indent += 4
  404. for point in rel.GetLineControlPoints()[1:-1]:
  405. self.fd.write('%s<point>\n' % (' ' * self.indent))
  406. self.indent += 4
  407. x, y = point.Get()
  408. self.fd.write('%s<x>%d</x>\n' % (' ' * self.indent, int(x)))
  409. self.fd.write('%s<y>%d</y>\n' % (' ' * self.indent, int(y)))
  410. self.indent -= 4
  411. self.fd.write('%s</point>\n' % (' ' * self.indent))
  412. self.indent -= 4
  413. self.fd.write('%s</relation>\n' % (' ' * self.indent))
  414. self.indent -= 4
  415. self.fd.write('%s</data>\n' % (' ' * self.indent))
  416. def _loop(self, loop):
  417. """!Write loops"""
  418. self.fd.write('%s<loop id="%d" pos="%d,%d" size="%d,%d">\n' % \
  419. (' ' * self.indent, loop.GetId(), loop.GetX(), loop.GetY(),
  420. loop.GetWidth(), loop.GetHeight()))
  421. text = loop.GetText()
  422. self.indent += 4
  423. if text:
  424. self.fd.write('%s<condition>%s</condition>\n' %
  425. (' ' * self.indent, self._filterValue(text)))
  426. for item in loop.GetItems():
  427. self.fd.write('%s<item>%d</item>\n' %
  428. (' ' * self.indent, item.GetId()))
  429. self.indent -= 4
  430. self.fd.write('%s</loop>\n' % (' ' * self.indent))
  431. def _condition(self, condition):
  432. """!Write conditions"""
  433. bbox = condition.GetBoundingBoxMin()
  434. self.fd.write('%s<if-else id="%d" pos="%d,%d" size="%d,%d">\n' % \
  435. (' ' * self.indent, condition.GetId(), condition.GetX(), condition.GetY(),
  436. bbox[0], bbox[1]))
  437. text = condition.GetText()
  438. self.indent += 4
  439. if text:
  440. self.fd.write('%s<condition>%s</condition>\n' %
  441. (' ' * self.indent, self._filterValue(text)))
  442. items = condition.GetItems()
  443. for b in items.keys():
  444. if len(items[b]) < 1:
  445. continue
  446. self.fd.write('%s<%s>\n' % (' ' * self.indent, b))
  447. self.indent += 4
  448. for item in items[b]:
  449. self.fd.write('%s<item>%d</item>\n' %
  450. (' ' * self.indent, item.GetId()))
  451. self.indent -= 4
  452. self.fd.write('%s</%s>\n' % (' ' * self.indent, b))
  453. self.indent -= 4
  454. self.fd.write('%s</if-else>\n' % (' ' * self.indent))
  455. class WritePythonFile:
  456. def __init__(self, fd, model):
  457. """!Class for exporting model to Python script
  458. @param fd file desciptor
  459. """
  460. self.fd = fd
  461. self.model = model
  462. self.indent = 4
  463. self._writePython()
  464. def _writePython(self):
  465. """!Write model to file"""
  466. properties = self.model.GetProperties()
  467. self.fd.write(
  468. r"""#!/usr/bin/env python
  469. #
  470. ############################################################################
  471. #
  472. # MODULE: %s
  473. #
  474. # AUTHOR(S): %s
  475. #
  476. # PURPOSE: %s
  477. #
  478. # DATE: %s
  479. #
  480. #############################################################################
  481. """ % (properties['name'],
  482. properties['author'],
  483. properties['description'],
  484. time.asctime()))
  485. self.fd.write(
  486. r"""
  487. import sys
  488. import os
  489. import atexit
  490. import grass.script as grass
  491. """)
  492. # cleanup()
  493. rast, vect, rast3d, msg = self.model.GetIntermediateData()
  494. self.fd.write(
  495. r"""
  496. def cleanup():
  497. """)
  498. if rast:
  499. self.fd.write(
  500. r""" grass.run_command('g.remove',
  501. rast=%s)
  502. """ % ','.join(map(lambda x: "'" + x + "'", rast)))
  503. if vect:
  504. self.fd.write(
  505. r""" grass.run_command('g.remove',
  506. vect = %s)
  507. """ % ','.join(map(lambda x: "'" + x + "'", vect)))
  508. if rast3d:
  509. self.fd.write(
  510. r""" grass.run_command('g.remove',
  511. rast3d = %s)
  512. """ % ','.join(map(lambda x: "'" + x + "'", rast3d)))
  513. if not rast and not vect and not rast3d:
  514. self.fd.write(' pass\n')
  515. self.fd.write("\ndef main():\n")
  516. for item in self.model.GetItems():
  517. self._writePythonItem(item)
  518. self.fd.write("\n return 0\n")
  519. self.fd.write(
  520. r"""
  521. if __name__ == "__main__":
  522. options, flags = grass.parser()
  523. atexit.register(cleanup)
  524. sys.exit(main())
  525. """)
  526. def _writePythonItem(self, item, ignoreBlock = True, variables = []):
  527. """!Write model object to Python file"""
  528. if isinstance(item, ModelAction):
  529. if ignoreBlock and item.GetBlockId(): # ignore items in loops of conditions
  530. return
  531. self._writePythonAction(item, variables = variables)
  532. elif isinstance(item, ModelLoop) or isinstance(item, ModelCondition):
  533. # substitute condition
  534. variables = self.model.GetVariables()
  535. cond = item.GetText()
  536. for variable in variables:
  537. pattern = re.compile('%' + variable)
  538. if pattern.search(cond):
  539. value = variables[variable].get('value', '')
  540. if variables[variable].get('type', 'string') == 'string':
  541. value = '"' + value + '"'
  542. cond = pattern.sub(value, cond)
  543. if isinstance(item, ModelLoop):
  544. condVar, condText = map(lambda x: x.strip(), re.split('\s*in\s*', cond))
  545. cond = "%sfor %s in " % (' ' * self.indent, condVar)
  546. if condText[0] == '`' and condText[-1] == '`':
  547. task = GUI(show = None).ParseCommand(cmd = utils.split(condText[1:-1]))
  548. cond += "grass.read_command("
  549. cond += self._getPythonActionCmd(task, len(cond), variables = [condVar]) + ".splitlines()"
  550. else:
  551. cond += condText
  552. self.fd.write('%s:\n' % cond)
  553. self.indent += 4
  554. for action in item.GetItems():
  555. self._writePythonItem(action, ignoreBlock = False, variables = [condVar])
  556. self.indent -= 4
  557. else: # ModelCondition
  558. self.fd.write('%sif %s:\n' % (' ' * self.indent, cond))
  559. self.indent += 4
  560. condItems = item.GetItems()
  561. for action in condItems['if']:
  562. self._writePythonItem(action, ignoreBlock = False)
  563. if condItems['else']:
  564. self.indent -= 4
  565. self.fd.write('%selse:\n' % (' ' * self.indent))
  566. self.indent += 4
  567. for action in condItems['else']:
  568. self._writePythonItem(action, ignoreBlock = False)
  569. self.indent += 4
  570. def _writePythonAction(self, item, variables = []):
  571. """!Write model action to Python file"""
  572. task = GUI(show = None).ParseCommand(cmd = item.GetLog(string = False))
  573. strcmd = "%sgrass.run_command(" % (' ' * self.indent)
  574. self.fd.write(strcmd + self._getPythonActionCmd(task, len(strcmd), variables) + '\n')
  575. def _getPythonActionCmd(self, task, cmdIndent, variables = []):
  576. opts = task.get_options()
  577. ret = ''
  578. flags = ''
  579. params = list()
  580. for f in opts['flags']:
  581. if f.get('value', False):
  582. name = f.get('name', '')
  583. if len(name) > 1:
  584. params.append('%s = True' % name)
  585. else:
  586. flags += name
  587. for p in opts['params']:
  588. name = p.get('name', None)
  589. value = p.get('value', None)
  590. if name and value:
  591. ptype = p.get('type', 'string')
  592. if value[0] == '%':
  593. params.append("%s = %s" % (name, value[1:]))
  594. elif ptype == 'string':
  595. params.append('%s = "%s"' % (name, value))
  596. else:
  597. params.append("%s = %s" % (name, value))
  598. ret += '"%s"' % task.get_name()
  599. if flags:
  600. ret += ",\n%sflags = '%s'" % (' ' * cmdIndent, flags)
  601. if len(params) > 0:
  602. ret += ",\n"
  603. for opt in params[:-1]:
  604. ret += "%s%s,\n" % (' ' * cmdIndent, opt)
  605. ret += "%s%s)" % (' ' * cmdIndent, params[-1])
  606. else:
  607. ret += ")"
  608. return ret