modelverse.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  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. return value.strip().split(split, 1)[1].rstrip()
  116. else:
  117. raise InterfaceMismatch(value)
  118. # Main MvC operations
  119. def init(address_param="http://127.0.0.1:8001"):
  120. """Starts up the connection to the Modelverse."""
  121. # return None
  122. # raises ConnectionError
  123. # raises UnknownError
  124. # raises InvalidMode
  125. global mode
  126. if mode != 0:
  127. raise InvalidMode()
  128. global address
  129. address = address_param
  130. try:
  131. _input_raw('"%s"' % taskname, "task_manager")
  132. mode = 1
  133. except URLError as e:
  134. raise ConnectionError(e.reason)
  135. def login(username, password):
  136. """Log in a user, if user doesn't exist, it is created."""
  137. # return None
  138. # raises UnknownError
  139. # raises PermissionDenied
  140. # raises InterfaceMismatch
  141. global mode
  142. if mode != 1:
  143. raise InvalidMode()
  144. _output("Log on as which user?")
  145. _input(username)
  146. if _output() == "Password for existing user?":
  147. _input(password)
  148. if _output() == "Welcome to the Model Management Interface v2.0!":
  149. _output("Use the 'help' command for a list of possible commands")
  150. _input("quiet")
  151. mode = 2
  152. elif _last_output() == "Wrong password!":
  153. raise PermissionDenied()
  154. else:
  155. raise InterfaceMismatch(_last_output())
  156. elif _last_output() == "This is a new user: please give password!":
  157. _input(password)
  158. _output("Please repeat the password")
  159. _input(password)
  160. if _output() == "Passwords match!":
  161. _output("Welcome to the Model Management Interface v2.0!")
  162. _output("Use the 'help' command for a list of possible commands")
  163. _input("quiet")
  164. mode = 2
  165. elif _last_output() == "Not the same password!":
  166. # We just sent the same password, so it should be identical, unless the interface changed
  167. raise InterfaceMismatch(_last_output())
  168. else:
  169. raise InterfaceMismatch(_last_output())
  170. else:
  171. raise InterfaceMismatch(_last_output())
  172. def model_add(model_name, metamodel_name, model_code=None):
  173. """Instantiate a new model."""
  174. # return None
  175. # raises UnknownModel
  176. # raises ModelExists
  177. # raises UnknownError
  178. # raises PermissionDenied
  179. # raises CompilationError
  180. if mode != 2:
  181. raise InvalidMode()
  182. # Do this before creating the model, as otherwise compilation errors would make us inconsistent
  183. if model_code is not None:
  184. try:
  185. compiled = _compile_model(model_code)
  186. except:
  187. raise CompilationError()
  188. else:
  189. compiled = ["exit"]
  190. _input(["model_add", metamodel_name, model_name])
  191. _handle_output("Waiting for model constructors...")
  192. _input(compiled)
  193. _output("Success")
  194. def model_modify(model_name):
  195. """Modify an existing model."""
  196. # return is_write
  197. # raises UnknownModel
  198. # raises PermissionDenied
  199. # raises UnknownError
  200. global mode
  201. if mode != 2:
  202. raise InvalidMode()
  203. _input(["model_modify", model_name])
  204. _handle_output("Success")
  205. # Mode has changed
  206. mode = 3
  207. _output("Model loaded, ready for commands!")
  208. if ("r/w" in _output()):
  209. write = True
  210. else:
  211. write = False
  212. _output("Use 'help' command for a list of possible commands")
  213. return write
  214. def model_list():
  215. """List all models."""
  216. # return [(model1, metamodel1), (model2, metamodel2), ...]
  217. # raises UnknownError
  218. if mode != 2:
  219. raise InvalidMode()
  220. _input("model_list")
  221. output = _handle_output("Success: ", split=" ")
  222. lst = []
  223. value = output.strip().split("\n")
  224. for v in value:
  225. m, mm = v.split(":")
  226. m = m.strip()
  227. mm = mm.strip()
  228. lst.append((m, mm))
  229. return lst
  230. def model_list_full():
  231. """List full information on all models."""
  232. # return [(model1, metamodel1, owner1, group1, permissions1), (model2, metamodel2, owner2, group2, permissions2), ...]
  233. # raises UnknownError
  234. if mode != 2:
  235. raise InvalidMode()
  236. _input("model_list_full")
  237. lst = []
  238. value = _output().strip().split("\n")
  239. for v in value:
  240. m, mm = v.split(":")
  241. m = m.strip()
  242. mm = mm.strip()
  243. perm, own, grp, m = m.split(" ")
  244. lst.append((m, mm, own, grp, perm))
  245. return lst
  246. def verify(model):
  247. """Verify if a model conforms to its metamodel."""
  248. # return "verification_result"
  249. # raises UnknownError
  250. # raises UnknownModel
  251. if mode != 2:
  252. raise InvalidMode()
  253. _input(["verify", model])
  254. return _handle_output("Success: ", split=" ")
  255. def model_overwrite(model_name, new_model=None):
  256. """Upload a new model and overwrite an existing model."""
  257. # return None
  258. # raises UnknownModel
  259. # raises PermissionDenied
  260. # raises CompilationError
  261. # raises UnknownError
  262. if mode != 2:
  263. raise InvalidMode()
  264. if new_model is not None:
  265. try:
  266. compiled = _compile_model(new_model)
  267. except Exception as e:
  268. raise CompilationError(e)
  269. else:
  270. compiled = ["exit"]
  271. _input(["model_overwrite", model_name])
  272. _handle_output("Waiting for model constructors...")
  273. _input(compiled)
  274. _output("Success")
  275. def user_logout():
  276. """Log out the current user and break the connection."""
  277. # return None
  278. # raises UnknownException
  279. global mode
  280. if mode != 2:
  281. raise InvalidMode()
  282. _input("exit")
  283. mode = 0
  284. def user_delete():
  285. """Removes the current user and break the connection."""
  286. # return None
  287. # raises UnknownException
  288. global mode
  289. if mode != 2:
  290. raise InvalidMode()
  291. _input("self-destruct")
  292. mode = 0
  293. def model_render(model, mapper):
  294. """Fetch a rendered verion of a model."""
  295. # return JSON_representation
  296. # raises UnknownException
  297. # raises UnknownIdentifier
  298. # raises InterfaceMismatch
  299. global mode
  300. if mode != 2:
  301. raise InvalidMode()
  302. _input(["model_render", model, mapper])
  303. return _handle_output("Success: ", split=" ")
  304. def transformation_between(source, target):
  305. global mode
  306. if mode != 2:
  307. raise InvalidMode()
  308. _input(["transformation_between", source, target])
  309. output = _handle_output("Success: ", split=" ")
  310. lst = [v for v in output.split("\n")]
  311. def transformation_add_MT_language():
  312. """Create a new Model Transformation language out of a set of metamodels."""
  313. raise NotImplementedError()
  314. def transformation_add_MT():
  315. """Create a new model transformation."""
  316. raise NotImplementedError()
  317. def transformation_add_AL():
  318. """Create a new action language fragment."""
  319. raise NotImplementedError()
  320. def transformation_add_MANUAL():
  321. """Create a new manual model operation."""
  322. raise NotImplementedError()
  323. def transformation_execute():
  324. """Execute an existing model operation."""
  325. raise NotImplementedError()
  326. def transformation_list():
  327. """List existing model operations."""
  328. raise NotImplementedError()
  329. def transformation_list_full():
  330. """List detailed information on model operations."""
  331. raise NotImplementedError()
  332. def transformation_detail():
  333. """List full details of a a model operation."""
  334. raise NotImplementedError()
  335. def transformation_RAMify():
  336. """Ramify an existing metamodel."""
  337. raise NotImplementedError()
  338. def process_execute():
  339. """Execute a process model."""
  340. raise NotImplementedError()
  341. def permission_modify():
  342. """Modify permissions of a model."""
  343. raise NotImplementedError()
  344. def permission_owner():
  345. """Modify the owning user of a model."""
  346. raise NotImplementedError()
  347. def permission_group():
  348. """Modify the owning group of a model."""
  349. raise NotImplementedError()
  350. def group_create():
  351. """Create a new group."""
  352. raise NotImplementedError()
  353. def group_delete():
  354. """Delete a group of which you are an owner."""
  355. raise NotImplementedError()
  356. def group_owner_add():
  357. """Add a new owning user to a group you own."""
  358. raise NotImplementedError()
  359. def group_owner_delete():
  360. """Delete an owning user to a group you own."""
  361. raise NotImplementedError()
  362. def group_join():
  363. """Add a new user to a group you own."""
  364. raise NotImplementedError()
  365. def group_kick():
  366. """Delete a user from a group you own."""
  367. raise NotImplementedError()
  368. def group_list():
  369. """List existing groups."""
  370. raise NotImplementedError()
  371. def admin_promote():
  372. """Promote a user to admin status."""
  373. raise NotImplementedError()
  374. def admin_demote():
  375. """Demote a user from admin status."""
  376. raise NotImplementedError()
  377. # Actual operations on the model
  378. def element_list(model_name):
  379. """Return a list of all IDs and the type of the element"""
  380. # return [(name1, type1), (name2, type2), ...]
  381. # raises UnknownError
  382. model_modify(model_name)
  383. if mode != 3:
  384. raise InvalidMode()
  385. try:
  386. _input("list_full")
  387. lst = []
  388. output = _handle_output("Success: ", split=" ")
  389. for v in output.split("\n"):
  390. m, mm = v.split(":")
  391. m = m.strip()
  392. mm = mm.strip()
  393. lst.append((m, mm))
  394. return lst
  395. finally:
  396. model_exit()
  397. def types(model_name):
  398. """Return a list of all types usable in the model"""
  399. # return [type1, type2, ...]
  400. # raises UnknownError
  401. model_modify(model_name)
  402. if mode != 3:
  403. raise InvalidMode()
  404. try:
  405. _input("types")
  406. lst = []
  407. output = _handle_output("Success: ", split=" ")
  408. for v in output.split("\n"):
  409. m, mm = v.split(":")
  410. m = m.strip()
  411. lst.append(m)
  412. return lst
  413. finally:
  414. model_exit()
  415. def types_full(model_name):
  416. """Return a list of full types usable in the model"""
  417. # return [(type1, typetype1), (type2, typetype2), ...]
  418. # raises UnknownError
  419. model_modify(model_name)
  420. if mode != 3:
  421. raise InvalidMode()
  422. try:
  423. _input("types")
  424. lst = []
  425. output = _handle_output("Success: ", split=" ")
  426. for v in output.split("\n"):
  427. m, mm = v.split(":")
  428. m = m.strip()
  429. mm = mm.strip()
  430. lst.append((m, mm))
  431. return lst
  432. finally:
  433. model_exit()
  434. def read(model_name, ID):
  435. """Return a tuple of information on the element: its type and source/target (None if not an edge)"""
  436. # return (type, (source, target))
  437. # raises UnknownError
  438. # raises UnknownIdentifier
  439. model_modify(model_name)
  440. if mode != 3:
  441. raise InvalidMode()
  442. try:
  443. _input(["read", ID])
  444. output = _handle_output("Success: ", split=" ")
  445. v = output.split("\n")
  446. t = v[1].split(":")[1].strip()
  447. if (not v[0].startswith("Source:")):
  448. rval = (t, None)
  449. else:
  450. src = v[0].split(":")[1].strip()
  451. trg = v[1].split(":")[1].strip()
  452. rval = (t, (src, trg))
  453. return rval
  454. finally:
  455. model_exit()
  456. def read_attrs(model_name, ID):
  457. """Return a dictionary of attribute value pairs"""
  458. # return {attr1: value1, attr2: value2, ...}
  459. # raises UnknownError
  460. # raises UnknownIdentifier
  461. model_modify(model_name)
  462. if mode != 3:
  463. raise InvalidMode()
  464. try:
  465. _input(["read", ID])
  466. output = _handle_output("Success: ", split=" ")
  467. v = output.split("\n")
  468. searching = True
  469. rval = {}
  470. for r in v:
  471. if searching:
  472. if r == "Attributes:":
  473. # Start working on attributes
  474. searching = False
  475. else:
  476. key, value = r.split(":", 1)
  477. _, value = value.split("=", 1)
  478. key = json.loads(key.strip())
  479. value = value.strip()
  480. if value == "None":
  481. value = None
  482. elif value == "True":
  483. value = True
  484. elif value == "False":
  485. value = False
  486. else:
  487. value = json.loads(value)
  488. rval[key] = value
  489. return rval
  490. finally:
  491. model_exit()
  492. def instantiate(model_name, typename, edge=None):
  493. """Create a new instance of the specified typename, between the selected elements (if not None), and with the provided ID (if any)"""
  494. # return instantiated_ID
  495. # raises UnknownError
  496. # raises UnknownType
  497. # raises UnknownIdentifier
  498. # raises NotAnEdge
  499. model_modify(model_name)
  500. if mode != 3:
  501. raise InvalidMode()
  502. try:
  503. if edge is None:
  504. _input(["instantiate_node", typename, ""])
  505. else:
  506. _input(["instantiate_edge", typename, "", edge[0], edge[1]])
  507. return _handle_output("Success: ", split=" ")
  508. finally:
  509. model_exit()
  510. def delete_element(model_name, ID):
  511. """Delete the element with the given ID"""
  512. # return None
  513. # raises UnknownError
  514. # raises UnknownIdentifier
  515. model_modify(model_name)
  516. if mode != 3:
  517. raise InvalidMode()
  518. try:
  519. _input(["delete", ID])
  520. _handle_output("Success")
  521. finally:
  522. model_exit()
  523. def attr_assign(model_name, ID, attr, value):
  524. """Assign a value to an attribute"""
  525. # return None
  526. # raises UnknownError
  527. # raises UnknownIdentifier
  528. # raises NoSuchAttribute
  529. # raises UnsupportedValue
  530. model_modify(model_name)
  531. if mode != 3:
  532. raise InvalidMode()
  533. try:
  534. _input(["attr_add", ID, attr, value])
  535. _handle_output("Success")
  536. finally:
  537. model_exit()
  538. def attr_assign_code(model_name, ID, attr, code):
  539. """Assign a piece of Action Language code to the attribute"""
  540. # return None
  541. # raises UnknownError
  542. # raises UnknownIdentifier
  543. # raises NoSuchAttribute
  544. # raises UnsupportedValue
  545. try:
  546. compiled = _compile_AL(code)
  547. except Exception as e:
  548. raise CompilationError(e)
  549. model_modify(model_name)
  550. if mode != 3:
  551. raise InvalidMode()
  552. try:
  553. _input(["attr_add", ID, attr])
  554. output = _handle_output("Waiting for code constructors...")
  555. _input(compiled)
  556. _output("Success")
  557. finally:
  558. model_exit()
  559. def attr_delete(model_name, ID, attr):
  560. """Remove an attribute."""
  561. model_modify(model_name)
  562. if mode != 3:
  563. raise InvalidMode()
  564. try:
  565. _input(["attr_del", ID, attr])
  566. _handle_output("Success")
  567. finally:
  568. model_exit()
  569. def read_outgoing(model_name, ID, typename):
  570. """Returns a list of all outgoing associations of a specific type ("" = all)"""
  571. # return [name1, name2, ...]
  572. # raises UnknownError
  573. # raises UnknownIdentifier
  574. model_modify(model_name)
  575. if mode != 3:
  576. raise InvalidMode()
  577. try:
  578. _input(["read_outgoing", ID, typename])
  579. output = _handle_output("Success: ", split=" ")
  580. return output.split("\n")
  581. finally:
  582. model_exit()
  583. def read_incoming(model_name, ID, typename):
  584. """Returns a list of all incoming associations of a specific type ("" = all)"""
  585. # return [name1, name2, ...]
  586. # raises UnknownError
  587. # raises UnknownIdentifier
  588. # raises UnknownType
  589. model_modify(model_name)
  590. if mode != 3:
  591. raise InvalidMode()
  592. try:
  593. _input(["read_incoming", ID, typename])
  594. output = _handle_output("Success: ", split=" ")
  595. return output.split("\n")
  596. finally:
  597. model_exit()
  598. def read_association_source(model_name, ID):
  599. """Returns the source of an association."""
  600. # returns name
  601. # raises UnknownError
  602. # raises UnknownIdentifier
  603. # raises NotAnAssociation
  604. model_modify(model_name)
  605. if mode != 3:
  606. raise InvalidMode()
  607. try:
  608. _input(["read_association_source", ID])
  609. return _handle_output("Success: ", split=" ")
  610. finally:
  611. model_exit()
  612. def read_association_destination(model_name, ID):
  613. """Returns the destination of an association."""
  614. # returns name
  615. # raises UnknownError
  616. # raises UnknownIdentifier
  617. # raises NotAnAssociation
  618. model_modify(model_name)
  619. if mode != 3:
  620. raise InvalidMode()
  621. try:
  622. _input(["read_association_destination", ID])
  623. return _handle_output("Success: ", split=" ")
  624. finally:
  625. model_exit()
  626. def model_exit():
  627. """Leave model modify mode."""
  628. # return None
  629. # raises UnknownError
  630. global mode
  631. if mode != 3:
  632. raise InvalidMode()
  633. _input("exit")
  634. _output("Success")
  635. mode = 2