# HG changeset patch
# User gga
# Date 1505123634 14400
# Node ID 791692de64d6984e9b2a440b70d6ce1ae48c8bec
planemo upload for repository https://github.com/galaxy-genome-annotation/galaxy-tools/tree/master/tools/tripal commit f745b23c84a615bf434d717c8c0e553a012f0268
diff -r 000000000000 -r 791692de64d6 macros.xml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/macros.xml	Mon Sep 11 05:53:54 2017 -0400
@@ -0,0 +1,215 @@
+
+
+    
+        
+            python-tripal
+            
+        
+    
+
+    
+        
+            
+            
+            
+        
+    
+
+    2.0.4
+
+    
+        
+            10.1093/database/bat075
+        
+    
+
+    
+
+    `_
+
+        `Tripal REST API module `_: a Tripal module required to use these galaxy tools
+    ]]>
+
+    \$GALAXY_TRIPAL_SHARED_DIR
+
+     '.auth.yml' &&
+        echo "local:" >> '.auth.yml' &&
+        echo "    tripal_url: \"\$GALAXY_TRIPAL_URL\"" >> '.auth.yml' &&
+        echo "    username: \"\$GALAXY_TRIPAL_USER\"" >> '.auth.yml' &&
+        echo "    password: \"\$GALAXY_TRIPAL_PASSWORD\"" >> '.auth.yml' &&
+
+        TRIPAILLE_GLOBAL_CONFIG_PATH='.auth.yml'
+    ]]>
+
+    
+        
+            
+        
+    
+
+    
+        
+            
+                
+            
+
+            
+                
+            
+            
+            
+                
+            
+
+            
+            
+            
+            
+                
+            
+            
+                ^[0-9]{4}-[0-9]{2}-[0-9]{2}$
+            
+        
+    
+
+    
+        
+            
+        
+
+        
+    
+
+    
+        
+            
+            
+        
+    
+
+    
+        
+    
+
+    
+        
+            
+                
+            
+            
+                
+                
+                
+            
+        
+    
+
+    
+
+    
+
diff -r 000000000000 -r 791692de64d6 organism_get_organisms.xml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/organism_get_organisms.xml	Mon Sep 11 05:53:54 2017 -0400
@@ -0,0 +1,64 @@
+
+
+    from Tripal
+    
+        macros.xml
+    
+    
+        jq
+    
+    
+     $results
+    ]]>
+    
+    	
+    	
+    	
+    	
+    	
+    	
+    
+    
+        
+    
+    
+        
+            
+        
+    
+    
+    
+
diff -r 000000000000 -r 791692de64d6 test-data/blast.xml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/blast.xml	Mon Sep 11 05:53:54 2017 -0400
@@ -0,0 +1,68 @@
+
+
+
+  blastx
+  blastx 2.2.25 [Feb-01-2011]
+  ~Reference: Altschul, Stephen F., Thomas L. Madden, Alejandro A. Schaffer, ~Jinghui Zhang, Zheng Zhang, Webb Miller, and David J. Lipman (1997), ~"Gapped BLAST and PSI-BLAST: a new generation of protein database search~programs",  Nucleic Acids Res. 25:3389-3402.
+  /scratch/mainlab/data/lib/nr
+  lcl|1_0
+  orange1.1g015632m PAC:18136217 (mRNA) Citrus sinensis
+  2075
+  
+    
+      BLOSUM62
+      1e-06
+      11
+      1
+      F
+    
+  
+  
+    
+      1
+      lcl|1_0
+      orange1.1g015632m PAC:18136217 (mRNA) Citrus sinensis
+      2075
+      
+        
+          1
+          gi|224068663|ref|XP_002302794.1|
+          predicted protein [Populus trichocarpa] >gi|222844520|gb|EEE82067.1| predicted protein [Populus trichocarpa]
+          XP_002302794
+          409
+          
+            
+              1
+              792.727
+              2046
+              0
+              559
+              1767
+              1
+              409
+              1
+              387
+              394
+              6
+              409
+              MASVSVVPASG------NTVGVDRLPEEMNDMKIRDDKEMEATVVDGNGTEAGHIIVTTIGGKNGQPKQTISYMAERVVGHGSFGVVFQAKCLETGEAVAIKKVLQDKRYKNRELQTMRLLDHPNVVSLKHCFFSTTEKDELYLNLVLEYVPETVHRVIKHHYKMSQRMPLIYVKLYFYQICRALAYIHNTIGVCHRDIKPQNLLVNPHTHQLKLCDFGSAKVLVKGEPNISYICSRYYRAPELIFGATEYTAAIDIWSAGCVLAELLLGQPLFPGESGVDQLVEIIKVLGTPTREEIKCMNPNYTEFKFPQIKAHPWHKIFQKRMPPEAVDLVSRLLQYSPNLRSTALEALIHPFFDELRDPNTRLPNGRFLPPLFNFKPHELKGVPVDMLVKLIPEHARKQCAFLGL
+              MASVSVVPASGLRDTLGNTTGVDKLPEEMNDMKISDDKEMEAAVVDGNGTETGHIIVTTIGGKNGQPKQTISYMAERVVGHGSFGLVFQAKCLETGETVAIKKVLQDKRYKNRELQTMRLLDHPNVVSLKHCFFSTTEKDELYLNLVLEYVPETIHRVIKHYYKMSQRMPLIYVKLYFYQICRALAYIHNSIGVCHRDIKPQNLLVNPHTHQVKLCDFGSAKVLVKGEPNISYICSRYYRAPELIFGATEYTTAIDIWSAGCVLAELLLGQPLFPGESGVDQLVEIIKVLGTPTREEIKCMNPNYTEFKFPQIKAHPWHKIFHKRMPPEAVDLVSRLLQYSPNLRSTALEALIHPFFDELRDPNARLPNGRILPPLFNFKPHELKGVPVEMLVKLIPEHARKQCAFLGL
+              MASVSVVPASG      NT GVD+LPEEMNDMKI DDKEMEA VVDGNGTE GHIIVTTIGGKNGQPKQTISYMAERVVGHGSFG+VFQAKCLETGE VAIKKVLQDKRYKNRELQTMRLLDHPNVVSLKHCFFSTTEKDELYLNLVLEYVPET+HRVIKH+YKMSQRMPLIYVKLYFYQICRALAYIHN+IGVCHRDIKPQNLLVNPHTHQ+KLCDFGSAKVLVKGEPNISYICSRYYRAPELIFGATEYT AIDIWSAGCVLAELLLGQPLFPGESGVDQLVEIIKVLGTPTREEIKCMNPNYTEFKFPQIKAHPWHKIF KRMPPEAVDLVSRLLQYSPNLRSTALEALIHPFFDELRDPN RLPNGR LPPLFNFKPHELKGVPV+MLVKLIPEHARKQCAFLGL
+            
+          
+        
+      
+      
+        
+          18996442
+          6510958228
+          0
+          0
+          0.041
+          0.267
+          0.14
+        
+      
+    
+  
+
diff -r 000000000000 -r 791692de64d6 test-data/blast2go.gaf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/blast2go.gaf	Mon Sep 11 05:53:54 2017 -0400
@@ -0,0 +1,6 @@
+!gaf-version: 2.1
+	gi|328696447|ref|XP_003240026.1|			GO:0016021												
+	gi|328696447|ref|XP_003240026.1|			GO:0006511												
+	gi|328696447|ref|XP_003240026.1|			GO:0030145												
+	gi|328696447|ref|XP_003240026.1|			GO:0004803												
+	gi|328696447|ref|XP_003240026.1|			GO:0004177										
diff -r 000000000000 -r 791692de64d6 test-data/citrus_genome.fasta
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/citrus_genome.fasta	Mon Sep 11 05:53:54 2017 -0400
@@ -0,0 +1,10 @@
+>scaffold00001  length=5927163
+TTTTGTATTCTATGTCCTCTGATCTTTATACTTCTTCATTTTGTCTTTGCAAGAACCGGA
+ATTATGGGTACATCACAAATTCTCTAGGTGTGACTTGTGTTGTGGGGCCTTTTTTTtACA
+TTTCCATATTGCAAGTATTTTTTTGCTACCATTGGTATATTTGTCTGTTAAAATCAATCT
+GCTTTCACTTATGTTCGTGCGTTCTTGTTCCCTCGCCTTGCAATTGCATATCTCAAATTA
+TCTTTCTTACTTTGATTTAGATGGCCAAGGTTTTAAGCTAACTTTTTACAATGCCAATTT
+TTAAATGGTTTTCTAATGCTGTTCAAAGTTGCAGCCTTTACTTCGTATATTTGTCAGGTT
+CTGACGGGTGCGGTCGGCGGCGGGGGCTATAGCATGCGGTCTCGAGAGCCGCAAAGAAAA
+ATGGGTGGTTTTCCCGGTTTCGGCCATAACTCGTGATCGGGGCCTCCGATTCTGGTTCCG
+TTTCGTCCCACGGGACCAGCCGGGCGGGGGCATCGGATTGCAAAAGTCTTTAAATTTGAA
diff -r 000000000000 -r 791692de64d6 test-data/interpro.xml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/interpro.xml	Mon Sep 11 05:53:54 2017 -0400
@@ -0,0 +1,12 @@
+
+
+   
+	
+	  
+	    
+	    
+	  
+	
+   
+
+
diff -r 000000000000 -r 791692de64d6 test-data/sample.gff3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/sample.gff3	Mon Sep 11 05:53:54 2017 -0400
@@ -0,0 +1,21 @@
+##gff-version 3
+##sequence-region scaffold00001 4058460 4062210
+scaffold00001	phytozome6	supercontig	1	5927163	.	.	.	Name=scaffold00001;ID=scaffold00001
+scaffold00001	phytozome6	gene	4058460	4062210	.	+	.	ID=orange1.1g015632m.g;Name=orange1.1g015632m.g
+scaffold00001	phytozome6	mRNA	4058460	4062210	.	+	.	ID=PAC:18136217;Name=orange1.1g015632m;PACid=18136217;Parent=orange1.1g015632m.g
+scaffold00001	phytozome6	five_prime_UTR	4058460	4058898	.	+	.	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	five_prime_UTR	4059019	4059074	.	+	.	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	five_prime_UTR	4059172	4059234	.	+	.	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4059235	4059330	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4059422	4059514	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4059600	4059659	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4059790	4060062	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4060285	4060359	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4060480	4060536	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4060625	4060765	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4060857	4060907	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4061250	4061345	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4061417	4061500	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4061617	4061719	.	+	0	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	CDS	4061823	4061905	.	+	2	Parent=PAC:18136217;PACid=18136217
+scaffold00001	phytozome6	three_prime_UTR	4061906	4062210	.	+	.	Parent=PAC:18136217;PACid=18136217
diff -r 000000000000 -r 791692de64d6 tripal.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tripal.py	Mon Sep 11 05:53:54 2017 -0400
@@ -0,0 +1,503 @@
+import collections
+import os
+import time
+
+from abc import abstractmethod
+
+import tripal
+
+
+#############################################
+#      BEGIN IMPORT OF CACHING LIBRARY      #
+#############################################
+# This code is licensed under the MIT       #
+# License and is a copy of code publicly    #
+# available in rev.                         #
+# e27332bc82f4e327aedaec17c9b656ae719322ed  #
+# of https://github.com/tkem/cachetools/    #
+#############################################
+class DefaultMapping(collections.MutableMapping):
+
+    __slots__ = ()
+
+    @abstractmethod
+    def __contains__(self, key):  # pragma: nocover
+        return False
+
+    @abstractmethod
+    def __getitem__(self, key):  # pragma: nocover
+        if hasattr(self.__class__, '__missing__'):
+            return self.__class__.__missing__(self, key)
+        else:
+            raise KeyError(key)
+
+    def get(self, key, default=None):
+        if key in self:
+            return self[key]
+        else:
+            return default
+
+    __marker = object()
+
+    def pop(self, key, default=__marker):
+        if key in self:
+            value = self[key]
+            del self[key]
+        elif default is self.__marker:
+            raise KeyError(key)
+        else:
+            value = default
+        return value
+
+    def setdefault(self, key, default=None):
+        if key in self:
+            value = self[key]
+        else:
+            self[key] = value = default
+        return value
+
+
+DefaultMapping.register(dict)
+
+
+class _DefaultSize(object):
+    def __getitem__(self, _):
+        return 1
+
+    def __setitem__(self, _, value):
+        assert value == 1
+
+    def pop(self, _):
+        return 1
+
+
+class Cache(DefaultMapping):
+    """Mutable mapping to serve as a simple cache or cache base class."""
+
+    __size = _DefaultSize()
+
+    def __init__(self, maxsize, missing=None, getsizeof=None):
+        if missing:
+            self.__missing = missing
+        if getsizeof:
+            self.__getsizeof = getsizeof
+            self.__size = dict()
+        self.__data = dict()
+        self.__currsize = 0
+        self.__maxsize = maxsize
+
+    def __repr__(self):
+        return '%s(%r, maxsize=%r, currsize=%r)' % (
+            self.__class__.__name__,
+            list(self.__data.items()),
+            self.__maxsize,
+            self.__currsize,
+        )
+
+    def __getitem__(self, key):
+        try:
+            return self.__data[key]
+        except KeyError:
+            return self.__missing__(key)
+
+    def __setitem__(self, key, value):
+        maxsize = self.__maxsize
+        size = self.getsizeof(value)
+        if size > maxsize:
+            raise ValueError('value too large')
+        if key not in self.__data or self.__size[key] < size:
+            while self.__currsize + size > maxsize:
+                self.popitem()
+        if key in self.__data:
+            diffsize = size - self.__size[key]
+        else:
+            diffsize = size
+        self.__data[key] = value
+        self.__size[key] = size
+        self.__currsize += diffsize
+
+    def __delitem__(self, key):
+        size = self.__size.pop(key)
+        del self.__data[key]
+        self.__currsize -= size
+
+    def __contains__(self, key):
+        return key in self.__data
+
+    def __missing__(self, key):
+        value = self.__missing(key)
+        try:
+            self.__setitem__(key, value)
+        except ValueError:
+            pass  # value too large
+        return value
+
+    def __iter__(self):
+        return iter(self.__data)
+
+    def __len__(self):
+        return len(self.__data)
+
+    @staticmethod
+    def __getsizeof(value):
+        return 1
+
+    @staticmethod
+    def __missing(key):
+        raise KeyError(key)
+
+    @property
+    def maxsize(self):
+        """The maximum size of the cache."""
+        return self.__maxsize
+
+    @property
+    def currsize(self):
+        """The current size of the cache."""
+        return self.__currsize
+
+    def getsizeof(self, value):
+        """Return the size of a cache element's value."""
+        return self.__getsizeof(value)
+
+
+class _Link(object):
+
+    __slots__ = ('key', 'expire', 'next', 'prev')
+
+    def __init__(self, key=None, expire=None):
+        self.key = key
+        self.expire = expire
+
+    def __reduce__(self):
+        return _Link, (self.key, self.expire)
+
+    def unlink(self):
+        next = self.next
+        prev = self.prev
+        prev.next = next
+        next.prev = prev
+
+
+class _Timer(object):
+
+    def __init__(self, timer):
+        self.__timer = timer
+        self.__nesting = 0
+
+    def __call__(self):
+        if self.__nesting == 0:
+            return self.__timer()
+        else:
+            return self.__time
+
+    def __enter__(self):
+        if self.__nesting == 0:
+            self.__time = time = self.__timer()
+        else:
+            time = self.__time
+        self.__nesting += 1
+        return time
+
+    def __exit__(self, *exc):
+        self.__nesting -= 1
+
+    def __reduce__(self):
+        return _Timer, (self.__timer,)
+
+    def __getattr__(self, name):
+        return getattr(self.__timer, name)
+
+
+class TTLCache(Cache):
+    """LRU Cache implementation with per-item time-to-live (TTL) value."""
+
+    def __init__(self, maxsize, ttl, timer=time.time, missing=None,
+                 getsizeof=None):
+        Cache.__init__(self, maxsize, missing, getsizeof)
+        self.__root = root = _Link()
+        root.prev = root.next = root
+        self.__links = collections.OrderedDict()
+        self.__timer = _Timer(timer)
+        self.__ttl = ttl
+
+    def __contains__(self, key):
+        try:
+            link = self.__links[key]  # no reordering
+        except KeyError:
+            return False
+        else:
+            return not (link.expire < self.__timer())
+
+    def __getitem__(self, key, cache_getitem=Cache.__getitem__):
+        try:
+            link = self.__getlink(key)
+        except KeyError:
+            expired = False
+        else:
+            expired = link.expire < self.__timer()
+        if expired:
+            return self.__missing__(key)
+        else:
+            return cache_getitem(self, key)
+
+    def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
+        with self.__timer as time:
+            self.expire(time)
+            cache_setitem(self, key, value)
+        try:
+            link = self.__getlink(key)
+        except KeyError:
+            self.__links[key] = link = _Link(key)
+        else:
+            link.unlink()
+        link.expire = time + self.__ttl
+        link.next = root = self.__root
+        link.prev = prev = root.prev
+        prev.next = root.prev = link
+
+    def __delitem__(self, key, cache_delitem=Cache.__delitem__):
+        cache_delitem(self, key)
+        link = self.__links.pop(key)
+        link.unlink()
+        if link.expire < self.__timer():
+            raise KeyError(key)
+
+    def __iter__(self):
+        root = self.__root
+        curr = root.next
+        while curr is not root:
+            # "freeze" time for iterator access
+            with self.__timer as time:
+                if not (curr.expire < time):
+                    yield curr.key
+            curr = curr.next
+
+    def __len__(self):
+        root = self.__root
+        curr = root.next
+        time = self.__timer()
+        count = len(self.__links)
+        while curr is not root and curr.expire < time:
+            count -= 1
+            curr = curr.next
+        return count
+
+    def __setstate__(self, state):
+        self.__dict__.update(state)
+        root = self.__root
+        root.prev = root.next = root
+        for link in sorted(self.__links.values(), key=lambda obj: obj.expire):
+            link.next = root
+            link.prev = prev = root.prev
+            prev.next = root.prev = link
+        self.expire(self.__timer())
+
+    def __repr__(self, cache_repr=Cache.__repr__):
+        with self.__timer as time:
+            self.expire(time)
+            return cache_repr(self)
+
+    @property
+    def currsize(self):
+        with self.__timer as time:
+            self.expire(time)
+            return super(TTLCache, self).currsize
+
+    @property
+    def timer(self):
+        """The timer function used by the cache."""
+        return self.__timer
+
+    @property
+    def ttl(self):
+        """The time-to-live value of the cache's items."""
+        return self.__ttl
+
+    def expire(self, time=None):
+        """Remove expired items from the cache."""
+        if time is None:
+            time = self.__timer()
+        root = self.__root
+        curr = root.next
+        links = self.__links
+        cache_delitem = Cache.__delitem__
+        while curr is not root and curr.expire < time:
+            cache_delitem(self, curr.key)
+            del links[curr.key]
+            next = curr.next
+            curr.unlink()
+            curr = next
+
+    def clear(self):
+        with self.__timer as time:
+            self.expire(time)
+            Cache.clear(self)
+
+    def get(self, *args, **kwargs):
+        with self.__timer:
+            return Cache.get(self, *args, **kwargs)
+
+    def pop(self, *args, **kwargs):
+        with self.__timer:
+            return Cache.pop(self, *args, **kwargs)
+
+    def setdefault(self, *args, **kwargs):
+        with self.__timer:
+            return Cache.setdefault(self, *args, **kwargs)
+
+    def popitem(self):
+        """Remove and return the `(key, value)` pair least recently used that
+        has not already expired.
+
+        """
+        with self.__timer as time:
+            self.expire(time)
+            try:
+                key = next(iter(self.__links))
+            except StopIteration:
+                raise KeyError('%s is empty' % self.__class__.__name__)
+            else:
+                return (key, self.pop(key))
+
+    if hasattr(collections.OrderedDict, 'move_to_end'):
+        def __getlink(self, key):
+            value = self.__links[key]
+            self.__links.move_to_end(key)
+            return value
+    else:
+        def __getlink(self, key):
+            value = self.__links.pop(key)
+            self.__links[key] = value
+            return value
+
+
+#############################################
+#       END IMPORT OF CACHING LIBRARY       #
+#############################################
+
+cache = TTLCache(
+    100,  # Up to 100 items
+    1 * 60  # 5 minute cache life
+)
+
+
+def _get_instance():
+    return tripal.TripalInstance(
+        os.environ['GALAXY_TRIPAL_URL'],
+        os.environ['GALAXY_TRIPAL_USER'],
+        os.environ['GALAXY_TRIPAL_PASSWORD']
+    )
+
+
+def list_organisms(*args, **kwargs):
+
+    ti = _get_instance()
+
+    # Key for cached data
+    cacheKey = 'orgs'
+    # We don't want to trust "if key in cache" because between asking and fetch
+    # it might through key error.
+    if cacheKey not in cache:
+        # However if it ISN'T there, we know we're safe to fetch + put in
+        # there.
+        data = _list_organisms(ti, *args, **kwargs)
+        cache[cacheKey] = data
+        return data
+    try:
+        # The cache key may or may not be in the cache at this point, it
+        # /likely/ is. However we take no chances that it wasn't evicted between
+        # when we checked above and now, so we reference the object from the
+        # cache in preparation to return.
+        data = cache[cacheKey]
+        return data
+    except KeyError:
+        # If access fails due to eviction, we will fail over and can ensure that
+        # data is inserted.
+        data = _list_organisms(ti, *args, **kwargs)
+        cache[cacheKey] = data
+        return data
+
+
+def _list_organisms(ti, *args, **kwargs):
+    # Fetch the orgs.
+    orgs_data = []
+    for org in ti.organism.get_organisms():
+        clean_name = '%s %s' % (org['genus'], org['species'])
+        if org['infraspecific_name']:
+            clean_name += ' (%s)' % (org['infraspecific_name'])
+        orgs_data.append((clean_name, org['organism_id'], False))
+    return orgs_data
+
+
+def list_analyses(*args, **kwargs):
+
+    ti = _get_instance()
+
+    # Key for cached data
+    cacheKey = 'analyses'
+    # We don't want to trust "if key in cache" because between asking and fetch
+    # it might through key error.
+    if cacheKey not in cache:
+        # However if it ISN'T there, we know we're safe to fetch + put in
+        # there.
+
+        data = _list_analyses(ti, *args, **kwargs)
+        cache[cacheKey] = data
+        return data
+    try:
+        # The cache key may or may not be in the cache at this point, it
+        # /likely/ is. However we take no chances that it wasn't evicted between
+        # when we checked above and now, so we reference the object from the
+        # cache in preparation to return.
+        data = cache[cacheKey]
+        return data
+    except KeyError:
+        # If access fails due to eviction, we will fail over and can ensure that
+        # data is inserted.
+        data = _list_analyses(ti, *args, **kwargs)
+        cache[cacheKey] = data
+        return data
+
+
+def _list_analyses(ti, *args, **kwargs):
+    ans_data = []
+    for an in ti.analysis.get_analyses():
+        ans_data.append((an['name'], an['analysis_id'], False))
+    return ans_data
+
+
+def list_blastdbs(*args, **kwargs):
+
+    ti = _get_instance()
+
+    # Key for cached data
+    cacheKey = 'blastdbs'
+    # We don't want to trust "if key in cache" because between asking and fetch
+    # it might through key error.
+    if cacheKey not in cache:
+        # However if it ISN'T there, we know we're safe to fetch + put in
+        # there.
+        data = _list_blastdbs(ti, *args, **kwargs)
+        cache[cacheKey] = data
+        return data
+    try:
+        # The cache key may or may not be in the cache at this point, it
+        # /likely/ is. However we take no chances that it wasn't evicted between
+        # when we checked above and now, so we reference the object from the
+        # cache in preparation to return.
+        data = cache[cacheKey]
+        return data
+    except KeyError:
+        # If access fails due to eviction, we will fail over and can ensure that
+        # data is inserted.
+        data = _list_blastdbs(ti, *args, **kwargs)
+        cache[cacheKey] = data
+        return data
+
+
+def _list_blastdbs(ti, *args, **kwargs):
+    dbs_data = []
+    for db in ti.db.get_dbs():
+        dbs_data.append((db['name'], db['db_id'], False))
+    return dbs_data