modelverse.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. import urllib
  2. import urllib2
  3. import json
  4. import random
  5. from urllib2 import URLError
  6. import sys
  7. COMPILER_PATH = "interface/HUTN"
  8. # Bind to the compiler (might have to update path manually!)
  9. sys.path.append(COMPILER_PATH)
  10. from hutn_compiler.compiler import main as do_compile
  11. # Exceptions
  12. class ModelverseException(Exception):
  13. pass
  14. class UnknownException(ModelverseException):
  15. pass
  16. class UnknownIdentifier(ModelverseException):
  17. pass
  18. class UnknownType(ModelverseException):
  19. pass
  20. class NotAnAssociation(ModelverseException):
  21. pass
  22. class UnsupportedValue(ModelverseException):
  23. pass
  24. class CompilationError(ModelverseException):
  25. pass
  26. class NoSuchAttribute(ModelverseException):
  27. pass
  28. class UnknownModel(ModelverseException):
  29. pass
  30. class ConnectionError(ModelverseException):
  31. pass
  32. class ModelExists(ModelverseException):
  33. pass
  34. class PermissionDenied(ModelverseException):
  35. pass
  36. class InvalidMode(ModelverseException):
  37. pass
  38. class InterfaceMismatch(ModelverseException):
  39. pass
  40. # Helper functions and configuration: do not use yourself!
  41. taskname = random.random()
  42. address = None
  43. last_output = None
  44. mode = 0
  45. def _input(value):
  46. # Ugly json encoding of primitives
  47. if isinstance(value, type([])):
  48. value = json.dumps(value)
  49. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "data": value, "taskname": taskname}))).read()
  50. else:
  51. value = json.dumps(value)
  52. #print("Set input: " + str(value))
  53. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
  54. def _input_raw(value, taskname):
  55. # Ugly json encoding of primitives
  56. urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "set_input", "value": value, "taskname": taskname}))).read()
  57. def _compile_AL(code):
  58. # Compile an action language file and send the compiled code
  59. code_fragments = code.split("\n")
  60. code_fragments = [i for i in code_fragments if i.strip() != ""]
  61. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  62. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  63. code_fragments = [i[initial_tabs:] for i in code_fragments]
  64. code_fragments.append("")
  65. code = "\n".join(code_fragments)
  66. with open("__constraint.alc", "w") as f:
  67. f.write(code)
  68. f.flush()
  69. return do_compile("__constraint.alc", COMPILER_PATH + "/grammars/actionlanguage.g", "CS")
  70. def _compile_model(code):
  71. # Compile a model and send the compiled graph
  72. # First change multiple spaces to a tab
  73. code_fragments = code.split("\n")
  74. code_fragments = [i for i in code_fragments if i.strip() != ""]
  75. code_fragments = [i.replace(" ", "\t") for i in code_fragments]
  76. initial_tabs = min([len(i) - len(i.lstrip("\t")) for i in code_fragments])
  77. code_fragments = [i[initial_tabs:] for i in code_fragments]
  78. code_fragments.append("")
  79. code = "\n".join(code_fragments)
  80. with open("__model.mvc", "w") as f:
  81. f.write(code)
  82. f.flush()
  83. return do_compile("__model.mvc", COMPILER_PATH + "/grammars/modelling.g", "M") + ["exit"]
  84. def _output(expected=None):
  85. try:
  86. global last_output
  87. last_output = json.loads(urllib2.urlopen(urllib2.Request(address, urllib.urlencode({"op": "get_output", "taskname": taskname}))).read())
  88. #print("Got output: " + str(last_output))
  89. except:
  90. raise UnknownError()
  91. if expected is not None and last_output != expected:
  92. raise InterfaceMismatch(_last_output(), expected)
  93. return last_output
  94. def _last_output():
  95. return last_output
  96. # Raise common exceptions
  97. def _handle_output(requested=None, split=None):
  98. value = _output()
  99. if value.startswith("Model exists: "):
  100. raise ModelExists()
  101. elif value.startswith("Permission denied"):
  102. raise PermissionDenied()
  103. elif value.startswith("Model not found: "):
  104. raise UnknownModel()
  105. elif value.startswith("Element not found: "):
  106. raise UnknownIdentifier()
  107. elif value.startswith("Element exists: "):
  108. raise ElementExists()
  109. elif value.startswith("Attribute not found: "):
  110. raise NoSuchAttribute()
  111. elif requested is not None and value.startswith(requested):
  112. if split is None:
  113. return value
  114. else:
  115. splitted = value.strip().split(split, 1)
  116. if len(splitted) == 1:
  117. return ""
  118. else:
  119. return splitted[1].rstrip()
  120. else:
  121. raise InterfaceMismatch(value)
  122. # Main MvC operations
  123. def init(address_param="http://127.0.0.1:8001"):
  124. """Starts up the connection to the Modelverse."""
  125. # return None
  126. # raises ConnectionError
  127. # raises UnknownError
  128. # raises InvalidMode
  129. global mode
  130. if mode != 0:
  131. raise InvalidMode()
  132. global address
  133. address = address_param
  134. try:
  135. _input_raw('"%s"' % taskname, "task_manager")
  136. mode = 1
  137. except URLError as e:
  138. raise ConnectionError(e.reason)
  139. def login(username, password):
  140. """Log in a user, if user doesn't exist, it is created."""
  141. # return None
  142. # raises UnknownError
  143. # raises PermissionDenied
  144. # raises InterfaceMismatch
  145. global mode
  146. if mode != 1:
  147. raise InvalidMode()
  148. _output("Log on as which user?")
  149. _input(username)
  150. if _output() == "Password for existing user?":
  151. _input(password)
  152. if _output() == "Welcome to the Model Management Interface v2.0!":
  153. _output("Use the 'help' command for a list of possible commands")
  154. _input("quiet")
  155. mode = 2
  156. elif _last_output() == "Wrong password!":
  157. raise PermissionDenied()
  158. else:
  159. raise InterfaceMismatch(_last_output())
  160. elif _last_output() == "This is a new user: please give password!":
  161. _input(password)
  162. _output("Please repeat the password")
  163. _input(password)
  164. if _output() == "Passwords match!":
  165. _output("Welcome to the Model Management Interface v2.0!")
  166. _output("Use the 'help' command for a list of possible commands")
  167. _input("quiet")
  168. mode = 2
  169. elif _last_output() == "Not the same password!":
  170. # We just sent the same password, so it should be identical, unless the interface changed
  171. raise InterfaceMismatch(_last_output())
  172. else:
  173. raise InterfaceMismatch(_last_output())
  174. else:
  175. raise InterfaceMismatch(_last_output())
  176. def model_add(model_name, metamodel_name, model_code=None):
  177. """Instantiate a new model."""
  178. # return None
  179. # raises UnknownModel
  180. # raises ModelExists
  181. # raises UnknownError
  182. # raises PermissionDenied
  183. # raises CompilationError
  184. if mode != 2:
  185. raise InvalidMode()
  186. # Do this before creating the model, as otherwise compilation errors would make us inconsistent
  187. if model_code is not None:
  188. try:
  189. compiled = _compile_model(model_code)
  190. except:
  191. raise CompilationError()
  192. else:
  193. compiled = ["exit"]
  194. _input(["model_add", metamodel_name, model_name])
  195. _handle_output("Waiting for model constructors...")
  196. _input(compiled)
  197. _output("Success")
  198. def model_modify(model_name):
  199. """Modify an existing model."""
  200. # return is_write
  201. # raises UnknownModel
  202. # raises PermissionDenied
  203. # raises UnknownError
  204. global mode
  205. if mode != 2:
  206. raise InvalidMode()
  207. _input(["model_modify", model_name])
  208. _handle_output("Success")
  209. # Mode has changed
  210. mode = 3
  211. _output("Model loaded, ready for commands!")
  212. if ("r/w" in _output()):
  213. write = True
  214. else:
  215. write = False
  216. _output("Use 'help' command for a list of possible commands")
  217. return write
  218. def model_list():
  219. """List all models."""
  220. # return [(model1, metamodel1), (model2, metamodel2), ...]
  221. # raises UnknownError
  222. if mode != 2:
  223. raise InvalidMode()
  224. _input("model_list")
  225. output = _handle_output("Success: ", split=" ")
  226. if output == "":
  227. return []
  228. lst = []
  229. value = output.strip().split("\n")
  230. for v in value:
  231. m, mm = v.split(":")
  232. m = m.strip()
  233. mm = mm.strip()
  234. lst.append((m, mm))
  235. return lst
  236. def model_list_full():
  237. """List full information on all models."""
  238. # return [(model1, metamodel1, owner1, group1, permissions1), (model2, metamodel2, owner2, group2, permissions2), ...]
  239. # raises UnknownError
  240. if mode != 2:
  241. raise InvalidMode()
  242. _input("model_list_full")
  243. lst = []
  244. value = _output().strip().split("\n")
  245. for v in value:
  246. m, mm = v.split(":")
  247. m = m.strip()
  248. mm = mm.strip()
  249. perm, own, grp, m = m.split(" ")
  250. lst.append((m, mm, own, grp, perm))
  251. return lst
  252. def verify(model):
  253. """Verify if a model conforms to its metamodel."""
  254. # return "verification_result"
  255. # raises UnknownError
  256. # raises UnknownModel
  257. if mode != 2:
  258. raise InvalidMode()
  259. _input(["verify", model])
  260. return _handle_output("Success: ", split=" ")
  261. def model_overwrite(model_name, new_model=None):
  262. """Upload a new model and overwrite an existing model."""
  263. # return None
  264. # raises UnknownModel
  265. # raises PermissionDenied
  266. # raises CompilationError
  267. # raises UnknownError
  268. if mode != 2:
  269. raise InvalidMode()
  270. if new_model is not None:
  271. try:
  272. compiled = _compile_model(new_model)
  273. except Exception as e:
  274. raise CompilationError(e)
  275. else:
  276. compiled = ["exit"]
  277. _input(["model_overwrite", model_name])
  278. _handle_output("Waiting for model constructors...")
  279. _input(compiled)
  280. _output("Success")
  281. def user_logout():
  282. """Log out the current user and break the connection."""
  283. # return None
  284. # raises UnknownException
  285. global mode
  286. if mode != 2:
  287. raise InvalidMode()
  288. _input("exit")
  289. mode = 0
  290. def user_delete():
  291. """Removes the current user and break the connection."""
  292. # return None
  293. # raises UnknownException
  294. global mode
  295. if mode != 2:
  296. raise InvalidMode()
  297. _input("self-destruct")
  298. mode = 0
  299. def model_render(model, mapper):
  300. """Fetch a rendered verion of a model."""
  301. # return JSON_representation
  302. # raises UnknownException
  303. # raises UnknownIdentifier
  304. # raises InterfaceMismatch
  305. global mode
  306. if mode != 2:
  307. raise InvalidMode()
  308. _input(["model_render", model, mapper])
  309. return _handle_output("Success: ", split=" ")
  310. def transformation_between(source, target):
  311. global mode
  312. if mode != 2:
  313. raise InvalidMode()
  314. _input(["transformation_between", source, target])
  315. output = _handle_output("Success: ", split=" ")
  316. if output == "":
  317. return []
  318. lst = [v for v in output.split("\n")]
  319. def transformation_add_MT_language():
  320. """Create a new Model Transformation language out of a set of metamodels."""
  321. raise NotImplementedError()
  322. def transformation_add_MT():
  323. """Create a new model transformation."""
  324. raise NotImplementedError()
  325. def transformation_add_AL():
  326. """Create a new action language fragment."""
  327. raise NotImplementedError()
  328. def transformation_add_MANUAL():
  329. """Create a new manual model operation."""
  330. raise NotImplementedError()
  331. def transformation_execute():
  332. """Execute an existing model operation."""
  333. raise NotImplementedError()
  334. def transformation_list():
  335. """List existing model operations."""
  336. raise NotImplementedError()
  337. def transformation_list_full():
  338. """List detailed information on model operations."""
  339. raise NotImplementedError()
  340. def transformation_detail():
  341. """List full details of a a model operation."""
  342. raise NotImplementedError()
  343. def transformation_RAMify():
  344. """Ramify an existing metamodel."""
  345. raise NotImplementedError()
  346. def process_execute():
  347. """Execute a process model."""
  348. raise NotImplementedError()
  349. def permission_modify():
  350. """Modify permissions of a model."""
  351. raise NotImplementedError()
  352. def permission_owner():
  353. """Modify the owning user of a model."""
  354. raise NotImplementedError()
  355. def permission_group():
  356. """Modify the owning group of a model."""
  357. raise NotImplementedError()
  358. def group_create():
  359. """Create a new group."""
  360. raise NotImplementedError()
  361. def group_delete():
  362. """Delete a group of which you are an owner."""
  363. raise NotImplementedError()
  364. def group_owner_add():
  365. """Add a new owning user to a group you own."""
  366. raise NotImplementedError()
  367. def group_owner_delete():
  368. """Delete an owning user to a group you own."""
  369. raise NotImplementedError()
  370. def group_join():
  371. """Add a new user to a group you own."""
  372. raise NotImplementedError()
  373. def group_kick():
  374. """Delete a user from a group you own."""
  375. raise NotImplementedError()
  376. def group_list():
  377. """List existing groups."""
  378. raise NotImplementedError()
  379. def admin_promote():
  380. """Promote a user to admin status."""
  381. raise NotImplementedError()
  382. def admin_demote():
  383. """Demote a user from admin status."""
  384. raise NotImplementedError()
  385. # Actual operations on the model
  386. def element_list(model_name):
  387. """Return a list of all IDs and the type of the element"""
  388. # return [(name1, type1), (name2, type2), ...]
  389. # raises UnknownError
  390. model_modify(model_name)
  391. if mode != 3:
  392. raise InvalidMode()
  393. try:
  394. _input("list_full")
  395. lst = []
  396. output = _handle_output("Success: ", split=" ")
  397. if output == "":
  398. return []
  399. for v in output.split("\n"):
  400. m, mm = v.split(":")
  401. m = m.strip()
  402. mm = mm.strip()
  403. lst.append((m, mm))
  404. return lst
  405. finally:
  406. model_exit()
  407. def types(model_name):
  408. """Return a list of all types usable in the model"""
  409. # return [type1, type2, ...]
  410. # raises UnknownError
  411. model_modify(model_name)
  412. if mode != 3:
  413. raise InvalidMode()
  414. try:
  415. _input("types")
  416. lst = []
  417. output = _handle_output("Success: ", split=" ")
  418. if output == "":
  419. return []
  420. for v in output.split("\n"):
  421. m, mm = v.split(":")
  422. m = m.strip()
  423. lst.append(m)
  424. return lst
  425. finally:
  426. model_exit()
  427. def types_full(model_name):
  428. """Return a list of full types usable in the model"""
  429. # return [(type1, typetype1), (type2, typetype2), ...]
  430. # raises UnknownError
  431. model_modify(model_name)
  432. if mode != 3:
  433. raise InvalidMode()
  434. try:
  435. _input("types")
  436. lst = []
  437. output = _handle_output("Success: ", split=" ")
  438. if output == "":
  439. return []
  440. for v in output.split("\n"):
  441. m, mm = v.split(":")
  442. m = m.strip()
  443. mm = mm.strip()
  444. lst.append((m, mm))
  445. return lst
  446. finally:
  447. model_exit()
  448. def read(model_name, ID):
  449. """Return a tuple of information on the element: its type and source/target (None if not an edge)"""
  450. # return (type, (source, target))
  451. # raises UnknownError
  452. # raises UnknownIdentifier
  453. model_modify(model_name)
  454. if mode != 3:
  455. raise InvalidMode()
  456. try:
  457. _input(["read", ID])
  458. output = _handle_output("Success: ", split=" ")
  459. v = output.split("\n")
  460. t = v[1].split(":")[1].strip()
  461. if (not v[0].startswith("Source:")):
  462. rval = (t, None)
  463. else:
  464. src = v[0].split(":")[1].strip()
  465. trg = v[1].split(":")[1].strip()
  466. rval = (t, (src, trg))
  467. return rval
  468. finally:
  469. model_exit()
  470. def read_attrs(model_name, ID):
  471. """Return a dictionary of attribute value pairs"""
  472. # return {attr1: value1, attr2: value2, ...}
  473. # raises UnknownError
  474. # raises UnknownIdentifier
  475. model_modify(model_name)
  476. if mode != 3:
  477. raise InvalidMode()
  478. try:
  479. _input(["read", ID])
  480. output = _handle_output("Success: ", split=" ")
  481. v = output.split("\n")
  482. searching = True
  483. rval = {}
  484. for r in v:
  485. if searching:
  486. if r == "Attributes:":
  487. # Start working on attributes
  488. searching = False
  489. else:
  490. key, value = r.split(":", 1)
  491. _, value = value.split("=", 1)
  492. key = json.loads(key.strip())
  493. value = value.strip()
  494. if value == "None":
  495. value = None
  496. elif value == "True":
  497. value = True
  498. elif value == "False":
  499. value = False
  500. else:
  501. value = json.loads(value)
  502. rval[key] = value
  503. return rval
  504. finally:
  505. model_exit()
  506. def instantiate(model_name, typename, edge=None):
  507. """Create a new instance of the specified typename, between the selected elements (if not None), and with the provided ID (if any)"""
  508. # return instantiated_ID
  509. # raises UnknownError
  510. # raises UnknownType
  511. # raises UnknownIdentifier
  512. # raises NotAnEdge
  513. model_modify(model_name)
  514. if mode != 3:
  515. raise InvalidMode()
  516. try:
  517. if edge is None:
  518. _input(["instantiate_node", typename, ""])
  519. else:
  520. _input(["instantiate_edge", typename, "", edge[0], edge[1]])
  521. return _handle_output("Success: ", split=" ")
  522. finally:
  523. model_exit()
  524. def delete_element(model_name, ID):
  525. """Delete the element with the given ID"""
  526. # return None
  527. # raises UnknownError
  528. # raises UnknownIdentifier
  529. model_modify(model_name)
  530. if mode != 3:
  531. raise InvalidMode()
  532. try:
  533. _input(["delete", ID])
  534. _handle_output("Success")
  535. finally:
  536. model_exit()
  537. def attr_assign(model_name, ID, attr, value):
  538. """Assign a value to an attribute"""
  539. # return None
  540. # raises UnknownError
  541. # raises UnknownIdentifier
  542. # raises NoSuchAttribute
  543. # raises UnsupportedValue
  544. model_modify(model_name)
  545. if mode != 3:
  546. raise InvalidMode()
  547. try:
  548. _input(["attr_add", ID, attr, value])
  549. _handle_output("Success")
  550. finally:
  551. model_exit()
  552. def attr_assign_code(model_name, ID, attr, code):
  553. """Assign a piece of Action Language code to the attribute"""
  554. # return None
  555. # raises UnknownError
  556. # raises UnknownIdentifier
  557. # raises NoSuchAttribute
  558. # raises UnsupportedValue
  559. try:
  560. compiled = _compile_AL(code)
  561. except Exception as e:
  562. raise CompilationError(e)
  563. model_modify(model_name)
  564. if mode != 3:
  565. raise InvalidMode()
  566. try:
  567. _input(["attr_add", ID, attr])
  568. output = _handle_output("Waiting for code constructors...")
  569. _input(compiled)
  570. _output("Success")
  571. finally:
  572. model_exit()
  573. def attr_delete(model_name, ID, attr):
  574. """Remove an attribute."""
  575. model_modify(model_name)
  576. if mode != 3:
  577. raise InvalidMode()
  578. try:
  579. _input(["attr_del", ID, attr])
  580. _handle_output("Success")
  581. finally:
  582. model_exit()
  583. def read_outgoing(model_name, ID, typename):
  584. """Returns a list of all outgoing associations of a specific type ("" = all)"""
  585. # return [name1, name2, ...]
  586. # raises UnknownError
  587. # raises UnknownIdentifier
  588. model_modify(model_name)
  589. if mode != 3:
  590. raise InvalidMode()
  591. try:
  592. _input(["read_outgoing", ID, typename])
  593. output = _handle_output("Success: ", split=" ")
  594. if output == "":
  595. return []
  596. else:
  597. return output.split("\n")
  598. finally:
  599. model_exit()
  600. def read_incoming(model_name, ID, typename):
  601. """Returns a list of all incoming associations of a specific type ("" = all)"""
  602. # return [name1, name2, ...]
  603. # raises UnknownError
  604. # raises UnknownIdentifier
  605. # raises UnknownType
  606. model_modify(model_name)
  607. if mode != 3:
  608. raise InvalidMode()
  609. try:
  610. _input(["read_incoming", ID, typename])
  611. output = _handle_output("Success: ", split=" ")
  612. if output == "":
  613. return []
  614. else:
  615. return output.split("\n")
  616. finally:
  617. model_exit()
  618. def read_association_source(model_name, ID):
  619. """Returns the source of an association."""
  620. # returns name
  621. # raises UnknownError
  622. # raises UnknownIdentifier
  623. # raises NotAnAssociation
  624. model_modify(model_name)
  625. if mode != 3:
  626. raise InvalidMode()
  627. try:
  628. _input(["read_association_source", ID])
  629. return _handle_output("Success: ", split=" ")
  630. finally:
  631. model_exit()
  632. def read_association_destination(model_name, ID):
  633. """Returns the destination of an association."""
  634. # returns name
  635. # raises UnknownError
  636. # raises UnknownIdentifier
  637. # raises NotAnAssociation
  638. model_modify(model_name)
  639. if mode != 3:
  640. raise InvalidMode()
  641. try:
  642. _input(["read_association_destination", ID])
  643. return _handle_output("Success: ", split=" ")
  644. finally:
  645. model_exit()
  646. def model_exit():
  647. """Leave model modify mode."""
  648. # return None
  649. # raises UnknownError
  650. global mode
  651. if mode != 3:
  652. raise InvalidMode()
  653. _input("exit")
  654. _output("Success")
  655. mode = 2