modelverse.py 28 KB

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