modelverse.py 30 KB

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