source: orange-bioinformatics/obiKEGG2/databases.py @ 1535:50a0227812f7

Revision 1535:50a0227812f7, 12.0 KB checked in by ales_erjavec, 2 years ago (diff)

Added batch_get method to DBDataBase class.

Line 
1"""
2DBGET database
3"""
4from __future__ import absolute_import
5
6import re
7
8from . import entry
9from .entry import fields
10from . import api
11
12class DBDataBase(object):
13    """ A wrapper for DBGET database.
14    """
15    # ENTRY_TYPE constructor (type)
16    ENTRY_TYPE = entry.DBEntry
17   
18    # Needs to be set in a subclass or object instance
19    DB = None
20   
21    def __init__(self, **kwargs):
22        if not self.DB:
23            raise TypeError("Cannot make an instance of abstract base class %r." \
24                            % type(self).__name__)
25           
26        self.api = api.CachedKeggApi()
27        self.info = self.api.binfo(self.DB)
28        release = self.info.release
29        self.api.set_default_release(release)
30        self._keys = []
31       
32    def keys(self):
33        return list(self._keys)
34   
35    def iterkeys(self):
36        return iter(self._keys)
37   
38    def items(self):
39        return [(key, self.__getitem__(key)) for key in self.keys()]
40   
41    def iteritems(self):
42        return ((key, self.__getitem__(key)) for key in self.iterkeys())
43   
44    def values(self):
45        return [self.__getitem__(key) for key in self.keys()]
46   
47    def itervalues(self):
48        return (self.__getitem__(key) for key in self.iterkeys())
49   
50    def get(self, key, default=None):
51        if key in self:
52            return self.__getitem__(key)
53        else:
54            return default
55       
56    def has_key(self, key):
57        return self.__contains__(key)
58   
59    def __getitem__(self, key):
60        e = self.get_entry(key)
61        if e is None:
62            raise KeyError(key)
63        else:
64            return e
65   
66    def __contains__(self, key):
67        return key in set(self.keys())
68   
69    def __len__(self):
70        return len(self.keys())
71   
72    def __iter__(self):
73        return iter(self.keys())
74   
75    def get_text(self, key):
76        key = self._add_db(key)
77        return self.api.bget([key])
78   
79    def get_entry(self, key):
80        text = self.get_text(key)
81        if not text or text == "None":
82            return None
83        else:
84            return self.ENTRY_TYPE(text)
85       
86    def find(self, name):
87        """ Find ``name`` using BFIND.
88        """
89        res = self.api.bfind(self.DB, name).splitlines()
90        return [r.split(" ", 1)[0] for r in res]   
91       
92    def pre_cache(self, keys=None, batch_size=100, progress_callback=None):
93        """ Retrive all the entries and cache them locally.
94        """
95        # TODO do this in multiple threads
96   
97        if not isinstance(self.api, api.CachedKeggApi):
98            raise TypeError("Not an an instance of api.CachedKeggApi")
99       
100        if batch_size > 100 or batch_size < 1:
101            raise ValueError("Invalid batch_size")
102       
103        if keys is None:
104            keys = self.keys()
105           
106        keys = list(keys)
107        start = 0
108        while start < len(keys):
109            batch = keys[start: start + batch_size]
110            batch = map(self._add_db, batch)
111           
112            self.api.bget(batch)
113           
114            if progress_callback:
115                progress_callback(100.0 * start / len(keys))
116               
117            start += batch_size
118           
119    def batch_get(self, keys):
120        """ Batch retrieve all entries for keys. This can be
121        significantly faster then getting each entry separately
122        especially if entries are not yet cached.
123       
124        """
125        entries = []
126        batch_size = 100
127        keys = list(keys)
128        start = 0
129        while start < len(keys):
130            batch = keys[start: start + batch_size]
131            batch = map(self._add_db, batch)
132            batch_entries = self.api.bget(batch)
133            if batch_entries is not None:
134                batch_entries = batch_entries.split("///\n")
135                # Remove possible empty last line 
136                batch_entries = [e for e in batch_entries if e.strip()] 
137                entries.extend(map(self.ENTRY_TYPE, batch_entries))
138            start += batch_size
139           
140        return entries
141           
142    def _add_db(self, key):
143        """ Prefix the key with '%(DB)s:' string if not already
144        prefixed.
145        """
146        if not key.startswith(self.DB + ":"):
147            return self.DB + ":" + key
148        else:
149            return key
150       
151    @property
152    def entries(self):
153        return self.values()
154   
155@entry.entry_decorate
156class GenomeEntry(entry.DBEntry):
157    FIELDS = [("ENTRY", fields.DBEntryField),
158              ("NAME", fields.DBNameField),
159              ("DEFINITION", fields.DBDefinitionField),
160              ("ANNOTATION", fields.DBSimpleField),
161              ("TAXONOMY", fields.DBTaxonomyField),
162              ("DATA_SOURCE", fields.DBSimpleField),
163              ("ORIGINAL_DB", fields.DBSimpleField),
164              ("CHROMOSOME", fields.DBFieldWithSubsections),
165              ("STATISTICS", fields.DBSimpleField),
166              ("REFERENCE", fields.DBReference)]
167   
168    MULTIPLE_FIELDS = ["REFERENCE"]
169   
170    def __init__(self, text):
171        entry.DBEntry.__init__(self, text)
172       
173    @property
174    def entry_key(self):
175        """ Primary entry key used for querying.
176       
177        .. note:: Unlike most of the other entry types this is the
178            first listed 'NAME'.
179           
180        """
181       
182        return self.name.split(",", 1)[0]
183
184    @property
185    def taxid(self):
186        return self.TAXONOMY.taxid
187           
188    def org_code(self):
189        if self.name is not None:
190            return self.name.split(",")[0]
191        else:
192            return self.entry.split(" ")[0]
193       
194
195class Genome(DBDataBase):
196    DB = "genome"
197    ENTRY_TYPE = GenomeEntry
198   
199    # For obiTaxonomy.common_taxids mapping
200    TAXID_MAP = {"562": "511145",   # Escherichia coli K-12 MG1655
201                 "2104": "272634",  # Mycoplasma pneumoniae M129
202                 "4530": "39947",   # Oryza sativa ssp. japonica cultivar Nipponbare (Japanese rice)
203                 "4932" : "559292", # Saccharomyces cerevisiae S288C
204                 "4896": "284812",  # Schizosaccharomyces pombe 972h-
205                 }
206   
207    def __init__(self):
208        DBDataBase.__init__(self)
209        self._keys = [org.entry_id for org in self.api.list_organisms()]
210   
211    def _key_to_gn_entry_id(self, key):
212        res = self.find(key)
213        if len(res) == 0:
214            raise KeyError("Unknown key")
215        elif len(res) > 1:
216            raise ValueError("Not a unique key")
217        else:
218            return res[0]
219   
220    @classmethod
221    def common_organisms(cls):
222        return ['ath', 'bta', 'cel', 'cre', 'dre', 'ddi',
223                'dme', 'eco', 'hsa', 'mmu', 'mpn', 'osa',
224                'pfa', 'rno', 'sce', 'spo', 'zma', 'xla']
225       
226    @classmethod
227    def essential_organisms(cls):
228        return ['ddi', 'dme', 'hsa', 'mmu', 'sce']
229   
230    def search(self, string, relevance=False):
231        if relevance:
232            raise NotImplementedError("relevance is no longer supported")
233        if string in self.TAXID_MAP:
234            string = self.TAXID_MAP[string]
235           
236        res = self.api.bfind(self.DB, string)
237        if not res:
238            return []
239       
240        res = res.splitlines()
241        res = [r.split(",", 1)[0] for r in res]
242        res = [r.split(" ", 1)[1] for r in res]
243        return res
244   
245@entry.entry_decorate
246class GeneEntry(entry.DBEntry):
247    FIELDS = [("ENTRY", fields.DBEntryField),
248              ("NAME", fields.DBNameField),
249              ("DEFINITION", fields.DBDefinitionField),
250              ("ORTHOLOGY", fields.DBSimpleField),
251              ("DRUG_TARGET", fields.DBSimpleField),
252              ("PATHWAY", fields.DBPathway),
253              ("MODULE", fields.DBSimpleField),
254              ("DISEASE", fields.DBSimpleField),
255              ("CLASS", fields.DBSimpleField),
256              ("POSITION", fields.DBSimpleField),
257              ("MOTIF", fields.DBSimpleField),
258              ("DBLINKS", fields.DBDBLinks),
259              ("STRUCTURE", fields.DBSimpleField),
260              ("AASEQ", fields.DBAASeq),
261              ("NTSEQ", fields.DBNTSeq)]
262   
263    def aliases(self):
264        return [self.entry_key] + (self.name.split(",") if self.name else []) + [link[1][0] for link in self.dblinks.items() if self.dblinks]
265
266    @property
267    def alt_names(self):
268        """ For backwards compatibility.
269        """
270        return self.aliases()
271 
272class Genes(DBDataBase):
273    DB = None # Needs to be set in __init__
274    ENTRY_TYPE = GeneEntry
275   
276    def __init__(self, org_code):
277        self.DB = org_code
278        self.org_code = org_code
279        DBDataBase.__init__(self)
280        self._keys = self.api.get_genes_by_organism(org_code)
281       
282    def gene_aliases(self):
283        aliases = {}
284        for entry in self.itervalues():
285            aliases.update(dict.fromkeys(entry.aliases(), self.org_code + ":" + entry.entry_key()))
286        return aliases
287   
288
289@entry.entry_decorate
290class CompoundEntry(entry.DBEntry):
291    FIELDS = [("ENTRY", fields.DBEntryField),
292              ("NAME", fields.DBNameField),
293              ("FORMULA", fields.DBSimpleField),
294              ("MASS", fields.DBSimpleField),
295              ("REMARK", fields.DBSimpleField),
296              ("REACTION", fields.DBSimpleField),
297              ("PATHWAY", fields.DBPathway),
298              ("ENZYME", fields.DBSimpleField),
299              ("DBLINKS", fields.DBDBLinks),
300              ("ATOM", fields.DBSimpleField),
301              ("BOND", fields.DBSimpleField)
302              ]
303   
304   
305class Compounds(DBDataBase):
306    DB = "cpd"
307    ENTRY_TYPE = CompoundEntry
308   
309    def __init__(self):
310        DBDataBase.__init__(self)
311        self._keys = [] # All keys are not available
312
313KEGGCompounds = Compounds
314
315@entry.entry_decorate   
316class ReactionEntry(entry.DBEntry):
317    FIELDS = [("ENTRY", fields.DBEntryField),
318              ("NAME", fields.DBNameField),
319              ("DEFINITION", fields.DBDefinitionField),
320              ("EQUATION", fields.DBSimpleField),
321              ("ENZYME", fields.DBSimpleField)
322              ]
323   
324class Reactions(DBDataBase):
325    DB = "rn"
326    ENTRY_TYPE = ReactionEntry
327   
328    def __init__(self):
329        DBDataBase.__init__(self)
330        self._keys = [] # All keys are not available
331         
332class Brite(DBDataBase):
333    DB = "br"
334   
335class Disease(DBDataBase):
336    DB = "ds"
337       
338class Drug(DBDataBase):
339    DB = "dr"
340   
341class Enzymes(DBDataBase):
342    DB = "ec"
343   
344@entry.entry_decorate
345class OrthologyEntry(entry.DBEntry):
346    FIELDS = [("ENTRY", fields.DBEntryField),
347              ("NAME", fields.DBNameField),
348              ("CLASS", fields.DBSimpleField),
349              ("DBLINKS", fields.DBDBLinks),
350              ("GENES", fields.DBSimpleField),
351              ]
352   
353class Orthology(DBDataBase):
354    DB = "ko"
355    ENTRY_TYPE = OrthologyEntry
356   
357@entry.entry_decorate
358class PathwayEntry(entry.DBEntry):
359    FIELDS = [("ENTRY", fields.DBEntryField),
360              ("NAME", fields.DBNameField),
361              ("DESCRIPTION", fields.DBSimpleField),
362              ("CLASS", fields.DBSimpleField),
363              ("PATHWAY_MAP", fields.DBPathwayMapField),
364              ("DISEASE", fields.DBSimpleField),
365              ("DRUG", fields.DBSimpleField),
366              ("DBLINKS", fields.DBDBLinks),
367              ("ORGANISM", fields.DBSimpleField),
368              ("GENE", fields.DBGeneField),
369              ("ENZYME", fields.DBSimpleField),
370              ("COMPOUND", fields.DBSimpleField),
371              ("REFERENCE", fields.DBReference),
372              ("REL_PATHWAY", fields.DBSimpleField),
373              ("KO_PATHWAY", fields.DBSimpleField),
374              ]
375   
376    MULTIPLE_FIELDS = ["REFERENCE"]
377   
378    @property
379    def gene(self):
380        if hasattr(self, "GENE"):
381            genes = self.GENE._convert()
382        else:
383            return None
384       
385        org = self.organism
386        org_prefix = ""
387        if org:
388            match = re.findall(r"\[GN:([a-z]+)\]", org)
389            if match:
390                org_prefix = match[0] + ":"
391        genes = [org_prefix + g for g in genes]
392        return genes
393   
394class Pathways(DBDataBase):
395    DB = "path"
396    ENTRY_TYPE = PathwayEntry
397   
398    def __init__(self):
399        DBDataBase.__init__(self)
400   
Note: See TracBrowser for help on using the repository browser.