modelverse.py 31 KB

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