Mercurial > repos > gga > apollo_fetch_jbrowse
comparison webapollo.py @ 10:dca2fb399ee6 draft
"planemo upload for repository https://github.com/galaxy-genome-annotation/galaxy-tools/tree/master/tools/apollo commit 08015be1ee8a784e0619f961aaa724857debfd6f"
author | gga |
---|---|
date | Mon, 02 Dec 2019 05:45:36 -0500 |
parents | 54277b911534 |
children | d91f4bc313d3 |
comparison
equal
deleted
inserted
replaced
9:d8bb1f9b85b2 | 10:dca2fb399ee6 |
---|---|
3 import argparse | 3 import argparse |
4 import collections | 4 import collections |
5 import json | 5 import json |
6 import logging | 6 import logging |
7 import os | 7 import os |
8 import random | |
9 import time | 8 import time |
10 from abc import abstractmethod | 9 from abc import abstractmethod |
11 | 10 |
12 from BCBio import GFF | |
13 | |
14 from Bio import SeqIO | |
15 | |
16 import requests | 11 import requests |
17 | 12 |
18 from six.moves.builtins import next | 13 from six.moves.builtins import next |
19 from six.moves.builtins import object | 14 from six.moves.builtins import object |
20 from six.moves.builtins import str | 15 |
21 | 16 import yaml |
22 | 17 |
23 try: | |
24 import StringIO as io | |
25 except BaseException: | |
26 import io | |
27 logging.getLogger("requests").setLevel(logging.CRITICAL) | 18 logging.getLogger("requests").setLevel(logging.CRITICAL) |
28 log = logging.getLogger() | 19 log = logging.getLogger() |
29 | 20 |
30 | 21 |
31 ############################################# | 22 ############################################# |
429 OrgOrGuess(parser) | 420 OrgOrGuess(parser) |
430 parser.add_argument('--seq_fasta', type=argparse.FileType("r"), help='Fasta file, IDs used as sequence sources') | 421 parser.add_argument('--seq_fasta', type=argparse.FileType("r"), help='Fasta file, IDs used as sequence sources') |
431 parser.add_argument('--seq_raw', nargs='*', help='Sequence Names') | 422 parser.add_argument('--seq_raw', nargs='*', help='Sequence Names') |
432 | 423 |
433 | 424 |
434 def GuessOrg(args, wa): | |
435 if args.org_json: | |
436 orgs = [x.get('commonName', None) | |
437 for x in json.load(args.org_json)] | |
438 orgs = [x for x in orgs if x is not None] | |
439 return orgs | |
440 elif args.org_raw: | |
441 org = args.org_raw.strip() | |
442 if len(org) > 0: | |
443 return [org] | |
444 else: | |
445 raise Exception("Organism Common Name not provided") | |
446 elif args.org_id: | |
447 return [wa.organisms.findOrganismById(args.org_id).get('commonName', None)] | |
448 else: | |
449 raise Exception("Organism Common Name not provided") | |
450 | |
451 | |
452 def GuessCn(args, wa): | |
453 org = GuessOrg(args, wa) | |
454 seqs = [] | |
455 if args.seq_fasta: | |
456 # If we have a fasta, pull all rec ids from that. | |
457 for rec in SeqIO.parse(args.seq_fasta, 'fasta'): | |
458 seqs.append(rec.id) | |
459 elif args.seq_raw: | |
460 # Otherwise raw list. | |
461 seqs = [x.strip() for x in args.seq_raw if len(x.strip()) > 0] | |
462 | |
463 return org, seqs | |
464 | |
465 | |
466 def AssertUser(user_list): | 425 def AssertUser(user_list): |
467 if len(user_list) == 0: | 426 if len(user_list) == 0: |
468 raise UnknownUserException() | 427 raise UnknownUserException() |
469 elif len(user_list) == 1: | 428 elif len(user_list) == 1: |
470 return user_list[0] | 429 return user_list[0] |
471 else: | 430 else: |
472 raise Exception("Too many users!") | 431 raise Exception("Too many users!") |
473 | 432 |
474 | 433 |
475 def AssertAdmin(user): | |
476 if user.role == 'ADMIN': | |
477 return True | |
478 else: | |
479 raise Exception("User is not an administrator. Permission denied") | |
480 | |
481 | |
482 def PermissionCheck(user, org_cn, permission_type): | |
483 return any(org["organism"] == org_cn and permission_type in org["permissions"] for org in user.organismPermissions) | |
484 | |
485 | |
486 def PasswordGenerator(length): | |
487 chars = list('qwrtpsdfghjklzxcvbnm') | |
488 return ''.join(random.choice(chars) for _ in range(length)) | |
489 | |
490 | |
491 def IsRemoteUser(): | |
492 if 'GALAXY_WEBAPOLLO_REMOTE_USER' not in os.environ: | |
493 return False | |
494 value = os.environ['GALAXY_WEBAPOLLO_REMOTE_USER'] | |
495 if value.lower() in ('true', 't', '1'): | |
496 return True | |
497 else: | |
498 return False | |
499 | |
500 | |
501 class WebApolloInstance(object): | 434 class WebApolloInstance(object): |
502 | 435 |
503 def __init__(self, url, username, password): | 436 def __init__(self): |
504 self.apollo_url = url | 437 |
505 self.username = username | 438 if 'ARROW_GLOBAL_CONFIG_PATH' in os.environ: |
506 self.password = password | 439 |
507 | 440 with open(os.environ['ARROW_GLOBAL_CONFIG_PATH'], 'r') as config: |
508 self.annotations = AnnotationsClient(self) | 441 conf = yaml.safe_load(config) |
442 try: | |
443 instance_name = conf['__default'] | |
444 except KeyError: | |
445 raise Exception("Unknown Apollo instance and no __default provided") | |
446 self.apollo_url = conf[instance_name]['url'] | |
447 self.username = conf[instance_name]['username'] | |
448 self.password = conf[instance_name]['password'] | |
449 else: | |
450 self.apollo_url = os.environ['GALAXY_WEBAPOLLO_URL'] | |
451 self.username = os.environ['GALAXY_WEBAPOLLO_USER'] | |
452 self.password = os.environ['GALAXY_WEBAPOLLO_PASSWORD'] | |
453 | |
509 self.groups = GroupsClient(self) | 454 self.groups = GroupsClient(self) |
510 self.io = IOClient(self) | |
511 self.organisms = OrganismsClient(self) | 455 self.organisms = OrganismsClient(self) |
512 self.users = UsersClient(self) | 456 self.users = UsersClient(self) |
513 self.metrics = MetricsClient(self) | |
514 self.bio = RemoteRecord(self) | |
515 self.status = StatusClient(self) | |
516 self.canned_comments = CannedCommentsClient(self) | |
517 self.canned_keys = CannedKeysClient(self) | |
518 self.canned_values = CannedValuesClient(self) | |
519 | 457 |
520 def __str__(self): | 458 def __str__(self): |
521 return '<WebApolloInstance at %s>' % self.apollo_url | 459 return '<WebApolloInstance at %s>' % self.apollo_url |
522 | 460 |
523 def requireUser(self, email): | 461 def requireUser(self, email): |
557 for groupData in kwargs['groups']: | 495 for groupData in kwargs['groups']: |
558 groups.append(GroupObj(**groupData)) | 496 groups.append(GroupObj(**groupData)) |
559 self.groups = groups | 497 self.groups = groups |
560 | 498 |
561 self.__props = kwargs.keys() | 499 self.__props = kwargs.keys() |
562 | |
563 def isAdmin(self): | |
564 if hasattr(self, 'role'): | |
565 return self.role == self.ROLE_ADMIN | |
566 return False | |
567 | |
568 def refresh(self, wa): | |
569 # This method requires some sleeping usually. | |
570 newU = wa.users.loadUser(self).toDict() | |
571 for prop in newU: | |
572 setattr(self, prop, newU[prop]) | |
573 | |
574 def toDict(self): | |
575 data = {} | |
576 for prop in self.__props: | |
577 data[prop] = getattr(self, prop) | |
578 return data | |
579 | |
580 def orgPerms(self): | |
581 for orgPer in self.organismPermissions: | |
582 if len(orgPer['permissions']) > 2: | |
583 orgPer['permissions'] = json.loads(orgPer['permissions']) | |
584 yield orgPer | |
585 | 500 |
586 def __str__(self): | 501 def __str__(self): |
587 return '<User %s: %s %s <%s>>' % (self.userId, self.firstName, | 502 return '<User %s: %s %s <%s>>' % (self.userId, self.firstName, |
588 self.lastName, self.username) | 503 self.lastName, self.username) |
589 | 504 |
645 # @see self.body for HTTP response body | 560 # @see self.body for HTTP response body |
646 raise Exception("Unexpected response from apollo %s: %s" % | 561 raise Exception("Unexpected response from apollo %s: %s" % |
647 (r.status_code, r.text)) | 562 (r.status_code, r.text)) |
648 | 563 |
649 | 564 |
650 class MetricsClient(Client): | |
651 CLIENT_BASE = '/metrics/' | |
652 | |
653 def getServerMetrics(self): | |
654 return self.get('metrics', {}) | |
655 | |
656 | |
657 class AnnotationsClient(Client): | |
658 CLIENT_BASE = '/annotationEditor/' | |
659 | |
660 def _update_data(self, data): | |
661 if not hasattr(self, '_extra_data'): | |
662 raise Exception("Please call setSequence first") | |
663 | |
664 data.update(self._extra_data) | |
665 return data | |
666 | |
667 def setSequence(self, sequence, organism): | |
668 self._extra_data = { | |
669 'sequence': sequence, | |
670 'organism': organism, | |
671 } | |
672 | |
673 def setDescription(self, featureDescriptions): | |
674 data = { | |
675 'features': featureDescriptions, | |
676 } | |
677 data = self._update_data(data) | |
678 return self.request('setDescription', data) | |
679 | |
680 def setName(self, uniquename, name): | |
681 # TODO | |
682 data = { | |
683 'features': [ | |
684 { | |
685 'uniquename': uniquename, | |
686 'name': name, | |
687 } | |
688 ], | |
689 } | |
690 data = self._update_data(data) | |
691 return self.request('setName', data) | |
692 | |
693 def setNames(self, features): | |
694 # TODO | |
695 data = { | |
696 'features': features, | |
697 } | |
698 data = self._update_data(data) | |
699 return self.request('setName', data) | |
700 | |
701 def setStatus(self, statuses): | |
702 # TODO | |
703 data = { | |
704 'features': statuses, | |
705 } | |
706 data = self._update_data(data) | |
707 return self.request('setStatus', data) | |
708 | |
709 def setSymbol(self, symbols): | |
710 data = { | |
711 'features': symbols, | |
712 } | |
713 data.update(self._extra_data) | |
714 return self.request('setSymbol', data) | |
715 | |
716 def getComments(self, feature_id): | |
717 data = { | |
718 'features': [{'uniquename': feature_id}], | |
719 } | |
720 data = self._update_data(data) | |
721 return self.request('getComments', data) | |
722 | |
723 def addComments(self, feature_id, comments): | |
724 # TODO: This is probably not great and will delete comments, if I had to guess... | |
725 data = { | |
726 'features': [ | |
727 { | |
728 'uniquename': feature_id, | |
729 'comments': comments | |
730 } | |
731 ], | |
732 } | |
733 data = self._update_data(data) | |
734 return self.request('addComments', data) | |
735 | |
736 def addAttributes(self, feature_id, attributes): | |
737 nrps = [] | |
738 for (key, values) in attributes.items(): | |
739 for value in values: | |
740 nrps.append({ | |
741 'tag': key, | |
742 'value': value | |
743 }) | |
744 | |
745 data = { | |
746 'features': [ | |
747 { | |
748 'uniquename': feature_id, | |
749 'non_reserved_properties': nrps | |
750 } | |
751 ] | |
752 } | |
753 data = self._update_data(data) | |
754 return self.request('addAttribute', data) | |
755 | |
756 def deleteAttribute(self, feature_id, key, value): | |
757 data = { | |
758 'features': [ | |
759 { | |
760 'uniquename': feature_id, | |
761 'non_reserved_properties': [ | |
762 {'tag': key, 'value': value} | |
763 ] | |
764 } | |
765 ] | |
766 } | |
767 data = self._update_data(data) | |
768 return self.request('addAttribute', data) | |
769 | |
770 def getFeatures(self): | |
771 data = self._update_data({}) | |
772 return self.request('getFeatures', data) | |
773 | |
774 def getSequence(self, uniquename): | |
775 data = { | |
776 'features': [ | |
777 {'uniquename': uniquename} | |
778 ] | |
779 } | |
780 data = self._update_data(data) | |
781 return self.request('getSequence', data) | |
782 | |
783 def addFeature(self, feature, trustme=False): | |
784 if not trustme: | |
785 raise NotImplementedError("Waiting on better docs from project. If you know what you are doing, pass trustme=True to this function.") | |
786 | |
787 data = { | |
788 'features': feature, | |
789 } | |
790 data = self._update_data(data) | |
791 return self.request('addFeature', data) | |
792 | |
793 def addTranscript(self, transcript, trustme=False): | |
794 if not trustme: | |
795 raise NotImplementedError("Waiting on better docs from project. If you know what you are doing, pass trustme=True to this function.") | |
796 | |
797 data = {} | |
798 data.update(transcript) | |
799 data = self._update_data(data) | |
800 return self.request('addTranscript', data) | |
801 | |
802 # addExon, add/delete/updateComments, addTranscript skipped due to docs | |
803 | |
804 def duplicateTranscript(self, transcriptId): | |
805 data = { | |
806 'features': [{'uniquename': transcriptId}] | |
807 } | |
808 | |
809 data = self._update_data(data) | |
810 return self.request('duplicateTranscript', data) | |
811 | |
812 def setTranslationStart(self, uniquename, start): | |
813 data = { | |
814 'features': [{ | |
815 'uniquename': uniquename, | |
816 'location': { | |
817 'fmin': start | |
818 } | |
819 }] | |
820 } | |
821 data = self._update_data(data) | |
822 return self.request('setTranslationStart', data) | |
823 | |
824 def setTranslationEnd(self, uniquename, end): | |
825 data = { | |
826 'features': [{ | |
827 'uniquename': uniquename, | |
828 'location': { | |
829 'fmax': end | |
830 } | |
831 }] | |
832 } | |
833 data = self._update_data(data) | |
834 return self.request('setTranslationEnd', data) | |
835 | |
836 def setLongestOrf(self, uniquename): | |
837 data = { | |
838 'features': [{ | |
839 'uniquename': uniquename, | |
840 }] | |
841 } | |
842 data = self._update_data(data) | |
843 return self.request('setLongestOrf', data) | |
844 | |
845 def setBoundaries(self, uniquename, start, end): | |
846 data = { | |
847 'features': [{ | |
848 'uniquename': uniquename, | |
849 'location': { | |
850 'fmin': start, | |
851 'fmax': end, | |
852 } | |
853 }] | |
854 } | |
855 data = self._update_data(data) | |
856 return self.request('setBoundaries', data) | |
857 | |
858 def getSequenceAlterations(self): | |
859 data = { | |
860 } | |
861 data = self._update_data(data) | |
862 return self.request('getSequenceAlterations', data) | |
863 | |
864 def setReadthroughStopCodon(self, uniquename): | |
865 data = { | |
866 'features': [{ | |
867 'uniquename': uniquename, | |
868 }] | |
869 } | |
870 data = self._update_data(data) | |
871 return self.request('setReadthroughStopCodon', data) | |
872 | |
873 def deleteSequenceAlteration(self, uniquename): | |
874 data = { | |
875 'features': [{ | |
876 'uniquename': uniquename, | |
877 }] | |
878 } | |
879 data = self._update_data(data) | |
880 return self.request('deleteSequenceAlteration', data) | |
881 | |
882 def flipStrand(self, uniquenames): | |
883 data = { | |
884 'features': [ | |
885 {'uniquename': x} for x in uniquenames | |
886 ] | |
887 } | |
888 data = self._update_data(data) | |
889 return self.request('flipStrand', data) | |
890 | |
891 def mergeExons(self, exonA, exonB): | |
892 data = { | |
893 'features': [ | |
894 {'uniquename': exonA}, | |
895 {'uniquename': exonB}, | |
896 ] | |
897 } | |
898 data = self._update_data(data) | |
899 return self.request('mergeExons', data) | |
900 | |
901 # def splitExon(): pass | |
902 | |
903 def deleteFeatures(self, uniquenames): | |
904 assert isinstance(uniquenames, collections.Iterable) | |
905 data = { | |
906 'features': [ | |
907 {'uniquename': x} for x in uniquenames | |
908 ] | |
909 } | |
910 data = self._update_data(data) | |
911 return self.request('deleteFeature', data) | |
912 | |
913 # def deleteExon(): pass | |
914 | |
915 # def makeIntron(self, uniquename, ): pass | |
916 | |
917 def getSequenceSearchTools(self): | |
918 return self.get('getSequenceSearchTools', {}) | |
919 | |
920 def getCannedComments(self): | |
921 return self.get('getCannedComments', {}) | |
922 | |
923 def searchSequence(self, searchTool, sequence, database): | |
924 data = { | |
925 'key': searchTool, | |
926 'residues': sequence, | |
927 'database_id': database, | |
928 } | |
929 return self.request('searchSequences', data) | |
930 | |
931 def getGff3(self, uniquenames): | |
932 assert isinstance(uniquenames, collections.Iterable) | |
933 data = { | |
934 'features': [ | |
935 {'uniquename': x} for x in uniquenames | |
936 ] | |
937 } | |
938 data = self._update_data(data) | |
939 return self.request('getGff3', data, isJson=False) | |
940 | |
941 | |
942 class GroupsClient(Client): | 565 class GroupsClient(Client): |
943 CLIENT_BASE = '/group/' | 566 CLIENT_BASE = '/group/' |
944 | |
945 def createGroup(self, name): | |
946 data = {'name': name} | |
947 return self.request('createGroup', data) | |
948 | |
949 def getOrganismPermissionsForGroup(self, group): | |
950 data = { | |
951 'id': group.groupId, | |
952 'name': group.name, | |
953 } | |
954 return self.request('getOrganismPermissionsForGroup', data) | |
955 | |
956 def loadGroup(self, group): | |
957 return self.loadGroupById(group.groupId) | |
958 | |
959 def loadGroupById(self, groupId): | |
960 res = self.request('loadGroups', {'groupId': groupId}) | |
961 if isinstance(res, list): | |
962 # We can only match one, right? | |
963 return GroupObj(**res[0]) | |
964 else: | |
965 return res | |
966 | |
967 def loadGroupByName(self, name): | |
968 res = self.request('loadGroups', {'name': name}) | |
969 if isinstance(res, list): | |
970 # We can only match one, right? | |
971 return GroupObj(**res[0]) | |
972 else: | |
973 return res | |
974 | 567 |
975 def loadGroups(self, group=None): | 568 def loadGroups(self, group=None): |
976 res = self.request('loadGroups', {}) | 569 res = self.request('loadGroups', {}) |
977 data = [GroupObj(**x) for x in res] | 570 data = [GroupObj(**x) for x in res] |
978 if group is not None: | 571 if group is not None: |
979 data = [x for x in data if x.name == group] | 572 data = [x for x in data if x.name == group] |
980 | 573 |
981 return data | 574 return data |
982 | 575 |
983 def deleteGroup(self, group): | |
984 data = { | |
985 'id': group.groupId, | |
986 'name': group.name, | |
987 } | |
988 return self.request('deleteGroup', data) | |
989 | |
990 def updateGroup(self, group, newName): | |
991 # TODO: Sure would be nice if modifying ``group.name`` would invoke | |
992 # this? | |
993 data = { | |
994 'id': group.groupId, | |
995 'name': newName, | |
996 } | |
997 return self.request('updateGroup', data) | |
998 | |
999 def updateOrganismPermission(self, group, organismName, | |
1000 administrate=False, write=False, read=False, | |
1001 export=False): | |
1002 data = { | |
1003 'groupId': group.groupId, | |
1004 'organism': organismName, | |
1005 'ADMINISTRATE': administrate, | |
1006 'WRITE': write, | |
1007 'EXPORT': export, | |
1008 'READ': read, | |
1009 } | |
1010 return self.request('updateOrganismPermission', data) | |
1011 | |
1012 def updateMembership(self, group, users): | |
1013 data = { | |
1014 'groupId': group.groupId, | |
1015 'user': [user.email for user in users] | |
1016 } | |
1017 return self.request('updateMembership', data) | |
1018 | |
1019 | |
1020 class IOClient(Client): | |
1021 CLIENT_BASE = '/IOService/' | |
1022 | |
1023 def write(self, exportType='FASTA', seqType='peptide', | |
1024 exportFormat='text', sequences=None, organism=None, | |
1025 output='text', exportAllSequences=False, | |
1026 exportGff3Fasta=False): | |
1027 if exportType not in ('FASTA', 'GFF3'): | |
1028 raise Exception("exportType must be one of FASTA, GFF3") | |
1029 | |
1030 if seqType not in ('peptide', 'cds', 'cdna', 'genomic'): | |
1031 raise Exception("seqType must be one of peptide, cds, dna, genomic") | |
1032 | |
1033 if exportFormat not in ('gzip', 'text'): | |
1034 raise Exception("exportFormat must be one of gzip, text") | |
1035 | |
1036 if output not in ('file', 'text'): | |
1037 raise Exception("output must be one of file, text") | |
1038 | |
1039 data = { | |
1040 'type': exportType, | |
1041 'seqType': seqType, | |
1042 'format': exportFormat, | |
1043 'sequences': sequences, | |
1044 'organism': organism, | |
1045 'output': output, | |
1046 'exportAllSequences': exportAllSequences, | |
1047 'exportGff3Fasta': exportGff3Fasta, | |
1048 } | |
1049 | |
1050 return self.request('write', data, isJson=output == 'file') | |
1051 | |
1052 def download(self, uuid, outputFormat='gzip'): | |
1053 | |
1054 if outputFormat.lower() not in ('gzip', 'text'): | |
1055 raise Exception("outputFormat must be one of file, text") | |
1056 | |
1057 data = { | |
1058 'format': outputFormat, | |
1059 'uuid': uuid, | |
1060 } | |
1061 return self.request('write', data) | |
1062 | |
1063 | |
1064 class StatusClient(Client): | |
1065 CLIENT_BASE = '/availableStatus/' | |
1066 | |
1067 def addStatus(self, value): | |
1068 data = { | |
1069 'value': value | |
1070 } | |
1071 | |
1072 return self.request('createStatus', data) | |
1073 | |
1074 def findAllStatuses(self): | |
1075 return self.request('showStatus', {}) | |
1076 | |
1077 def findStatusByValue(self, value): | |
1078 statuses = self.findAllStatuses() | |
1079 statuses = [x for x in statuses if x['value'] == value] | |
1080 if len(statuses) == 0: | |
1081 raise Exception("Unknown status value") | |
1082 else: | |
1083 return statuses[0] | |
1084 | |
1085 def findStatusById(self, id_number): | |
1086 statuses = self.findAllStatuses() | |
1087 statuses = [x for x in statuses if str(x['id']) == str(id_number)] | |
1088 if len(statuses) == 0: | |
1089 raise Exception("Unknown ID") | |
1090 else: | |
1091 return statuses[0] | |
1092 | |
1093 def updateStatus(self, id_number, new_value): | |
1094 data = { | |
1095 'id': id_number, | |
1096 'new_value': new_value | |
1097 } | |
1098 | |
1099 return self.request('updateStatus', data) | |
1100 | |
1101 def deleteStatus(self, id_number): | |
1102 data = { | |
1103 'id': id_number | |
1104 } | |
1105 | |
1106 return self.request('deleteStatus', data) | |
1107 | |
1108 | |
1109 class CannedCommentsClient(Client): | |
1110 CLIENT_BASE = '/cannedComment/' | |
1111 | |
1112 def addComment(self, comment, metadata=""): | |
1113 data = { | |
1114 'comment': comment, | |
1115 'metadata': metadata | |
1116 } | |
1117 | |
1118 return self.request('createComment', data) | |
1119 | |
1120 def findAllComments(self): | |
1121 return self.request('showComment', {}) | |
1122 | |
1123 def findCommentByValue(self, value): | |
1124 comments = self.findAllComments() | |
1125 comments = [x for x in comments if x['comment'] == value] | |
1126 if len(comments) == 0: | |
1127 raise Exception("Unknown comment") | |
1128 else: | |
1129 return comments[0] | |
1130 | |
1131 def findCommentById(self, id_number): | |
1132 comments = self.findAllComments() | |
1133 comments = [x for x in comments if str(x['id']) == str(id_number)] | |
1134 if len(comments) == 0: | |
1135 raise Exception("Unknown ID") | |
1136 else: | |
1137 return comments[0] | |
1138 | |
1139 def updateComment(self, id_number, new_value, metadata=None): | |
1140 data = { | |
1141 'id': id_number, | |
1142 'new_comment': new_value | |
1143 } | |
1144 | |
1145 if metadata is not None: | |
1146 data['metadata'] = metadata | |
1147 | |
1148 return self.request('updateComment', data) | |
1149 | |
1150 def deleteComment(self, id_number): | |
1151 data = { | |
1152 'id': id_number | |
1153 } | |
1154 | |
1155 return self.request('deleteComment', data) | |
1156 | |
1157 | |
1158 class CannedKeysClient(Client): | |
1159 CLIENT_BASE = '/cannedKey/' | |
1160 | |
1161 def addKey(self, key, metadata=""): | |
1162 data = { | |
1163 'key': key, | |
1164 'metadata': metadata | |
1165 } | |
1166 | |
1167 return self.request('createKey', data) | |
1168 | |
1169 def findAllKeys(self): | |
1170 return self.request('showKey', {}) | |
1171 | |
1172 def findKeyByValue(self, value): | |
1173 keys = self.findAllKeys() | |
1174 keys = [x for x in keys if x['label'] == value] | |
1175 if len(keys) == 0: | |
1176 raise Exception("Unknown key") | |
1177 else: | |
1178 return keys[0] | |
1179 | |
1180 def findKeyById(self, id_number): | |
1181 keys = self.findAllKeys() | |
1182 keys = [x for x in keys if str(x['id']) == str(id_number)] | |
1183 if len(keys) == 0: | |
1184 raise Exception("Unknown ID") | |
1185 else: | |
1186 return keys[0] | |
1187 | |
1188 def updateKey(self, id_number, new_key, metadata=None): | |
1189 data = { | |
1190 'id': id_number, | |
1191 'new_key': new_key | |
1192 } | |
1193 | |
1194 if metadata is not None: | |
1195 data['metadata'] = metadata | |
1196 | |
1197 return self.request('updateKey', data) | |
1198 | |
1199 def deleteKey(self, id_number): | |
1200 data = { | |
1201 'id': id_number | |
1202 } | |
1203 | |
1204 return self.request('deleteKey', data) | |
1205 | |
1206 | |
1207 class CannedValuesClient(Client): | |
1208 CLIENT_BASE = '/cannedValue/' | |
1209 | |
1210 def addValue(self, value, metadata=""): | |
1211 data = { | |
1212 'value': value, | |
1213 'metadata': metadata | |
1214 } | |
1215 | |
1216 return self.request('createValue', data) | |
1217 | |
1218 def findAllValues(self): | |
1219 return self.request('showValue', {}) | |
1220 | |
1221 def findValueByValue(self, value): | |
1222 values = self.findAllValues() | |
1223 values = [x for x in values if x['label'] == value] | |
1224 if len(values) == 0: | |
1225 raise Exception("Unknown value") | |
1226 else: | |
1227 return values[0] | |
1228 | |
1229 def findValueById(self, id_number): | |
1230 values = self.findAllValues() | |
1231 values = [x for x in values if str(x['id']) == str(id_number)] | |
1232 if len(values) == 0: | |
1233 raise Exception("Unknown ID") | |
1234 else: | |
1235 return values[0] | |
1236 | |
1237 def updateValue(self, id_number, new_value, metadata=None): | |
1238 data = { | |
1239 'id': id_number, | |
1240 'new_value': new_value | |
1241 } | |
1242 | |
1243 if metadata is not None: | |
1244 data['metadata'] = metadata | |
1245 | |
1246 return self.request('updateValue', data) | |
1247 | |
1248 def deleteValue(self, id_number): | |
1249 data = { | |
1250 'id': id_number | |
1251 } | |
1252 | |
1253 return self.request('deleteValue', data) | |
1254 | |
1255 | 576 |
1256 class OrganismsClient(Client): | 577 class OrganismsClient(Client): |
1257 CLIENT_BASE = '/organism/' | 578 CLIENT_BASE = '/organism/' |
1258 | |
1259 def addOrganism(self, commonName, directory, blatdb=None, species=None, | |
1260 genus=None, public=False): | |
1261 data = { | |
1262 'commonName': commonName, | |
1263 'directory': directory, | |
1264 'publicMode': public, | |
1265 } | |
1266 | |
1267 if blatdb is not None: | |
1268 data['blatdb'] = blatdb | |
1269 if genus is not None: | |
1270 data['genus'] = genus | |
1271 if species is not None: | |
1272 data['species'] = species | |
1273 | |
1274 return self.request('addOrganism', data) | |
1275 | 579 |
1276 def findAllOrganisms(self): | 580 def findAllOrganisms(self): |
1277 orgs = self.request('findAllOrganisms', {}) | 581 orgs = self.request('findAllOrganisms', {}) |
1278 if not isinstance(orgs, (list,)): | 582 if not isinstance(orgs, (list,)): |
1279 orgs = [] | 583 orgs = [] |
1280 return orgs | 584 return orgs |
1281 | 585 |
1282 def findOrganismByCn(self, cn): | |
1283 orgs = self.findAllOrganisms() | |
1284 orgs = [x for x in orgs if x['commonName'] == cn] | |
1285 if len(orgs) == 0: | |
1286 raise Exception("Unknown common name") | |
1287 else: | |
1288 return orgs[0] | |
1289 | |
1290 def findOrganismById(self, id_number): | |
1291 orgs = self.findAllOrganisms() | |
1292 orgs = [x for x in orgs if str(x['id']) == str(id_number)] | |
1293 if len(orgs) == 0: | |
1294 raise Exception("Unknown ID") | |
1295 else: | |
1296 return orgs[0] | |
1297 | |
1298 def deleteOrganism(self, organismId): | |
1299 return self.request('deleteOrganism', {'id': organismId}) | |
1300 | |
1301 def deleteOrganismFeatures(self, organismId): | |
1302 return self.request('deleteOrganismFeatures', {'id': organismId}) | |
1303 | |
1304 def getSequencesForOrganism(self, commonName): | |
1305 return self.request('getSequencesForOrganism', {'organism': commonName}) | |
1306 | |
1307 def updateOrganismInfo(self, organismId, commonName, directory, blatdb=None, species=None, genus=None, public=False): | |
1308 data = { | |
1309 'id': organismId, | |
1310 'name': commonName, | |
1311 'directory': directory, | |
1312 'publicMode': public, | |
1313 } | |
1314 | |
1315 if blatdb is not None: | |
1316 data['blatdb'] = blatdb | |
1317 if genus is not None: | |
1318 data['genus'] = genus | |
1319 if species is not None: | |
1320 data['species'] = species | |
1321 | |
1322 return self.request('updateOrganismInfo', data) | |
1323 | |
1324 | 586 |
1325 class UsersClient(Client): | 587 class UsersClient(Client): |
1326 CLIENT_BASE = '/user/' | 588 CLIENT_BASE = '/user/' |
1327 | 589 |
1328 # Real one | 590 def loadUsers(self): |
1329 # def getOrganismPermissionsForUser(self, user): | |
1330 # data = { | |
1331 # 'userId': user.userId, | |
1332 # } | |
1333 # return self.request('getOrganismPermissionsForUser', data) | |
1334 | |
1335 # Utter frigging hack | |
1336 def getOrganismPermissionsForUser(self, user): | |
1337 return self.loadUser(user).organismPermissions | |
1338 | |
1339 def updateOrganismPermission(self, user, organism, administrate=False, | |
1340 write=False, export=False, read=False): | |
1341 data = { | |
1342 'userId': user.userId, | |
1343 'organism': organism, | |
1344 'ADMINISTRATE': administrate, | |
1345 'WRITE': write, | |
1346 'EXPORT': export, | |
1347 'READ': read, | |
1348 } | |
1349 return self.request('updateOrganismPermission', data) | |
1350 | |
1351 def loadUser(self, user): | |
1352 return self.loadUserById(user.userId) | |
1353 | |
1354 def loadUserById(self, userId): | |
1355 res = self.request('loadUsers', {'userId': userId}) | |
1356 if isinstance(res, list): | |
1357 # We can only match one, right? | |
1358 return UserObj(**res[0]) | |
1359 else: | |
1360 return res | |
1361 | |
1362 def loadUsers(self, email=None): | |
1363 res = self.request('loadUsers', {}) | 591 res = self.request('loadUsers', {}) |
592 | |
1364 data = [UserObj(**x) for x in res] | 593 data = [UserObj(**x) for x in res] |
1365 if email is not None: | |
1366 data = [x for x in data if x.username == email] | |
1367 | 594 |
1368 return data | 595 return data |
1369 | 596 |
1370 def addUserToGroup(self, group, user): | 597 |
1371 data = {'group': group.name, 'userId': user.userId} | 598 def handle_credentials(user): |
1372 return self.request('addUserToGroup', data) | 599 if hasattr(user, 'new_password'): |
1373 | 600 f = open("Apollo_credentials.txt", "w") |
1374 def removeUserFromGroup(self, group, user): | 601 f.write('Username:\t%s\nPassword:\t%s' % (user.username, user.new_password)) |
1375 data = {'group': group.name, 'userId': user.userId} | |
1376 return self.request('removeUserFromGroup', data) | |
1377 | |
1378 def createUser(self, email, firstName, lastName, newPassword, role="user", groups=None, addToHistory=False): | |
1379 data = { | |
1380 'firstName': firstName, | |
1381 'lastName': lastName, | |
1382 'email': email, | |
1383 'role': role, | |
1384 'groups': [] if groups is None else groups, | |
1385 # 'availableGroups': [], | |
1386 'newPassword': newPassword, | |
1387 # 'organismPermissions': [], | |
1388 } | |
1389 returnData = self.request('createUser', data) | |
1390 if addToHistory and not IsRemoteUser(): | |
1391 f = open("Apollo_credentials.txt", "w") | |
1392 f.write('Username: %s\tPassword: %s' % (email, newPassword)) | |
1393 return returnData | |
1394 | |
1395 def assertOrCreateUser(self, email): | |
1396 try: | |
1397 gx_user = AssertUser(self.loadUsers(email)) | |
1398 except Exception: | |
1399 self.createUser(email, email, email, PasswordGenerator(12), role='user', addToHistory=True) | |
1400 gx_user = AssertUser(self.loadUsers(email)) | |
1401 return gx_user | |
1402 | |
1403 def deleteUser(self, user): | |
1404 return self.request('deleteUser', {'userId': user.userId}) | |
1405 | |
1406 def updateUser(self, user, email, firstName, lastName, newPassword): | |
1407 data = { | |
1408 'userId': user.userId, | |
1409 'email': email, | |
1410 'firstName': firstName, | |
1411 'lastName': lastName, | |
1412 'newPassword': newPassword, | |
1413 } | |
1414 return self.request('updateUser', data) | |
1415 | |
1416 | |
1417 class RemoteRecord(Client): | |
1418 CLIENT_BASE = None | |
1419 | |
1420 def ParseRecord(self, cn): | |
1421 org = self._wa.organisms.findOrganismByCn(cn) | |
1422 self._wa.annotations.setSequence(org['commonName'], org['id']) | |
1423 | |
1424 data = io.StringIO(self._wa.io.write( | |
1425 exportType='GFF3', | |
1426 seqType='genomic', | |
1427 exportAllSequences=False, | |
1428 exportGff3Fasta=True, | |
1429 output="text", | |
1430 exportFormat="text", | |
1431 sequences=cn, | |
1432 )) | |
1433 data.seek(0) | |
1434 | |
1435 for record in GFF.parse(data): | |
1436 yield WebApolloSeqRecord(record, self._wa) | |
1437 | |
1438 | |
1439 class WebApolloSeqRecord(object): | |
1440 def __init__(self, sr, wa): | |
1441 self._sr = sr | |
1442 self._wa = wa | |
1443 | |
1444 def __dir__(self): | |
1445 return dir(self._sr) | |
1446 | |
1447 def __getattr__(self, key): | |
1448 if key in ('_sr', '_wa'): | |
1449 return self.__dict__[key] | |
1450 else: | |
1451 if key == 'features': | |
1452 return (WebApolloSeqFeature(x, self._wa) | |
1453 for x in self._sr.__dict__[key]) | |
1454 else: | |
1455 return self._sr.__dict__[key] | |
1456 | |
1457 def __setattr__(self, key, value): | |
1458 if key in ('_sd', '_wa'): | |
1459 self.__dict__[key] = value | |
1460 else: | |
1461 self._sr.__dict__[key] = value | |
1462 # Methods acting on the SeqRecord object | |
1463 | |
1464 | |
1465 class WebApolloSeqFeature(object): | |
1466 def __init__(self, sf, wa): | |
1467 self._sf = sf | |
1468 self._wa = wa | |
1469 | |
1470 def __dir__(self): | |
1471 return dir(self._sf) | |
1472 | |
1473 def __getattr__(self, key): | |
1474 if key in ('_sf', '_wa'): | |
1475 return self.__dict__[key] | |
1476 else: | |
1477 return self._sf.__dict__[key] | |
1478 | |
1479 def __setattr__(self, key, value): | |
1480 if key in ('_sf', '_wa'): | |
1481 self.__dict__[key] = value | |
1482 else: | |
1483 # Methods acting on the SeqFeature object | |
1484 if key == 'location': | |
1485 if value.strand != self._sf.location.strand: | |
1486 self.wa.annotations.flipStrand( | |
1487 self._sf.qualifiers['ID'][0] | |
1488 ) | |
1489 | |
1490 self.wa.annotations.setBoundaries( | |
1491 self._sf.qualifiers['ID'][0], | |
1492 value.start, | |
1493 value.end, | |
1494 ) | |
1495 | |
1496 self._sf.__dict__[key] = value | |
1497 else: | |
1498 self._sf.__dict__[key] = value | |
1499 | |
1500 | |
1501 def _tnType(feature): | |
1502 if feature.type in ('gene', 'mRNA', 'exon', 'CDS', 'terminator', 'tRNA'): | |
1503 return feature.type | |
1504 else: | |
1505 return 'exon' | |
1506 | |
1507 | |
1508 def _yieldFeatData(features): | |
1509 for f in features: | |
1510 current = { | |
1511 'location': { | |
1512 'strand': f.strand, | |
1513 'fmin': int(f.location.start), | |
1514 'fmax': int(f.location.end), | |
1515 }, | |
1516 'type': { | |
1517 'name': _tnType(f), | |
1518 'cv': { | |
1519 'name': 'sequence', | |
1520 } | |
1521 }, | |
1522 } | |
1523 if f.type in ('gene', 'mRNA'): | |
1524 current['name'] = f.qualifiers.get('Name', [f.id])[0] | |
1525 if hasattr(f, 'sub_features') and len(f.sub_features) > 0: | |
1526 current['children'] = [x for x in _yieldFeatData(f.sub_features)] | |
1527 | |
1528 yield current | |
1529 | |
1530 | |
1531 def featuresToFeatureSchema(features): | |
1532 compiled = [] | |
1533 for feature in features: | |
1534 # if feature.type != 'gene': | |
1535 # log.warn("Not able to handle %s features just yet...", feature.type) | |
1536 # continue | |
1537 | |
1538 for x in _yieldFeatData([feature]): | |
1539 compiled.append(x) | |
1540 return compiled | |
1541 | 602 |
1542 | 603 |
1543 def accessible_organisms(user, orgs): | 604 def accessible_organisms(user, orgs): |
1544 permissionMap = { | 605 permissionMap = { |
1545 x['organism']: x['permissions'] | 606 x['organism']: x['permissions'] |
1557 ] | 618 ] |
1558 | 619 |
1559 | 620 |
1560 def galaxy_list_groups(trans, *args, **kwargs): | 621 def galaxy_list_groups(trans, *args, **kwargs): |
1561 email = trans.get_user().email | 622 email = trans.get_user().email |
1562 wa = WebApolloInstance( | 623 wa = WebApolloInstance() |
1563 os.environ['GALAXY_WEBAPOLLO_URL'], | |
1564 os.environ['GALAXY_WEBAPOLLO_USER'], | |
1565 os.environ['GALAXY_WEBAPOLLO_PASSWORD'] | |
1566 ) | |
1567 | 624 |
1568 # Key for cached data | 625 # Key for cached data |
1569 cacheKey = 'groups-' + email | 626 cacheKey = 'groups-' + email |
1570 # We don't want to trust "if key in cache" because between asking and fetch | 627 # We don't want to trust "if key in cache" because between asking and fetch |
1571 # it might through key error. | 628 # it might through key error. |
1599 return group_data | 656 return group_data |
1600 | 657 |
1601 | 658 |
1602 def galaxy_list_orgs(trans, *args, **kwargs): | 659 def galaxy_list_orgs(trans, *args, **kwargs): |
1603 email = trans.get_user().email | 660 email = trans.get_user().email |
1604 wa = WebApolloInstance( | 661 wa = WebApolloInstance() |
1605 os.environ['GALAXY_WEBAPOLLO_URL'], | |
1606 os.environ['GALAXY_WEBAPOLLO_USER'], | |
1607 os.environ['GALAXY_WEBAPOLLO_PASSWORD'] | |
1608 ) | |
1609 try: | 662 try: |
1610 gx_user = wa.requireUser(email) | 663 gx_user = wa.requireUser(email) |
1611 except UnknownUserException: | 664 except UnknownUserException: |
1612 return [] | 665 return [] |
1613 | 666 |
1633 orgs = accessible_organisms(gx_user, all_orgs) | 686 orgs = accessible_organisms(gx_user, all_orgs) |
1634 # Return org list | 687 # Return org list |
1635 return orgs | 688 return orgs |
1636 | 689 |
1637 | 690 |
1638 def galaxy_list_users(trans, *args, **kwargs): | |
1639 email = trans.get_user().email | |
1640 wa = WebApolloInstance( | |
1641 os.environ['GALAXY_WEBAPOLLO_URL'], | |
1642 os.environ['GALAXY_WEBAPOLLO_USER'], | |
1643 os.environ['GALAXY_WEBAPOLLO_PASSWORD'] | |
1644 ) | |
1645 # Assert that the email exists in apollo | |
1646 try: | |
1647 gx_user = wa.requireUser(email) | |
1648 except UnknownUserException: | |
1649 return [] | |
1650 | |
1651 # Key for cached data | |
1652 cacheKey = 'users-' + email | |
1653 # We don't want to trust "if key in cache" because between asking and fetch | |
1654 # it might through key error. | |
1655 if cacheKey not in cache: | |
1656 # However if it ISN'T there, we know we're safe to fetch + put in | |
1657 # there. | |
1658 data = _galaxy_list_users(wa, gx_user, *args, **kwargs) | |
1659 cache[cacheKey] = data | |
1660 return data | |
1661 try: | |
1662 # The cache key may or may not be in the cache at this point, it | |
1663 # /likely/ is. However we take no chances that it wasn't evicted between | |
1664 # when we checked above and now, so we reference the object from the | |
1665 # cache in preparation to return. | |
1666 data = cache[cacheKey] | |
1667 return data | |
1668 except KeyError: | |
1669 # If access fails due to eviction, we will fail over and can ensure that | |
1670 # data is inserted. | |
1671 data = _galaxy_list_users(wa, gx_user, *args, **kwargs) | |
1672 cache[cacheKey] = data | |
1673 return data | |
1674 | |
1675 | |
1676 def _galaxy_list_users(wa, gx_user, *args, **kwargs): | |
1677 # Fetch the users. | |
1678 user_data = [] | |
1679 for user in wa.users.loadUsers(): | |
1680 # Reformat | |
1681 user_data.append((user.username, user.username, False)) | |
1682 return user_data | |
1683 | |
1684 | |
1685 # This is all for implementing the command line interface for testing. | 691 # This is all for implementing the command line interface for testing. |
1686 class obj(object): | 692 class obj(object): |
1687 pass | 693 pass |
1688 | 694 |
1689 | 695 |
1696 o = obj() | 702 o = obj() |
1697 o.email = self.un | 703 o.email = self.un |
1698 return o | 704 return o |
1699 | 705 |
1700 | 706 |
1701 def retry(closure, sleep=1, limit=5): | |
1702 """ | |
1703 Apollo has the bad habit of returning 500 errors if you call APIs | |
1704 too quickly, largely because of the unholy things that happen in | |
1705 grails. | |
1706 | |
1707 To deal with the fact that we cannot send an addComments call too | |
1708 quickly after a createFeature call, we have this function that will | |
1709 keep calling a closure until it works. | |
1710 """ | |
1711 count = 0 | |
1712 while True: | |
1713 count += 1 | |
1714 | |
1715 if count >= limit: | |
1716 return False | |
1717 try: | |
1718 # Try calling it | |
1719 closure() | |
1720 # If successful, exit | |
1721 return True | |
1722 except Exception as e: | |
1723 log.info(str(e)[0:100]) | |
1724 time.sleep(sleep) | |
1725 | |
1726 | |
1727 if __name__ == '__main__': | 707 if __name__ == '__main__': |
1728 parser = argparse.ArgumentParser(description='Test access to apollo server') | 708 parser = argparse.ArgumentParser(description='Test access to apollo server') |
1729 parser.add_argument('email', help='Email of user to test') | 709 parser.add_argument('email', help='Email of user to test') |
1730 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.') | 710 parser.add_argument('--action', choices=['org', 'group'], default='org', help='Data set to test, fetch a list of groups or orgs known to the requesting user.') |
1731 args = parser.parse_args() | 711 args = parser.parse_args() |
1732 | 712 |
1733 trans = fakeTrans(args.email) | 713 trans = fakeTrans(args.email) |
1734 if args.action == 'org': | 714 if args.action == 'org': |
1735 for f in galaxy_list_orgs(trans): | 715 print(galaxy_list_orgs(trans)) |
1736 print(f) | |
1737 elif args.action == 'group': | 716 elif args.action == 'group': |
1738 for f in galaxy_list_groups(trans): | 717 print(galaxy_list_groups(trans)) |
1739 print(f) | |
1740 else: | |
1741 for f in galaxy_list_users(trans): | |
1742 print(f) |