model_file.py 26 KB

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