modelverse.py 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  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. def _model_modify(model_name):
  165. """Modify an existing model."""
  166. # raises UnknownModel
  167. # raises PermissionDenied
  168. # raises UnknownError
  169. global mode
  170. global prev_mode
  171. if mode == MODE_MANUAL:
  172. prev_mode = MODE_MANUAL
  173. mode = MODE_MODIFY
  174. return None
  175. _goto_mode(MODE_MODELLING)
  176. prev_mode = MODE_MODELLING
  177. _input(["model_modify", model_name])
  178. _handle_output("Success")
  179. global current_model
  180. current_model = model_name
  181. # Mode has changed
  182. mode = MODE_MODIFY
  183. _output("Model loaded, ready for commands!")
  184. def _model_exit():
  185. """Leave model modify mode."""
  186. # return None
  187. # raises UnknownError
  188. global mode
  189. global prev_mode
  190. if prev_mode == MODE_MANUAL:
  191. mode = MODE_MANUAL
  192. return
  193. if mode != MODE_MODIFY:
  194. raise InvalidMode()
  195. _input("exit")
  196. _output("Success")
  197. mode = MODE_MODELLING
  198. # Main MvC operations
  199. def init(address_param="http://127.0.0.1:8001", timeout=20.0):
  200. """Starts up the connection to the Modelverse."""
  201. # return None
  202. # raises ConnectionError
  203. # raises UnknownError
  204. # raises InvalidMode
  205. global mode
  206. global address
  207. global taskname
  208. address = address_param
  209. start_time = time.time()
  210. taskname = random.random()
  211. while 1:
  212. try:
  213. _input_raw('"%s"' % taskname, "task_manager")
  214. mode = MODE_UNAUTHORIZED
  215. break
  216. except URLError as e:
  217. if time.time() - start_time > timeout:
  218. raise ConnectionError(e.reason)
  219. else:
  220. time.sleep(0.1)
  221. def login(username, password):
  222. """Log in a user, if user doesn't exist, it is created."""
  223. # return None
  224. # raises UnknownError
  225. # raises PermissionDenied
  226. # raises InterfaceMismatch
  227. global mode
  228. _goto_mode(MODE_UNAUTHORIZED)
  229. _output("Log on as which user?")
  230. _input(username)
  231. if _output() == "Password for existing user?":
  232. _input(password)
  233. if _output() == "Welcome to the Model Management Interface v2.0!":
  234. _output("Use the 'help' command for a list of possible commands")
  235. _input("quiet")
  236. mode = MODE_MODELLING
  237. elif _last_output() == "Wrong password!":
  238. raise PermissionDenied()
  239. else:
  240. raise InterfaceMismatch(_last_output())
  241. elif _last_output() == "This is a new user: please give password!":
  242. _input(password)
  243. _output("Please repeat the password")
  244. _input(password)
  245. if _output() == "Passwords match!":
  246. _output("Welcome to the Model Management Interface v2.0!")
  247. _output("Use the 'help' command for a list of possible commands")
  248. _input("quiet")
  249. mode = MODE_MODELLING
  250. elif _last_output() == "Not the same password!":
  251. # We just sent the same password, so it should be identical, unless the interface changed
  252. raise InterfaceMismatch(_last_output())
  253. else:
  254. raise InterfaceMismatch(_last_output())
  255. else:
  256. raise InterfaceMismatch(_last_output())
  257. def model_add(model_name, metamodel_name, model_code=None):
  258. """Instantiate a new model."""
  259. # return None
  260. # raises UnknownModel
  261. # raises ModelExists
  262. # raises UnknownError
  263. # raises PermissionDenied
  264. # raises CompilationError
  265. _goto_mode(MODE_MODELLING)
  266. # Do this before creating the model, as otherwise compilation errors would make us inconsistent
  267. if model_code is not None:
  268. try:
  269. compiled = _compile_model(model_code)
  270. except Exception as e:
  271. raise CompilationError(e)
  272. else:
  273. compiled = ["exit"]
  274. _input(["model_add", metamodel_name, model_name])
  275. _handle_output("Waiting for model constructors...")
  276. _input(compiled)
  277. _output("Success")
  278. def upload_code(code):
  279. try:
  280. compiled = _compile_AL(code)
  281. except Exception as e:
  282. raise CompilationError(e)
  283. _input(compiled)
  284. def model_delete(model_name):
  285. """Delete an existing model."""
  286. _goto_mode(MODE_MODELLING)
  287. _input(["model_delete", model_name])
  288. _handle_output("Success")
  289. def model_list():
  290. """List all models."""
  291. # return [(model1, metamodel1), (model2, metamodel2), ...]
  292. # raises UnknownError
  293. _goto_mode(MODE_MODELLING)
  294. _input("model_list")
  295. output = _handle_output("Success: ", split=" ")
  296. if output == "":
  297. return set([])
  298. lst = set([])
  299. value = output.strip().split("\n")
  300. for v in value:
  301. m, mm = v.split(":")
  302. m = m.strip()
  303. mm = mm.strip()
  304. lst.add((m, mm))
  305. return lst
  306. def model_list_full():
  307. """List full information on all models."""
  308. # return [(model1, metamodel1, owner1, group1, permissions1), (model2, metamodel2, owner2, group2, permissions2), ...]
  309. # raises UnknownError
  310. _goto_mode(MODE_MODELLING)
  311. _input("model_list_full")
  312. output = _handle_output("Success: ", split=" ")
  313. if output == "":
  314. return set([])
  315. lst = set([])
  316. value = output.strip().split("\n")
  317. for v in value:
  318. m, mm = v.split(":")
  319. m = m.strip()
  320. mm = mm.strip()
  321. perm, own, grp, m = m.split(" ")
  322. lst.add((m, mm, own, grp, perm))
  323. return lst
  324. def verify(model):
  325. """Verify if a model conforms to its metamodel."""
  326. # return "verification_result"
  327. # raises UnknownError
  328. # raises UnknownModel
  329. _goto_mode(MODE_MODELLING)
  330. _input(["verify", model])
  331. return _handle_output("Success: ", split=" ")
  332. def model_overwrite(model_name, new_model=None):
  333. """Upload a new model and overwrite an existing model."""
  334. # return None
  335. # raises UnknownModel
  336. # raises PermissionDenied
  337. # raises CompilationError
  338. # raises UnknownError
  339. if mode not in [MODE_MODELLING, MODE_MODIFY, MODE_MANUAL]:
  340. raise InvalidMode()
  341. if new_model is not None:
  342. try:
  343. compiled = _compile_model(new_model)
  344. except Exception as e:
  345. raise CompilationError(e)
  346. else:
  347. compiled = ["exit"]
  348. if mode == MODE_MODIFY:
  349. _goto_mode(MODE_MODELLING)
  350. if mode == MODE_MODELLING:
  351. _input(["model_overwrite", model_name])
  352. elif mode == MODE_MANUAL:
  353. _input("upload")
  354. else:
  355. raise InvalidMode()
  356. _handle_output("Waiting for model constructors...")
  357. _input(compiled)
  358. _output("Success")
  359. def user_logout():
  360. """Log out the current user and break the connection."""
  361. # return None
  362. # raises UnknownError
  363. global mode
  364. _goto_mode(MODE_MODELLING)
  365. _input("exit")
  366. mode = MODE_UNCONNECTED
  367. def user_delete():
  368. """Removes the current user and break the connection."""
  369. # return None
  370. # raises UnknownError
  371. global mode
  372. _goto_mode(MODE_MODELLING)
  373. _input("self-destruct")
  374. mode = MODE_UNCONNECTED
  375. def model_render(model_name, mapper_name):
  376. """Fetch a rendered verion of a model."""
  377. # return JSON_representation
  378. # raises UnknownError
  379. # raises UnknownIdentifier
  380. # raises InterfaceMismatch
  381. _goto_mode(MODE_MODELLING)
  382. _input(["model_render", model_name, mapper_name])
  383. return _handle_output("Success: ", split=" ")
  384. def transformation_between(source, target):
  385. _goto_mode(MODE_MODELLING)
  386. _input(["transformation_between", source, target])
  387. output = _handle_output("Success: ", split=" ")
  388. if output == "":
  389. return set([])
  390. return set([v for v in output.split("\n")])
  391. def transformation_add_MT(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None):
  392. """Create a new model transformation."""
  393. global mode
  394. _goto_mode(MODE_MODELLING)
  395. try:
  396. compiled = _compile_model(code)
  397. except Exception as e:
  398. raise CompilationError(e)
  399. mv_dict_rep = _dict_to_list(source_metamodels) + [""] + _dict_to_list(target_metamodels) + [""]
  400. _input(["transformation_add_MT"] + mv_dict_rep + [operation_name])
  401. # Possibly modify the merged metamodel first (add tracability links)
  402. if len(source_metamodels) + len(target_metamodels) > 0:
  403. mode = MODE_MANUAL
  404. _output("Model loaded, ready for commands!")
  405. callback()
  406. _input("exit")
  407. mode = MODE_MODELLING
  408. # Done, so RAMify and upload the model
  409. _handle_output("Waiting for model constructors...")
  410. _input(compiled)
  411. _handle_output("Success")
  412. def transformation_add_AL(source_metamodels, target_metamodels, operation_name, code, callback=lambda: None):
  413. """Create a new action language model, which can be executed."""
  414. global mode
  415. _goto_mode(MODE_MODELLING)
  416. try:
  417. compiled = _compile_AL(code)
  418. except Exception as e:
  419. raise CompilationError(e)
  420. mv_dict_rep = _dict_to_list(source_metamodels) + [""] + _dict_to_list(target_metamodels) + [""]
  421. _input(["transformation_add_AL"] + mv_dict_rep + [operation_name])
  422. # Possibly modify the merged metamodel first (add tracability links)
  423. if len(source_metamodels) + len(target_metamodels) > 0:
  424. mode = MODE_MANUAL
  425. _output("Model loaded, ready for commands!")
  426. callback()
  427. _input("exit")
  428. mode = MODE_MODELLING
  429. _handle_output("Waiting for code constructors...")
  430. _input(compiled)
  431. _output("Success")
  432. def transformation_add_MANUAL(source_metamodels, target_metamodels, operation_name, callback=lambda: None):
  433. """Create a new manual model operation."""
  434. global mode
  435. _goto_mode(MODE_MODELLING)
  436. mv_dict_rep = _dict_to_list(source_metamodels) + [""] + _dict_to_list(target_metamodels) + [""]
  437. _input(["transformation_add_MANUAL"] + mv_dict_rep + [operation_name])
  438. # Possibly modify the merged metamodel first (add tracability links)
  439. if len(source_metamodels) + len(target_metamodels) > 0:
  440. mode = MODE_MANUAL
  441. _output("Model loaded, ready for commands!")
  442. callback()
  443. _input("exit")
  444. mode = MODE_MODELLING
  445. _handle_output("Success")
  446. def transformation_execute_AL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  447. """Execute an existing model operation."""
  448. global mode
  449. _goto_mode(MODE_MODELLING)
  450. mv_dict_rep = _dict_to_list(input_models_dict) + [""] + _dict_to_list(output_models_dict) + [""]
  451. _input(["transformation_execute", operation_name] + mv_dict_rep)
  452. _handle_output("Success: ready for AL execution")
  453. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  454. while _output() not in ["Success", "Failure"]:
  455. mode = MODE_DIALOG
  456. reply = callback(_last_output())
  457. mode = MODE_MODELLING
  458. if reply is not None:
  459. _input(reply)
  460. # Got termination message, so we are done!
  461. if _last_output() == "Success":
  462. return True
  463. else:
  464. return False
  465. def transformation_execute_MANUAL(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  466. """Execute an existing model operation."""
  467. global mode
  468. _goto_mode(MODE_MODELLING)
  469. mv_dict_rep = _dict_to_list(input_models_dict) + [""] + _dict_to_list(output_models_dict) + [""]
  470. _input(["transformation_execute", operation_name] + mv_dict_rep)
  471. _handle_output("Success: ready for MANUAL execution")
  472. # Skip over the begin of mini_modify
  473. _handle_output("Please perform manual operation ")
  474. _output("Model loaded, ready for commands!")
  475. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  476. mode = MODE_MANUAL
  477. callback()
  478. # Finished, so leave
  479. _input("exit")
  480. mode = MODE_MODELLING
  481. # Got termination message, so we are done!
  482. if _output() == "Success":
  483. return True
  484. else:
  485. return False
  486. def transformation_execute_MT(operation_name, input_models_dict, output_models_dict, callback=lambda i: None):
  487. """Execute an existing model operation."""
  488. global mode
  489. _goto_mode(MODE_MODELLING)
  490. mv_dict_rep = _dict_to_list(input_models_dict) + [""] + _dict_to_list(output_models_dict) + [""]
  491. _input(["transformation_execute", operation_name] + mv_dict_rep)
  492. _handle_output("Success: ready for MT execution")
  493. # We are now executing, so everything we get is part of the dialog, except if it is the string for transformation termination
  494. while _output() not in ["Success", "Failure"]:
  495. mode = MODE_DIALOG
  496. reply = callback(_last_output())
  497. mode = MODE_MODELLING
  498. if reply is not None:
  499. _input(reply)
  500. # Got termination message, so we are done!
  501. if _last_output() == "Success":
  502. return True
  503. else:
  504. return False
  505. def transformation_list():
  506. """List existing model operations."""
  507. _goto_mode(MODE_MODELLING)
  508. _input("transformation_list")
  509. output = _handle_output("Success: ", split=" ")
  510. if output == "":
  511. return set([])
  512. lst = set([])
  513. value = output.strip().split("\n")
  514. for v in value:
  515. t, m = v.strip().split(" ", 1)
  516. t = t[1:-1].strip()
  517. m = m.strip().split(":")[0].strip()
  518. lst.add((t, m))
  519. return lst
  520. def process_execute(process_name, prefix, callbacks):
  521. """Execute a process model."""
  522. global mode
  523. _goto_mode(MODE_MODELLING)
  524. _input(["process_execute", process_name, prefix])
  525. _handle_output("Success")
  526. while _output() != "Success":
  527. output = _last_output()
  528. if output.startswith("Enacting "):
  529. # Next activity!
  530. t = output.split(" ", 1)[1].split(":", 1)[0]
  531. name = output.split(": ", 1)[1]
  532. if name in callbacks:
  533. callback = callbacks[name]
  534. if t == "ModelTransformation" or t == "ActionLanguage":
  535. while not (_output().startswith("Enacting ") or _last_output() == "Success"):
  536. mode = MODE_DIALOG
  537. reply = callback(_last_output())
  538. mode = MODE_MODELLING
  539. if reply is not None:
  540. _input(reply)
  541. elif t == "ManualOperation":
  542. _handle_output("Please perform manual operation ")
  543. _output("Model loaded, ready for commands!")
  544. mode = MODE_MANUAL
  545. callback()
  546. _input("exit")
  547. mode = MODE_MODELLING
  548. def permission_modify(model_name, permissions):
  549. """Modify permissions of a model."""
  550. _goto_mode(MODE_MODELLING)
  551. _input(["permission_modify", model_name, permissions])
  552. _handle_output("Success")
  553. def permission_owner(model_name, owner):
  554. """Modify the owning user of a model."""
  555. _goto_mode(MODE_MODELLING)
  556. _input(["permission_owner", model_name, owner])
  557. _handle_output("Success")
  558. def permission_group(model_name, group):
  559. """Modify the owning group of a model."""
  560. _goto_mode(MODE_MODELLING)
  561. _input(["permission_group", model_name, group])
  562. _handle_output("Success")
  563. def group_create(group_name):
  564. """Create a new group."""
  565. _goto_mode(MODE_MODELLING)
  566. _input(["group_create", group_name])
  567. _handle_output("Success")
  568. def group_delete(group_name):
  569. """Delete a group of which you are an owner."""
  570. _goto_mode(MODE_MODELLING)
  571. _input(["group_delete", group_name])
  572. _handle_output("Success")
  573. def group_owner_add(group_name, user_name):
  574. """Add a new owning user to a group you own."""
  575. _goto_mode(MODE_MODELLING)
  576. _input(["owner_add", group_name, user_name])
  577. _handle_output("Success")
  578. def group_owner_delete(group_name, user_name):
  579. """Delete an owning user to a group you own."""
  580. _goto_mode(MODE_MODELLING)
  581. _input(["owner_delete", group_name, user_name])
  582. _handle_output("Success")
  583. def group_join(group_name, user_name):
  584. """Add a new user to a group you own."""
  585. _goto_mode(MODE_MODELLING)
  586. _input(["group_join", group_name, user_name])
  587. _handle_output("Success")
  588. def group_kick(group_name, user_name):
  589. """Delete a user from a group you own."""
  590. _goto_mode(MODE_MODELLING)
  591. _input(["group_kick", group_name, user_name])
  592. _handle_output("Success")
  593. def group_list():
  594. """List existing groups."""
  595. _goto_mode(MODE_MODELLING)
  596. _input(["group_list"])
  597. _handle_output("Success")
  598. def admin_promote(user_name):
  599. """Promote a user to admin status."""
  600. _goto_mode(MODE_MODELLING)
  601. _input(["admin_promote", user_name])
  602. _handle_output("Success")
  603. def admin_demote():
  604. """Demote a user from admin status."""
  605. _goto_mode(MODE_MODELLING)
  606. _input(["admin_demote", user_name])
  607. _handle_output("Success")
  608. # Actual operations on the model
  609. def element_list(model_name):
  610. """Return a list of all IDs and the type of the element"""
  611. # return [(name1, type1), (name2, type2), ...]
  612. # raises UnknownError
  613. _goto_mode(MODE_MODIFY, model_name)
  614. _input("list_full")
  615. lst = set([])
  616. output = _handle_output("Success: ", split=" ")
  617. if output == "":
  618. return set([])
  619. for v in output.split("\n"):
  620. m, mm = v.split(":")
  621. m = m.strip()
  622. mm = mm.strip()
  623. lst.add((m, mm))
  624. return lst
  625. def types(model_name):
  626. """Return a list of all types usable in the model"""
  627. # return [type1, type2, ...]
  628. # raises UnknownError
  629. _goto_mode(MODE_MODIFY, model_name)
  630. _input("types")
  631. lst = set([])
  632. output = _handle_output("Success: ", split=" ")
  633. if output == "":
  634. return set([])
  635. for v in output.split("\n"):
  636. m, mm = v.split(":")
  637. m = m.strip()
  638. lst.add(m)
  639. return lst
  640. def types_full(model_name):
  641. """Return a list of full types usable in the model"""
  642. # return [(type1, typetype1), (type2, typetype2), ...]
  643. # raises UnknownError
  644. _goto_mode(MODE_MODIFY, model_name)
  645. _input("types")
  646. lst = set([])
  647. output = _handle_output("Success: ", split=" ")
  648. if output == "":
  649. return set([])
  650. for v in output.split("\n"):
  651. m, mm = v.split(":")
  652. m = m.strip()
  653. mm = mm.strip()
  654. lst.add((m, mm))
  655. return lst
  656. def read(model_name, ID):
  657. """Return a tuple of information on the element: its type and source/target (None if not an edge)"""
  658. # return (type, (source, target))
  659. # raises UnknownError
  660. # raises UnknownIdentifier
  661. _goto_mode(MODE_MODIFY, model_name)
  662. _input(["read", ID])
  663. output = _handle_output("Success: ", split=" ")
  664. v = output.split("\n")
  665. t = v[1].split(":")[1].strip()
  666. if (not v[2].startswith("Source:")):
  667. rval = (t, None)
  668. else:
  669. src = v[2].split(":")[1].strip()
  670. trg = v[3].split(":")[1].strip()
  671. rval = (t, (src, trg))
  672. return rval
  673. def read_attrs(model_name, ID):
  674. """Return a dictionary of attribute value pairs"""
  675. # return {attr1: value1, attr2: value2, ...}
  676. # raises UnknownError
  677. # raises UnknownIdentifier
  678. _goto_mode(MODE_MODIFY, model_name)
  679. _input(["read", ID])
  680. output = _handle_output("Success: ", split=" ")
  681. v = output.split("\n")
  682. searching = True
  683. rval = {}
  684. for r in v:
  685. if searching:
  686. if r == "Attributes:":
  687. # Start working on attributes
  688. searching = False
  689. else:
  690. key, value = r.split(":", 1)
  691. _, value = value.split("=", 1)
  692. key = json.loads(key.strip())
  693. value = value.strip()
  694. if value == "None":
  695. value = None
  696. elif value == "True":
  697. value = True
  698. elif value == "False":
  699. value = False
  700. else:
  701. value = json.loads(value)
  702. rval[key] = value
  703. return rval
  704. def instantiate(model_name, typename, edge=None, ID=""):
  705. """Create a new instance of the specified typename, between the selected elements (if not None), and with the provided ID (if any)"""
  706. # return instantiated_ID
  707. # raises UnknownError
  708. # raises UnknownType
  709. # raises UnknownIdentifier
  710. # raises NotAnEdge
  711. _goto_mode(MODE_MODIFY, model_name)
  712. if edge is None:
  713. _input(["instantiate_node", typename, ID])
  714. else:
  715. _input(["instantiate_edge", typename, ID, edge[0], edge[1]])
  716. return _handle_output("Success: ", split=" ")
  717. def delete_element(model_name, ID):
  718. """Delete the element with the given ID"""
  719. # return None
  720. # raises UnknownError
  721. # raises UnknownIdentifier
  722. _goto_mode(MODE_MODIFY, model_name)
  723. _input(["delete", ID])
  724. _handle_output("Success")
  725. def attr_assign(model_name, ID, attr, value):
  726. """Assign a value to an attribute"""
  727. # return None
  728. # raises UnknownError
  729. # raises UnknownIdentifier
  730. # raises NoSuchAttribute
  731. # raises UnsupportedValue
  732. _goto_mode(MODE_MODIFY, model_name)
  733. _input(["attr_add", ID, attr, value])
  734. _handle_output("Success")
  735. def attr_assign_code(model_name, ID, attr, code):
  736. """Assign a piece of Action Language code to the attribute"""
  737. # return None
  738. # raises UnknownError
  739. # raises UnknownIdentifier
  740. # raises NoSuchAttribute
  741. # raises UnsupportedValue
  742. try:
  743. compiled = _compile_AL(code)
  744. except Exception as e:
  745. raise CompilationError(e)
  746. _goto_mode(MODE_MODIFY, model_name)
  747. _input(["attr_add", ID, attr])
  748. _handle_output("Waiting for code constructors...")
  749. _input(compiled)
  750. _output("Success")
  751. def attr_delete(model_name, ID, attr):
  752. """Remove an attribute."""
  753. _goto_mode(MODE_MODIFY, model_name)
  754. _input(["attr_del", ID, attr])
  755. _handle_output("Success")
  756. def read_outgoing(model_name, ID, typename):
  757. """Returns a list of all outgoing associations of a specific type ("" = all)"""
  758. # return [name1, name2, ...]
  759. # raises UnknownError
  760. # raises UnknownIdentifier
  761. _goto_mode(MODE_MODIFY, model_name)
  762. _input(["read_outgoing", 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_incoming(model_name, ID, typename):
  769. """Returns a list of all incoming associations of a specific type ("" = all)"""
  770. # return [name1, name2, ...]
  771. # raises UnknownError
  772. # raises UnknownIdentifier
  773. # raises UnknownType
  774. _goto_mode(MODE_MODIFY, model_name)
  775. _input(["read_incoming", ID, typename])
  776. output = _handle_output("Success: ", split=" ")
  777. if output == "":
  778. return set([])
  779. else:
  780. return set(output.split("\n"))
  781. def read_association_source(model_name, ID):
  782. """Returns the source of an association."""
  783. # returns name
  784. # raises UnknownError
  785. # raises UnknownIdentifier
  786. # raises NotAnAssociation
  787. _goto_mode(MODE_MODIFY, model_name)
  788. _input(["read_association_source", ID])
  789. return _handle_output("Success: ", split=" ")
  790. def read_association_destination(model_name, ID):
  791. """Returns the destination of an association."""
  792. # returns name
  793. # raises UnknownError
  794. # raises UnknownIdentifier
  795. # raises NotAnAssociation
  796. _goto_mode(MODE_MODIFY, model_name)
  797. _input(["read_association_destination", ID])
  798. return _handle_output("Success: ", split=" ")
  799. ##### To document:
  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. _goto_mode(MODE_SERVICE)
  826. return _output(port=port)
  827. def service_set(port, value):
  828. """Set a value on a specified port."""
  829. _goto_mode(MODE_SERVICE)
  830. _input(value, port=port)
  831. def password_change(user, password):
  832. """Change a user's password."""
  833. raise NotImplementedError()
  834. def transformation_read_signature(transformation):
  835. """Reads an operation's signature, specifying the names and their required types."""
  836. raise NotImplementedError()
  837. def element_list_nice(model_name):
  838. """Fetches a nice representation of models."""
  839. raise NotImplementedError()
  840. def connections_between(model_name, source_element, target_element):
  841. """Gets a list of all allowed connections between the source and target element in the model."""
  842. raise NotImplementedError()
  843. def define_attribute(model_name, node, attr_name, attr_type):
  844. """Create a new attribute, which can be instantiated one meta-level below."""
  845. raise NotImplementedError()
  846. def all_instances(model_name, type_name):
  847. """Returns a list of all elements of a specific type."""
  848. raise NotImplementedError()
  849. def service_poll(port):
  850. """Checks whether or not the Modelverse side has any input ready to be processed."""
  851. raise NotImplementedError()