modelverse.py 30 KB

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