modelverse.py 30 KB

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