Mercurial > repos > eric-rasche > apollo
comparison webapollo.py @ 7:f9a6e151b3b4 draft
planemo upload for repository https://github.com/TAMU-CPT/galaxy-webapollo commit 52b9e5bf6a6efb09a5cb845ee48703651c644174
author | eric-rasche |
---|---|
date | Tue, 27 Jun 2017 04:05:17 -0400 |
parents | 7610987e0c48 |
children |
comparison
equal
deleted
inserted
replaced
6:8f76685cdfc8 | 7:f9a6e151b3b4 |
---|---|
1 from __future__ import print_function | |
2 import argparse | |
3 import collections | |
4 import json | |
5 import logging | |
6 import os | |
1 import requests | 7 import requests |
2 import json | |
3 import os | |
4 import collections | |
5 try: | |
6 import StringIO as io | |
7 except: | |
8 import io | |
9 import logging | |
10 import time | 8 import time |
11 import argparse | 9 from future import standard_library |
10 from builtins import next | |
11 from builtins import str | |
12 from builtins import object | |
12 from abc import abstractmethod | 13 from abc import abstractmethod |
13 from BCBio import GFF | 14 from BCBio import GFF |
14 from Bio import SeqIO | 15 from Bio import SeqIO |
16 standard_library.install_aliases() | |
17 try: | |
18 import StringIO as io | |
19 except BaseException: | |
20 import io | |
15 logging.getLogger("requests").setLevel(logging.CRITICAL) | 21 logging.getLogger("requests").setLevel(logging.CRITICAL) |
16 log = logging.getLogger() | 22 log = logging.getLogger() |
17 | 23 |
18 | 24 |
19 ############################################# | 25 ############################################# |
20 ###### BEGIN IMPORT OF CACHING LIBRARY ###### | 26 # BEGIN IMPORT OF CACHING LIBRARY # |
21 ############################################# | 27 ############################################# |
22 # This code is licensed under the MIT # | 28 # This code is licensed under the MIT # |
23 # License and is a copy of code publicly # | 29 # License and is a copy of code publicly # |
24 # available in rev. # | 30 # available in rev. # |
25 # e27332bc82f4e327aedaec17c9b656ae719322ed # | 31 # e27332bc82f4e327aedaec17c9b656ae719322ed # |
26 # of https://github.com/tkem/cachetools/ # | 32 # of https://github.com/tkem/cachetools/ # |
27 ############################################# | 33 ############################################# |
34 | |
28 class DefaultMapping(collections.MutableMapping): | 35 class DefaultMapping(collections.MutableMapping): |
29 | 36 |
30 __slots__ = () | 37 __slots__ = () |
31 | 38 |
32 @abstractmethod | 39 @abstractmethod |
62 if key in self: | 69 if key in self: |
63 value = self[key] | 70 value = self[key] |
64 else: | 71 else: |
65 self[key] = value = default | 72 self[key] = value = default |
66 return value | 73 return value |
74 | |
67 | 75 |
68 DefaultMapping.register(dict) | 76 DefaultMapping.register(dict) |
69 | 77 |
70 | 78 |
71 class _DefaultSize(object): | 79 class _DefaultSize(object): |
379 self.__links[key] = value | 387 self.__links[key] = value |
380 return value | 388 return value |
381 | 389 |
382 | 390 |
383 ############################################# | 391 ############################################# |
384 ###### END IMPORT OF CACHING LIBRARY ###### | 392 # END IMPORT OF CACHING LIBRARY # |
385 ############################################# | 393 ############################################# |
386 | 394 |
395 | |
387 cache = TTLCache( | 396 cache = TTLCache( |
388 100, # Up to 100 items | 397 100, # Up to 100 items |
389 5 * 60 # 5 minute cache life | 398 5 * 60 # 5 minute cache life |
390 ) | 399 ) |
391 userCache = TTLCache( | 400 userCache = TTLCache( |
392 2, # Up to 2 items | 401 2, # Up to 2 items |
393 60 # 1 minute cache life | 402 60 # 1 minute cache life |
394 ) | 403 ) |
404 | |
395 | 405 |
396 class UnknownUserException(Exception): | 406 class UnknownUserException(Exception): |
397 pass | 407 pass |
408 | |
398 | 409 |
399 def WAAuth(parser): | 410 def WAAuth(parser): |
400 parser.add_argument('apollo', help='Complete Apollo URL') | 411 parser.add_argument('apollo', help='Complete Apollo URL') |
401 parser.add_argument('username', help='WA Username') | 412 parser.add_argument('username', help='WA Username') |
402 parser.add_argument('password', help='WA Password') | 413 parser.add_argument('password', help='WA Password') |
466 | 477 |
467 def __init__(self, url, username, password): | 478 def __init__(self, url, username, password): |
468 self.apollo_url = url | 479 self.apollo_url = url |
469 self.username = username | 480 self.username = username |
470 self.password = password | 481 self.password = password |
471 # TODO: Remove after apollo 2.0.6. | |
472 self.clientToken = time.time() | |
473 | 482 |
474 self.annotations = AnnotationsClient(self) | 483 self.annotations = AnnotationsClient(self) |
475 self.groups = GroupsClient(self) | 484 self.groups = GroupsClient(self) |
476 self.io = IOClient(self) | 485 self.io = IOClient(self) |
477 self.organisms = OrganismsClient(self) | 486 self.organisms = OrganismsClient(self) |
478 self.users = UsersClient(self) | 487 self.users = UsersClient(self) |
479 self.metrics = MetricsClient(self) | 488 self.metrics = MetricsClient(self) |
480 self.bio = RemoteRecord(self) | 489 self.bio = RemoteRecord(self) |
490 self.status = StatusClient(self) | |
491 self.canned_comments = CannedCommentsClient(self) | |
492 self.canned_keys = CannedKeysClient(self) | |
493 self.canned_values = CannedValuesClient(self) | |
481 | 494 |
482 def __str__(self): | 495 def __str__(self): |
483 return '<WebApolloInstance at %s>' % self.apollo_url | 496 return '<WebApolloInstance at %s>' % self.apollo_url |
484 | 497 |
485 def requireUser(self, email): | 498 def requireUser(self, email): |
537 data = {} | 550 data = {} |
538 for prop in self.__props: | 551 for prop in self.__props: |
539 data[prop] = getattr(self, prop) | 552 data[prop] = getattr(self, prop) |
540 return data | 553 return data |
541 | 554 |
555 def orgPerms(self): | |
556 for orgPer in self.organismPermissions: | |
557 if len(orgPer['permissions']) > 2: | |
558 orgPer['permissions'] = json.loads(orgPer['permissions']) | |
559 yield orgPer | |
560 | |
542 def __str__(self): | 561 def __str__(self): |
543 return '<User %s: %s %s <%s>>' % (self.userId, self.firstName, | 562 return '<User %s: %s %s <%s>>' % (self.userId, self.firstName, |
544 self.lastName, self.username) | 563 self.lastName, self.username) |
545 | 564 |
546 | 565 |
563 } | 582 } |
564 | 583 |
565 data.update({ | 584 data.update({ |
566 'username': self._wa.username, | 585 'username': self._wa.username, |
567 'password': self._wa.password, | 586 'password': self._wa.password, |
568 'clientToken': self._wa.clientToken, | |
569 }) | 587 }) |
570 | 588 |
571 r = requests.post(url, data=json.dumps(data), headers=headers, | 589 r = requests.post(url, data=json.dumps(data), headers=headers, |
572 verify=self.__verify, params=post_params, allow_redirects=False, **self._requestArgs) | 590 verify=self.__verify, params=post_params, allow_redirects=False, **self._requestArgs) |
573 | 591 |
675 'features': [{'uniquename': feature_id}], | 693 'features': [{'uniquename': feature_id}], |
676 } | 694 } |
677 data = self._update_data(data) | 695 data = self._update_data(data) |
678 return self.request('getComments', data) | 696 return self.request('getComments', data) |
679 | 697 |
680 def addComments(self, feature_id, comment): | 698 def addComments(self, feature_id, comments): |
681 #TODO: This is probably not great and will delete comments, if I had to guess... | 699 # TODO: This is probably not great and will delete comments, if I had to guess... |
682 data = { | 700 data = { |
683 'features': [ | 701 'features': [ |
684 { | 702 { |
685 'uniquename': feature_id, | 703 'uniquename': feature_id, |
686 'comments': [comment] | 704 'comments': comments |
687 } | 705 } |
688 ], | 706 ], |
689 } | 707 } |
690 data = self._update_data(data) | 708 data = self._update_data(data) |
691 return self.request('getComments', data) | 709 return self.request('addComments', data) |
692 | 710 |
693 def addAttribute(self, features): | 711 def addAttributes(self, feature_id, attributes): |
694 data = { | 712 nrps = [] |
695 'features': features, | 713 for (key, values) in attributes.items(): |
714 for value in values: | |
715 nrps.append({ | |
716 'tag': key, | |
717 'value': value | |
718 }) | |
719 | |
720 data = { | |
721 'features': [ | |
722 { | |
723 'uniquename': feature_id, | |
724 'non_reserved_properties': nrps | |
725 } | |
726 ] | |
727 } | |
728 data = self._update_data(data) | |
729 return self.request('addAttribute', data) | |
730 | |
731 def deleteAttribute(self, feature_id, key, value): | |
732 data = { | |
733 'features': [ | |
734 { | |
735 'uniquename': feature_id, | |
736 'non_reserved_properties': [ | |
737 {'tag': key, 'value': value} | |
738 ] | |
739 } | |
740 ] | |
696 } | 741 } |
697 data = self._update_data(data) | 742 data = self._update_data(data) |
698 return self.request('addAttribute', data) | 743 return self.request('addAttribute', data) |
699 | 744 |
700 def getFeatures(self): | 745 def getFeatures(self): |
987 data = { | 1032 data = { |
988 'format': outputFormat, | 1033 'format': outputFormat, |
989 'uuid': uuid, | 1034 'uuid': uuid, |
990 } | 1035 } |
991 return self.request('write', data) | 1036 return self.request('write', data) |
1037 | |
1038 | |
1039 class StatusClient(Client): | |
1040 CLIENT_BASE = '/availableStatus/' | |
1041 | |
1042 def addStatus(self, value): | |
1043 data = { | |
1044 'value': value | |
1045 } | |
1046 | |
1047 return self.request('createStatus', data) | |
1048 | |
1049 def findAllStatuses(self): | |
1050 return self.request('showStatus', {}) | |
1051 | |
1052 def findStatusByValue(self, value): | |
1053 statuses = self.findAllStatuses() | |
1054 statuses = [x for x in statuses if x['value'] == value] | |
1055 if len(statuses) == 0: | |
1056 raise Exception("Unknown status value") | |
1057 else: | |
1058 return statuses[0] | |
1059 | |
1060 def findStatusById(self, id_number): | |
1061 statuses = self.findAllStatuses() | |
1062 statuses = [x for x in statuses if str(x['id']) == str(id_number)] | |
1063 if len(statuses) == 0: | |
1064 raise Exception("Unknown ID") | |
1065 else: | |
1066 return statuses[0] | |
1067 | |
1068 def updateStatus(self, id_number, new_value): | |
1069 data = { | |
1070 'id': id_number, | |
1071 'new_value': new_value | |
1072 } | |
1073 | |
1074 return self.request('updateStatus', data) | |
1075 | |
1076 def deleteStatus(self, id_number): | |
1077 data = { | |
1078 'id': id_number | |
1079 } | |
1080 | |
1081 return self.request('deleteStatus', data) | |
1082 | |
1083 | |
1084 class CannedCommentsClient(Client): | |
1085 CLIENT_BASE = '/cannedComment/' | |
1086 | |
1087 def addComment(self, comment, metadata=""): | |
1088 data = { | |
1089 'comment': comment, | |
1090 'metadata': metadata | |
1091 } | |
1092 | |
1093 return self.request('createComment', data) | |
1094 | |
1095 def findAllComments(self): | |
1096 return self.request('showComment', {}) | |
1097 | |
1098 def findCommentByValue(self, value): | |
1099 comments = self.findAllComments() | |
1100 comments = [x for x in comments if x['comment'] == value] | |
1101 if len(comments) == 0: | |
1102 raise Exception("Unknown comment") | |
1103 else: | |
1104 return comments[0] | |
1105 | |
1106 def findCommentById(self, id_number): | |
1107 comments = self.findAllComments() | |
1108 comments = [x for x in comments if str(x['id']) == str(id_number)] | |
1109 if len(comments) == 0: | |
1110 raise Exception("Unknown ID") | |
1111 else: | |
1112 return comments[0] | |
1113 | |
1114 def updateComment(self, id_number, new_value, metadata=None): | |
1115 data = { | |
1116 'id': id_number, | |
1117 'new_comment': new_value | |
1118 } | |
1119 | |
1120 if metadata is not None: | |
1121 data['metadata'] = metadata | |
1122 | |
1123 return self.request('updateComment', data) | |
1124 | |
1125 def deleteComment(self, id_number): | |
1126 data = { | |
1127 'id': id_number | |
1128 } | |
1129 | |
1130 return self.request('deleteComment', data) | |
1131 | |
1132 | |
1133 class CannedKeysClient(Client): | |
1134 CLIENT_BASE = '/cannedKey/' | |
1135 | |
1136 def addKey(self, key, metadata=""): | |
1137 data = { | |
1138 'key': key, | |
1139 'metadata': metadata | |
1140 } | |
1141 | |
1142 return self.request('createKey', data) | |
1143 | |
1144 def findAllKeys(self): | |
1145 return self.request('showKey', {}) | |
1146 | |
1147 def findKeyByValue(self, value): | |
1148 keys = self.findAllKeys() | |
1149 keys = [x for x in keys if x['label'] == value] | |
1150 if len(keys) == 0: | |
1151 raise Exception("Unknown key") | |
1152 else: | |
1153 return keys[0] | |
1154 | |
1155 def findKeyById(self, id_number): | |
1156 keys = self.findAllKeys() | |
1157 keys = [x for x in keys if str(x['id']) == str(id_number)] | |
1158 if len(keys) == 0: | |
1159 raise Exception("Unknown ID") | |
1160 else: | |
1161 return keys[0] | |
1162 | |
1163 def updateKey(self, id_number, new_key, metadata=None): | |
1164 data = { | |
1165 'id': id_number, | |
1166 'new_key': new_key | |
1167 } | |
1168 | |
1169 if metadata is not None: | |
1170 data['metadata'] = metadata | |
1171 | |
1172 return self.request('updateKey', data) | |
1173 | |
1174 def deleteKey(self, id_number): | |
1175 data = { | |
1176 'id': id_number | |
1177 } | |
1178 | |
1179 return self.request('deleteKey', data) | |
1180 | |
1181 | |
1182 class CannedValuesClient(Client): | |
1183 CLIENT_BASE = '/cannedValue/' | |
1184 | |
1185 def addValue(self, value, metadata=""): | |
1186 data = { | |
1187 'value': value, | |
1188 'metadata': metadata | |
1189 } | |
1190 | |
1191 return self.request('createValue', data) | |
1192 | |
1193 def findAllValues(self): | |
1194 return self.request('showValue', {}) | |
1195 | |
1196 def findValueByValue(self, value): | |
1197 values = self.findAllValues() | |
1198 values = [x for x in values if x['label'] == value] | |
1199 if len(values) == 0: | |
1200 raise Exception("Unknown value") | |
1201 else: | |
1202 return values[0] | |
1203 | |
1204 def findValueById(self, id_number): | |
1205 values = self.findAllValues() | |
1206 values = [x for x in values if str(x['id']) == str(id_number)] | |
1207 if len(values) == 0: | |
1208 raise Exception("Unknown ID") | |
1209 else: | |
1210 return values[0] | |
1211 | |
1212 def updateValue(self, id_number, new_value, metadata=None): | |
1213 data = { | |
1214 'id': id_number, | |
1215 'new_value': new_value | |
1216 } | |
1217 | |
1218 if metadata is not None: | |
1219 data['metadata'] = metadata | |
1220 | |
1221 return self.request('updateValue', data) | |
1222 | |
1223 def deleteValue(self, id_number): | |
1224 data = { | |
1225 'id': id_number | |
1226 } | |
1227 | |
1228 return self.request('deleteValue', data) | |
992 | 1229 |
993 | 1230 |
994 class OrganismsClient(Client): | 1231 class OrganismsClient(Client): |
995 CLIENT_BASE = '/organism/' | 1232 CLIENT_BASE = '/organism/' |
996 | 1233 |
1220 else: | 1457 else: |
1221 self._sf.__dict__[key] = value | 1458 self._sf.__dict__[key] = value |
1222 | 1459 |
1223 | 1460 |
1224 def _tnType(feature): | 1461 def _tnType(feature): |
1225 if feature.type in ('gene', 'mRNA', 'exon', 'CDS'): | 1462 if feature.type in ('gene', 'mRNA', 'exon', 'CDS', 'terminator', 'tRNA'): |
1226 return feature.type | 1463 return feature.type |
1227 else: | 1464 else: |
1228 return 'exon' | 1465 return 'exon' |
1229 | 1466 |
1230 | 1467 |
1363 # Figure out which are accessible to the user | 1600 # Figure out which are accessible to the user |
1364 orgs = accessible_organisms(gx_user, all_orgs) | 1601 orgs = accessible_organisms(gx_user, all_orgs) |
1365 # Return org list | 1602 # Return org list |
1366 return orgs | 1603 return orgs |
1367 | 1604 |
1368 ## This is all for implementing the command line interface for testing. | 1605 |
1369 | 1606 def galaxy_list_users(trans, *args, **kwargs): |
1607 email = trans.get_user().email | |
1608 wa = WebApolloInstance( | |
1609 os.environ['GALAXY_WEBAPOLLO_URL'], | |
1610 os.environ['GALAXY_WEBAPOLLO_USER'], | |
1611 os.environ['GALAXY_WEBAPOLLO_PASSWORD'] | |
1612 ) | |
1613 # Assert that the email exists in apollo | |
1614 try: | |
1615 gx_user = wa.requireUser(email) | |
1616 except UnknownUserException: | |
1617 return [] | |
1618 | |
1619 # Key for cached data | |
1620 cacheKey = 'users-' + email | |
1621 # We don't want to trust "if key in cache" because between asking and fetch | |
1622 # it might through key error. | |
1623 if cacheKey not in cache: | |
1624 # However if it ISN'T there, we know we're safe to fetch + put in | |
1625 # there. | |
1626 data = _galaxy_list_users(wa, gx_user, *args, **kwargs) | |
1627 cache[cacheKey] = data | |
1628 return data | |
1629 try: | |
1630 # The cache key may or may not be in the cache at this point, it | |
1631 # /likely/ is. However we take no chances that it wasn't evicted between | |
1632 # when we checked above and now, so we reference the object from the | |
1633 # cache in preparation to return. | |
1634 data = cache[cacheKey] | |
1635 return data | |
1636 except KeyError: | |
1637 # If access fails due to eviction, we will fail over and can ensure that | |
1638 # data is inserted. | |
1639 data = _galaxy_list_users(wa, gx_user, *args, **kwargs) | |
1640 cache[cacheKey] = data | |
1641 return data | |
1642 | |
1643 | |
1644 def _galaxy_list_users(wa, gx_user, *args, **kwargs): | |
1645 # Fetch the users. | |
1646 user_data = [] | |
1647 for user in wa.users.loadUsers(): | |
1648 # Reformat | |
1649 user_data.append((user.username, user.username, False)) | |
1650 return user_data | |
1651 | |
1652 | |
1653 # This is all for implementing the command line interface for testing. | |
1370 class obj(object): | 1654 class obj(object): |
1371 pass | 1655 pass |
1372 | 1656 |
1373 | 1657 |
1374 class fakeTrans(object): | 1658 class fakeTrans(object): |
1379 def get_user(self): | 1663 def get_user(self): |
1380 o = obj() | 1664 o = obj() |
1381 o.email = self.un | 1665 o.email = self.un |
1382 return o | 1666 return o |
1383 | 1667 |
1668 | |
1669 def retry(closure, sleep=1, limit=5): | |
1670 """ | |
1671 Apollo has the bad habit of returning 500 errors if you call APIs | |
1672 too quickly, largely because of the unholy things that happen in | |
1673 grails. | |
1674 | |
1675 To deal with the fact that we cannot send an addComments call too | |
1676 quickly after a createFeature call, we have this function that will | |
1677 keep calling a closure until it works. | |
1678 """ | |
1679 count = 0 | |
1680 while True: | |
1681 count += 1 | |
1682 | |
1683 if count >= limit: | |
1684 return False | |
1685 try: | |
1686 # Try calling it | |
1687 closure() | |
1688 # If successful, exit | |
1689 return True | |
1690 except Exception as e: | |
1691 log.info(str(e)[0:100]) | |
1692 time.sleep(sleep) | |
1693 | |
1694 | |
1384 if __name__ == '__main__': | 1695 if __name__ == '__main__': |
1385 parser = argparse.ArgumentParser(description='Test access to apollo server') | 1696 parser = argparse.ArgumentParser(description='Test access to apollo server') |
1386 parser.add_argument('email', help='Email of user to test') | 1697 parser.add_argument('email', help='Email of user to test') |
1387 parser.add_argument('--action', choices=['org', 'group'], default='org', help='Data set to test, fetch a list of groups or users known to the requesting user.') | 1698 parser.add_argument('--action', choices=['org', 'group', 'users'], default='org', help='Data set to test, fetch a list of groups or users known to the requesting user.') |
1388 args = parser.parse_args() | 1699 args = parser.parse_args() |
1389 | 1700 |
1390 trans = fakeTrans(args.email) | 1701 trans = fakeTrans(args.email) |
1391 if args.action == 'org': | 1702 if args.action == 'org': |
1392 for f in galaxy_list_orgs(trans): | 1703 for f in galaxy_list_orgs(trans): |
1393 print(f) | 1704 print(f) |
1394 else: | 1705 elif args.action == 'group': |
1395 for f in galaxy_list_groups(trans): | 1706 for f in galaxy_list_groups(trans): |
1396 print(f) | 1707 print(f) |
1708 else: | |
1709 for f in galaxy_list_users(trans): | |
1710 print(f) |