a8c03ebf30ec458b7f469261e5f5a189cb3ddd6e
[arvados.git] / sdk / python / arvados / vocabulary.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: Apache-2.0
4
5 import logging
6
7 from . import api
8
9 _logger = logging.getLogger('arvados.vocabulary')
10
11 def load_vocabulary(api_client=None):
12     """Load the Arvados vocabulary from the API.
13     """
14     if api_client is None:
15         api_client = api('v1')
16     return Vocabulary(api_client.vocabulary())
17
18 class Vocabulary(object):
19     def __init__(self, voc_definition={}):
20         self.strict_keys = voc_definition.get('strict_tags', False)
21         self.key_aliases = {}
22
23         for key_id, val in voc_definition.get('tags', {}).items():
24             strict = val.get('strict', False)
25             key_labels = [l['label'] for l in val.get('labels', [])]
26             values = {}
27             for v_id, v_val in val.get('values', {}).items():
28                 labels = [l['label'] for l in v_val.get('labels', [])]
29                 values[v_id] = VocabularyValue(v_id, labels)
30             vk = VocabularyKey(key_id, key_labels, values, strict)
31             self.key_aliases[key_id.lower()] = vk
32             for alias in vk.aliases:
33                 self.key_aliases[alias.lower()] = vk
34
35     def __getitem__(self, key):
36         return self.key_aliases[key.lower()]
37
38     def convert_to_identifiers(self, obj={}):
39         """Translate key/value pairs to machine readable identifiers.
40         """
41         if not isinstance(obj, dict):
42             raise ValueError("obj must be a dict")
43         r = {}
44         for k, v in obj.items():
45             k_id, v_id = k, v
46             try:
47                 k_id = self[k].identifier
48                 try:
49                     v_id = self[k][v].identifier
50                 except KeyError:
51                     if self[k].strict:
52                         raise ValueError("value '%s' not found for key '%s'" % (v, k))
53             except KeyError:
54                 if self.strict_keys:
55                     raise KeyError("key '%s' not found" % k)
56             r[k_id] = v_id
57         return r
58
59     def convert_to_labels(self, obj={}):
60         """Translate key/value pairs to human readable labels.
61         """
62         if not isinstance(obj, dict):
63             raise ValueError("obj must be a dict")
64         r = {}
65         for k, v in obj.items():
66             k_lbl, v_lbl = k, v
67             try:
68                 k_lbl = self[k].preferred_label
69                 try:
70                     v_lbl = self[k][v].preferred_label
71                 except KeyError:
72                     if self[k].strict:
73                         raise ValueError("value '%s' not found for key '%s'" % (v, k))
74             except KeyError:
75                 if self.strict_keys:
76                     raise KeyError("key '%s' not found" % k)
77             r[k_lbl] = v_lbl
78         return r
79
80 class VocabularyData(object):
81     def __init__(self, identifier, aliases=[]):
82         self.identifier = identifier
83         self.aliases = aliases
84
85     def __getattribute__(self, name):
86         if name == 'preferred_label':
87             return self.aliases[0]
88         return super(VocabularyData, self).__getattribute__(name)
89
90 class VocabularyValue(VocabularyData):
91     def __init__(self, identifier, aliases=[]):
92         super(VocabularyValue, self).__init__(identifier, aliases)
93
94 class VocabularyKey(VocabularyData):
95     def __init__(self, identifier, aliases=[], values={}, strict=False):
96         super(VocabularyKey, self).__init__(identifier, aliases)
97         self.strict = strict
98         self.value_aliases = {}
99         for v_id, v_val in values.items():
100             self.value_aliases[v_id.lower()] = v_val
101             for v_alias in v_val.aliases:
102                 self.value_aliases[v_alias.lower()] = v_val
103
104     def __getitem__(self, key):
105         return self.value_aliases[key.lower()]