18574: Adds support for value list conversion.
[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                     if isinstance(v, list):
50                         v_id = [self[k][x].identifier for x in v]
51                     else:
52                         v_id = self[k][v].identifier
53                 except KeyError:
54                     if self[k].strict:
55                         raise ValueError("value '%s' not found for key '%s'" % (v, k))
56             except KeyError:
57                 if self.strict_keys:
58                     raise KeyError("key '%s' not found" % k)
59             r[k_id] = v_id
60         return r
61
62     def convert_to_labels(self, obj={}):
63         """Translate key/value pairs to human readable labels.
64         """
65         if not isinstance(obj, dict):
66             raise ValueError("obj must be a dict")
67         r = {}
68         for k, v in obj.items():
69             k_lbl, v_lbl = k, v
70             try:
71                 k_lbl = self[k].preferred_label
72                 try:
73                     if isinstance(v, list):
74                         v_lbl = [self[k][x].preferred_label for x in v]
75                     else:
76                         v_lbl = self[k][v].preferred_label
77                 except KeyError:
78                     if self[k].strict:
79                         raise ValueError("value '%s' not found for key '%s'" % (v, k))
80             except KeyError:
81                 if self.strict_keys:
82                     raise KeyError("key '%s' not found" % k)
83             r[k_lbl] = v_lbl
84         return r
85
86 class VocabularyData(object):
87     def __init__(self, identifier, aliases=[]):
88         self.identifier = identifier
89         self.aliases = aliases
90
91     def __getattribute__(self, name):
92         if name == 'preferred_label':
93             return self.aliases[0]
94         return super(VocabularyData, self).__getattribute__(name)
95
96 class VocabularyValue(VocabularyData):
97     def __init__(self, identifier, aliases=[]):
98         super(VocabularyValue, self).__init__(identifier, aliases)
99
100 class VocabularyKey(VocabularyData):
101     def __init__(self, identifier, aliases=[], values={}, strict=False):
102         super(VocabularyKey, self).__init__(identifier, aliases)
103         self.strict = strict
104         self.value_aliases = {}
105         for v_id, v_val in values.items():
106             self.value_aliases[v_id.lower()] = v_val
107             for v_alias in v_val.aliases:
108                 self.value_aliases[v_alias.lower()] = v_val
109
110     def __getitem__(self, key):
111         return self.value_aliases[key.lower()]