modelverse.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. import urllib
  2. import urllib2
  3. import json
  4. import random
  5. from urllib2 import URLError
  6. import sys
  7. COMPILER_PATH = "interface/HUTN"
  8. MODE_UNCONNECTED = 0
  9. MODE_UNAUTHORIZED = 1
  10. MODE_MODELLING = 2
  11. MODE_MODIFY = 3
  12. MODE_DIALOG = 4
  13. MODE_MANUAL = 5
  14. # Bind to the compiler (might have to update path manually!)
  15. sys.path.append(COMPILER_PATH)
  16. from hutn_compiler.compiler import main as do_compile
  17. # Exceptions
  18. class ModelverseException(Exception):
  19. pass
  20. class UnknownError(ModelverseException):
  21. pass
  22. class UnknownIdentifier(ModelverseException):
  23. pass
  24. class UnknownType(ModelverseException):
  25. pass
  26. class NotAnAssociation(ModelverseException):
  27. pass
  28. class UnsupportedValue(ModelverseException):
  29. pass
  30. class CompilationError(ModelverseException):
  31. pass
  32. class NoSuchAttribute(ModelverseException):
  33. pass
  34. class UnknownModel(ModelverseException):
  35. pass
  36. class ConnectionError(ModelverseException):
  37. pass
  38. class ModelExists(ModelverseException):
  39. pass
  40. class PermissionDenied(ModelverseException):
  41. pass
  42. class InvalidMode(ModelverseException):
  43. pass
  44. class InterfaceMismatch(ModelverseException):
  45. pass
  46. # Helper functions and configuration: do not use yourself!
  47. taskname = random.random()
  48. address = None
  49. last_output = None
  50. mode = MODE_UNCONNECTED
  51. prev_mode = None
  52. def _input(value):
  53. # Ugly json encoding of primitives
  54. if isinstance(value, type([])):
  55. value = json.dumps(value)
  56. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": value, "taskname": taskname}))).read()
  57. else:
  58. value = json.dumps(value)
  59. #print("Set input: " + str(value))
  60. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
  61. def _input_raw(value, taskname):
  62. # Ugly json encoding of primitives
  63. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
  64. def _compile_AL(code):
  65. # Compile an action language file and send the compiled code
  66. code_fragments = code.split("\n")
  67. code_fragments = [i for i in code_fragments if i.strip() != ""]
  68. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  69. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  70. code_fragments = [i[initial_tabs:] for i in code_fragments]
  71. code_fragments.append("")
  72. code = "\n".join(code_fragments)
  73. with open("__constraint.alc", "w") as f:
  74. f.write(code)
  75. f.flush()
  76. return do_compile("__constraint.alc", COMPILER_PATH + "/grammars/actionlanguage.g", "CS")
  77. def _compile_model(code):
  78. # Compile a model and send the compiled graph
  79. # First change multiple spaces to a tab
  80. code_fragments = code.split("\n")
  81. code_fragments = [i for i in code_fragments if i.strip() != ""]
  82. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  83. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  84. code_fragments = [i[initial_tabs:] for i in code_fragments]
  85. code_fragments.append("")
  86. code = "\n".join(code_fragments)
  87. with open("__model.mvc", "w") as f:
  88. f.write(code)
  89. f.flush()
  90. return do_compile("__model.mvc", COMPILER_PATH + "/grammars/modelling.g", "M") + ["exit"]
  91. def _output(expected=None):
  92. try:
  93. global last_output
  94. last_output = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": taskname}))).read())
  95. except:
  96. raise UnknownError()
  97. if expected is not None and last_output != expected:
  98. raise InterfaceMismatch(_last_output(), expected)
  99. return last_output
  100. def _last_output():
  101. return last_output
  102. # Raise common exceptions
  103. def _handle_output(requested=None, split=None):
  104. value = _output()
  105. if value.startswith("Model exists: "):
  106. raise ModelExists(value.split(": ", 1)[1])
  107. elif value.startswith("Permission denied"):
  108. raise PermissionDenied(value.split(": ", 1)[1])
  109. elif value.startswith("Model not found: "):
  110. raise UnknownModel(value.split(": ", 1)[1])
  111. elif value.startswith("Element not found: "):
  112. raise UnknownIdentifier(value.split(": ", 1)[1])
  113. elif value.startswith("Element exists: "):
  114. raise ElementExists(value.split(": ", 1)[1])
  115. elif value.startswith("Attribute not found: "):
  116. raise NoSuchAttribute(value.split(": ", 1)[1])
  117. elif requested is not None and value.startswith(requested):
  118. if split is None:
  119. return value
  120. else:
  121. splitted = value.strip().split(split, 1)
  122. if len(splitted) == 1:
  123. return ""
  124. else:
  125. return splitted[1].rstrip()
  126. else:
  127. raise InterfaceMismatch(value)
  128. # Main MvC operations
  129. def init(address_param="http://127.0.0.1:8001"):
  130. """Starts up the connection to the Modelverse."""
  131. # return None
  132. # raises ConnectionError
  133. # raises UnknownError
  134. # raises InvalidMode
  135. global mode
  136. if mode != MODE_UNCONNECTED:
  137. raise InvalidMode()
  138. global address
  139. address = address_param
  140. try:
  141. _input_raw('"%s"' % taskname, "task_manager")
  142. mode = MODE_UNAUTHORIZED
  143. except URLError as e:
  144. raise ConnectionError(e.reason)
  145. def login(username, password):
  146. """Log in a user, if user doesn't exist, it is created."""
  147. # return None
  148. # raises UnknownError
  149. # raises PermissionDenied
  150. # raises InterfaceMismatch
  151. global mode
  152. if mode != MODE_UNAUTHORIZED:
  153. raise InvalidMode()
  154. _output("Log on as which user?")
  155. _input(username)
  156. if _output() == "Password for existing user?":
  157. _input(password)
  158. if _output() == "Welcome to the Model Management Interface v2.0!":
  159. _output("Use the 'help' command for a list of possible commands")
  160. _input("quiet")
  161. mode = MODE_MODELLING
  162. elif _last_output() == "Wrong password!":
  163. raise PermissionDenied()
  164. else:
  165. raise InterfaceMismatch(_last_output())
  166. elif _last_output() == "This is a new user: please give password!":
  167. _input(password)
  168. _output("Please repeat the password")
  169. _input(password)
  170. if _output() == "Passwords match!":
  171. _output("Welcome to the Model Management Interface v2.0!")
  172. _output("Use the 'help' command for a list of possible commands")
  173. _input("quiet")
  174. mode = MODE_MODELLING
  175. elif _last_output() == "Not the same password!":
  176. # We just sent the same password, so it should be identical, unless the interface changed
  177. raise InterfaceMismatch(_last_output())
  178. else:
  179. raise InterfaceMismatch(_last_output())
  180. else:
  181. raise InterfaceMismatch(_last_output())
  182. def model_add(model_name, metamodel_name, model_code=None):
  183. """Instantiate a new model."""
  184. # return None
  185. # raises UnknownModel
  186. # raises ModelExists
  187. # raises UnknownError
  188. # raises PermissionDenied
  189. # raises CompilationError
  190. if mode != MODE_MODELLING:
  191. raise InvalidMode()
  192. # Do this before creating the model, as otherwise compilation errors would make us inconsistent
  193. if model_code is not None:
  194. try:
  195. compiled = _compile_model(model_code)
  196. except:
  197. raise CompilationError()
  198. else:
  199. compiled = ["exit"]
  200. _input(["model_add", metamodel_name, model_name])
  201. _handle_output("Waiting for model constructors...")
  202. _input(compiled)
  203. _output("Success")
  204. def model_modify(model_name):
  205. """Modify an existing model."""
  206. # return is_write
  207. # raises UnknownModel
  208. # raises PermissionDenied
  209. # raises UnknownError
  210. global mode
  211. global prev_mode
  212. if mode == MODE_MANUAL:
  213. prev_mode = MODE_MANUAL
  214. mode = MODE_MODIFY
  215. return None
  216. if mode != MODE_MODELLING:
  217. raise InvalidMode()
  218. prev_mode = MODE_MODELLING
  219. _input(["model_modify", model_name])
  220. _handle_output("Success")
  221. # Mode has changed
  222. mode = MODE_MODIFY
  223. _output("Model loaded, ready for commands!")
  224. if ("r/w" in _output()):
  225. write = True
  226. else:
  227. write = False
  228. _output("Use 'help' command for a list of possible commands")
  229. return write
  230. def model_list():
  231. """List all models."""
  232. # return [(model1, metamodel1), (model2, metamodel2), ...]
  233. # raises UnknownError
  234. if mode != MODE_MODELLING:
  235. raise InvalidMode()
  236. _input("model_list")
  237. output = _handle_output("Success: ", split=" ")
  238. if output == "":
  239. return set([])
  240. lst = set([])
  241. value = output.strip().split("\n")
  242. for v in value:
  243. m, mm = v.split(":")
  244. m = m.strip()
  245. mm = mm.strip()
  246. lst.add((m, mm))
  247. return lst
  248. def model_list_full():
  249. """List full information on all models."""
  250. # return [(model1, metamodel1, owner1, group1, permissions1), (model2, metamodel2, owner2, group2, permissions2), ...]
  251. # raises UnknownError
  252. if mode != MODE_MODELLING:
  253. raise InvalidMode()
  254. _input("model_list_full")
  255. output = _handle_output("Success: ", split=" ")
  256. if output == "":
  257. return set([])
  258. lst = set([])
  259. value = output.strip().split("\n")
  260. for v in value:
  261. print("READ " + v)
  262. m, mm = v.split(":")
  263. m = m.strip()
  264. mm = mm.strip()
  265. perm, own, grp, m = m.split(" ")
  266. lst.add((m, mm, own, grp, perm))
  267. return lst
  268. def verify(model):
  269. """Verify if a model conforms to its metamodel."""
  270. # return "verification_result"
  271. # raises UnknownError
  272. # raises UnknownModel
  273. if mode != MODE_MODELLING:
  274. raise InvalidMode()
  275. _input(["verify", model])
  276. return _handle_output("Success: ", split=" ")
  277. def model_overwrite(model_name, new_model=None):
  278. """Upload a new model and overwrite an existing model."""
  279. # return None
  280. # raises UnknownModel
  281. # raises PermissionDenied
  282. # raises CompilationError
  283. # raises UnknownError
  284. if mode != MODE_MODELLING:
  285. raise InvalidMode()
  286. if new_model is not None:
  287. try:
  288. compiled = _compile_model(new_model)
  289. except Exception as e:
  290. raise CompilationError(e)
  291. else:
  292. compiled = ["exit"]
  293. _input(["model_overwrite", model_name])
  294. _handle_output("Waiting for model constructors...")
  295. _input(compiled)
  296. _output("Success")
  297. def user_logout():
  298. """Log out the current user and break the connection."""
  299. # return None
  300. # raises UnknownError
  301. global mode
  302. if mode != MODE_MODELLING:
  303. raise InvalidMode()
  304. _input("exit")
  305. mode = MODE_UNCONNECTED
  306. def user_delete():
  307. """Removes the current user and break the connection."""
  308. # return None
  309. # raises UnknownError
  310. global mode
  311. if mode != MODE_MODELLING:
  312. raise InvalidMode()
  313. _input("self-destruct")
  314. mode = MODE_UNCONNECTED
  315. def model_render(model, mapper):
  316. """Fetch a rendered verion of a model."""
  317. # return JSON_representation
  318. # raises UnknownError
  319. # raises UnknownIdentifier
  320. # raises InterfaceMismatch
  321. global mode
  322. if mode != MODE_MODELLING:
  323. raise InvalidMode()
  324. _input(["model_render", model, mapper])
  325. return _handle_output("Success: ", split=" ")
  326. def transformation_between(source, target):
  327. global mode
  328. if mode != MODE_MODELLING:
  329. raise InvalidMode()
  330. _input(["transformation_between", source, target])
  331. output = _handle_output("Success: ", split=" ")
  332. if output == "":
  333. return set([])
  334. lst = set([v for v in output.split("\n")])
  335. def transformation_add_MT_language(metamodels, RAMified_name):
  336. """Create a new Model Transformation language out of a set of metamodels."""
  337. global mode
  338. if mode != MODE_MODELLING:
  339. raise InvalidMode()
  340. _input(["transformation_add_MT_language"] + metamodels + ["", RAMified_name])
  341. _handle_output("Success")
  342. def transformation_add_MT(RAMified_metamodel, source_metamodels, target_metamodels, operation_name, code):
  343. """Create a new model transformation."""
  344. global mode
  345. if mode != MODE_MODELLING:
  346. raise InvalidMode()
  347. try:
  348. compiled = _compile_model(code)
  349. except Exception as e:
  350. raise CompilationError(e)
  351. _input(["transformation_add_MT", RAMified_metamodel] + source_metamodels + [""] + target_metamodels + ["", operation_name])
  352. _handle_output("Waiting for model constructors...")
  353. _input(compiled)
  354. _handle_output("Success")
  355. def transformation_add_AL(source_metamodels, target_metamodels, operation_name, code):
  356. """Create a new action language model, which can be executed."""
  357. global mode
  358. if mode != MODE_MODELLING:
  359. raise InvalidMode()
  360. try:
  361. compiled = _compile_AL(code)
  362. except Exception as e:
  363. raise CompilationError(e)
  364. _input(["transformation_add_AL"] + source_metamodels + [""] + target_metamodels + [""] + [operation_name])
  365. _handle_output("Waiting for code constructors...")
  366. _input(compiled)
  367. _output("Success")
  368. def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name):
  369. """Create a new manual model operation."""
  370. global mode
  371. if mode != MODE_MODELLING:
  372. raise InvalidMode()
  373. _input(["transformation_add_MANUAL"] + source_metamodels + [""] + target_metamodels + [""] + [operation_name])
  374. _handle_output("Success")
  375. def transformation_execute_AL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  376. """Execute an existing model operation."""
  377. global mode
  378. if mode != MODE_MODELLING:
  379. raise InvalidMode()
  380. mv_dict_rep = []
  381. for key, value in input_models_dict.items():
  382. mv_dict_rep += [key, value]
  383. mv_dict_rep += [""]
  384. for key, value in output_models_dict.items():
  385. mv_dict_rep += [key, value]
  386. mv_dict_rep += [""]
  387. _input(["transformation_execute", operation_name] + mv_dict_rep)
  388. _handle_output("Success: ready for AL execution")
  389. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  390. while _output() not in ["Success", "Failure"]:
  391. mode = MODE_DIALOG
  392. reply = callback(_last_output())
  393. mode = MODE_MODELLING
  394. if reply is not None:
  395. _input(reply)
  396. # Got termination message, so we are done!
  397. if _last_output() == "Success":
  398. return True
  399. else:
  400. return False
  401. def transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  402. """Execute an existing model operation."""
  403. global mode
  404. if mode != MODE_MODELLING:
  405. raise InvalidMode()
  406. mv_dict_rep = []
  407. for key, value in input_models_dict.items():
  408. mv_dict_rep += [key, value]
  409. mv_dict_rep += [""]
  410. for key, value in output_models_dict.items():
  411. mv_dict_rep += [key, value]
  412. mv_dict_rep += [""]
  413. _input(["transformation_execute", operation_name] + mv_dict_rep)
  414. _handle_output("Success: ready for MANUAL execution")
  415. # Skip over the begin of mini_modify
  416. _output()
  417. _output()
  418. _output()
  419. _output()
  420. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  421. mode = MODE_MANUAL
  422. callback()
  423. # Finished, so leave
  424. _input("exit")
  425. mode = MODE_MODELLING
  426. # Got termination message, so we are done!
  427. if _output() == "Success":
  428. return True
  429. else:
  430. return False
  431. def transformation_execute_MT(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  432. """Execute an existing model operation."""
  433. global mode
  434. if mode != MODE_MODELLING:
  435. raise InvalidMode()
  436. mv_dict_rep = []
  437. for key, value in input_models_dict.items():
  438. mv_dict_rep += [key, value]
  439. mv_dict_rep += [""]
  440. for key, value in output_models_dict.items():
  441. mv_dict_rep += [key, value]
  442. mv_dict_rep += [""]
  443. _input(["transformation_execute", operation_name] + mv_dict_rep)
  444. _handle_output("Success: ready for MT execution")
  445. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  446. while _output() not in ["Success", "Failure"]:
  447. reply = callback(_last_output())
  448. if reply is not None:
  449. _input(reply)
  450. # Got termination message, so we are done!
  451. if _last_output() == "Success":
  452. return True
  453. else:
  454. return False
  455. def transformation_list():
  456. """List existing model operations."""
  457. global mode
  458. if mode != MODE_MODELLING:
  459. raise InvalidMode()
  460. _input("transformation_list")
  461. output = _handle_output("Success: ", split=" ")
  462. if output == "":
  463. return set([])
  464. lst = set([])
  465. value = output.strip().split("\n")
  466. for v in value:
  467. t, m = v.strip().split(" ", 1)
  468. t = t[1:-1].strip()
  469. m = m.strip().split(":")[0].strip()
  470. lst.add((t, m))
  471. return lst
  472. def transformation_detail():
  473. """List full details of a a model operation."""
  474. raise NotImplementedError()
  475. def transformation_RAMify(metamodel_name, RAMified_metamodel_name):
  476. """Ramify an existing metamodel."""
  477. global mode
  478. if mode != MODE_MODELLING:
  479. raise InvalidMode()
  480. _input(["transformation_RAMify", metamodel_name, RAMified_metamodel_name])
  481. _handle_output("Success")
  482. def process_execute():
  483. """Execute a process model."""
  484. raise NotImplementedError()
  485. def permission_modify():
  486. """Modify permissions of a model."""
  487. raise NotImplementedError()
  488. def permission_owner():
  489. """Modify the owning user of a model."""
  490. raise NotImplementedError()
  491. def permission_group():
  492. """Modify the owning group of a model."""
  493. raise NotImplementedError()
  494. def group_create():
  495. """Create a new group."""
  496. raise NotImplementedError()
  497. def group_delete():
  498. """Delete a group of which you are an owner."""
  499. raise NotImplementedError()
  500. def group_owner_add():
  501. """Add a new owning user to a group you own."""
  502. raise NotImplementedError()
  503. def group_owner_delete():
  504. """Delete an owning user to a group you own."""
  505. raise NotImplementedError()
  506. def group_join():
  507. """Add a new user to a group you own."""
  508. raise NotImplementedError()
  509. def group_kick():
  510. """Delete a user from a group you own."""
  511. raise NotImplementedError()
  512. def group_list():
  513. """List existing groups."""
  514. raise NotImplementedError()
  515. def admin_promote():
  516. """Promote a user to admin status."""
  517. raise NotImplementedError()
  518. def admin_demote():
  519. """Demote a user from admin status."""
  520. raise NotImplementedError()
  521. # Actual operations on the model
  522. def element_list(model_name):
  523. """Return a list of all IDs and the type of the element"""
  524. # return [(name1, type1), (name2, type2), ...]
  525. # raises UnknownError
  526. model_modify(model_name)
  527. if mode != MODE_MODIFY:
  528. raise InvalidMode()
  529. try:
  530. _input("list_full")
  531. lst = set([])
  532. output = _handle_output("Success: ", split=" ")
  533. if output == "":
  534. return set([])
  535. for v in output.split("\n"):
  536. m, mm = v.split(":")
  537. m = m.strip()
  538. mm = mm.strip()
  539. lst.add((m, mm))
  540. return lst
  541. finally:
  542. model_exit()
  543. def types(model_name):
  544. """Return a list of all types usable in the model"""
  545. # return [type1, type2, ...]
  546. # raises UnknownError
  547. model_modify(model_name)
  548. if mode != MODE_MODIFY:
  549. raise InvalidMode()
  550. try:
  551. _input("types")
  552. lst = set([])
  553. output = _handle_output("Success: ", split=" ")
  554. if output == "":
  555. return set([])
  556. for v in output.split("\n"):
  557. m, mm = v.split(":")
  558. m = m.strip()
  559. lst.add(m)
  560. return lst
  561. finally:
  562. model_exit()
  563. def types_full(model_name):
  564. """Return a list of full types usable in the model"""
  565. # return [(type1, typetype1), (type2, typetype2), ...]
  566. # raises UnknownError
  567. model_modify(model_name)
  568. if mode != MODE_MODIFY:
  569. raise InvalidMode()
  570. try:
  571. _input("types")
  572. lst = set([])
  573. output = _handle_output("Success: ", split=" ")
  574. if output == "":
  575. return set([])
  576. for v in output.split("\n"):
  577. m, mm = v.split(":")
  578. m = m.strip()
  579. mm = mm.strip()
  580. lst.add((m, mm))
  581. return lst
  582. finally:
  583. model_exit()
  584. def read(model_name, ID):
  585. """Return a tuple of information on the element: its type and source/target (None if not an edge)"""
  586. # return (type, (source, target))
  587. # raises UnknownError
  588. # raises UnknownIdentifier
  589. model_modify(model_name)
  590. if mode != MODE_MODIFY:
  591. raise InvalidMode()
  592. try:
  593. _input(["read", ID])
  594. output = _handle_output("Success: ", split=" ")
  595. v = output.split("\n")
  596. t = v[1].split(":")[1].strip()
  597. if (not v[0].startswith("Source:")):
  598. rval = (t, None)
  599. else:
  600. src = v[0].split(":")[1].strip()
  601. trg = v[1].split(":")[1].strip()
  602. rval = (t, (src, trg))
  603. return rval
  604. finally:
  605. model_exit()
  606. def read_attrs(model_name, ID):
  607. """Return a dictionary of attribute value pairs"""
  608. # return {attr1: value1, attr2: value2, ...}
  609. # raises UnknownError
  610. # raises UnknownIdentifier
  611. model_modify(model_name)
  612. if mode != MODE_MODIFY:
  613. raise InvalidMode()
  614. try:
  615. _input(["read", ID])
  616. output = _handle_output("Success: ", split=" ")
  617. v = output.split("\n")
  618. searching = True
  619. rval = {}
  620. for r in v:
  621. if searching:
  622. if r == "Attributes:":
  623. # Start working on attributes
  624. searching = False
  625. else:
  626. key, value = r.split(":", 1)
  627. _, value = value.split("=", 1)
  628. key = json.loads(key.strip())
  629. value = value.strip()
  630. if value == "None":
  631. value = None
  632. elif value == "True":
  633. value = True
  634. elif value == "False":
  635. value = False
  636. else:
  637. value = json.loads(value)
  638. rval[key] = value
  639. return rval
  640. finally:
  641. model_exit()
  642. def instantiate(model_name, typename, edge=None, ID=""):
  643. """Create a new instance of the specified typename, between the selected elements (if not None), and with the provided ID (if any)"""
  644. # return instantiated_ID
  645. # raises UnknownError
  646. # raises UnknownType
  647. # raises UnknownIdentifier
  648. # raises NotAnEdge
  649. model_modify(model_name)
  650. if mode != MODE_MODIFY:
  651. raise InvalidMode()
  652. try:
  653. if edge is None:
  654. _input(["instantiate_node", typename, ID])
  655. else:
  656. _input(["instantiate_edge", typename, ID, edge[0], edge[1]])
  657. return _handle_output("Success: ", split=" ")
  658. finally:
  659. model_exit()
  660. def delete_element(model_name, ID):
  661. """Delete the element with the given ID"""
  662. # return None
  663. # raises UnknownError
  664. # raises UnknownIdentifier
  665. model_modify(model_name)
  666. if mode != MODE_MODIFY:
  667. raise InvalidMode()
  668. try:
  669. _input(["delete", ID])
  670. _handle_output("Success")
  671. finally:
  672. model_exit()
  673. def attr_assign(model_name, ID, attr, value):
  674. """Assign a value to an attribute"""
  675. # return None
  676. # raises UnknownError
  677. # raises UnknownIdentifier
  678. # raises NoSuchAttribute
  679. # raises UnsupportedValue
  680. model_modify(model_name)
  681. if mode != MODE_MODIFY:
  682. raise InvalidMode()
  683. try:
  684. _input(["attr_add", ID, attr, value])
  685. _handle_output("Success")
  686. finally:
  687. model_exit()
  688. def attr_assign_code(model_name, ID, attr, code):
  689. """Assign a piece of Action Language code to the attribute"""
  690. # return None
  691. # raises UnknownError
  692. # raises UnknownIdentifier
  693. # raises NoSuchAttribute
  694. # raises UnsupportedValue
  695. try:
  696. compiled = _compile_AL(code)
  697. except Exception as e:
  698. raise CompilationError(e)
  699. model_modify(model_name)
  700. if mode != MODE_MODIFY:
  701. raise InvalidMode()
  702. try:
  703. _input(["attr_add", ID, attr])
  704. _handle_output("Waiting for code constructors...")
  705. _input(compiled)
  706. _output("Success")
  707. finally:
  708. model_exit()
  709. def attr_delete(model_name, ID, attr):
  710. """Remove an attribute."""
  711. model_modify(model_name)
  712. if mode != MODE_MODIFY:
  713. raise InvalidMode()
  714. try:
  715. _input(["attr_del", ID, attr])
  716. _handle_output("Success")
  717. finally:
  718. model_exit()
  719. def read_outgoing(model_name, ID, typename):
  720. """Returns a list of all outgoing associations of a specific type ("" = all)"""
  721. # return [name1, name2, ...]
  722. # raises UnknownError
  723. # raises UnknownIdentifier
  724. model_modify(model_name)
  725. if mode != MODE_MODIFY:
  726. raise InvalidMode()
  727. try:
  728. _input(["read_outgoing", ID, typename])
  729. output = _handle_output("Success: ", split=" ")
  730. if output == "":
  731. return set([])
  732. else:
  733. return set(output.split("\n"))
  734. finally:
  735. model_exit()
  736. def read_incoming(model_name, ID, typename):
  737. """Returns a list of all incoming associations of a specific type ("" = all)"""
  738. # return [name1, name2, ...]
  739. # raises UnknownError
  740. # raises UnknownIdentifier
  741. # raises UnknownType
  742. model_modify(model_name)
  743. if mode != MODE_MODIFY:
  744. raise InvalidMode()
  745. try:
  746. _input(["read_incoming", ID, typename])
  747. output = _handle_output("Success: ", split=" ")
  748. if output == "":
  749. return set([])
  750. else:
  751. return set(output.split("\n"))
  752. finally:
  753. model_exit()
  754. def read_association_source(model_name, ID):
  755. """Returns the source of an association."""
  756. # returns name
  757. # raises UnknownError
  758. # raises UnknownIdentifier
  759. # raises NotAnAssociation
  760. model_modify(model_name)
  761. if mode != MODE_MODIFY:
  762. raise InvalidMode()
  763. try:
  764. _input(["read_association_source", ID])
  765. return _handle_output("Success: ", split=" ")
  766. finally:
  767. model_exit()
  768. def read_association_destination(model_name, ID):
  769. """Returns the destination of an association."""
  770. # returns name
  771. # raises UnknownError
  772. # raises UnknownIdentifier
  773. # raises NotAnAssociation
  774. model_modify(model_name)
  775. if mode != MODE_MODIFY:
  776. raise InvalidMode()
  777. try:
  778. _input(["read_association_destination", ID])
  779. return _handle_output("Success: ", split=" ")
  780. finally:
  781. model_exit()
  782. def model_exit():
  783. """Leave model modify mode."""
  784. # return None
  785. # raises UnknownError
  786. global mode
  787. global prev_mode
  788. if prev_mode == MODE_MANUAL:
  789. mode = MODE_MANUAL
  790. return
  791. if mode != MODE_MODIFY:
  792. raise InvalidMode()
  793. _input("exit")
  794. _output("Success")
  795. mode = MODE_MODELLING