modelverse.py 29 KB

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