modelverse.py 27 KB

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