1#!/usr/bin/env python3
 2
 3"""Implement the get_constant accessor function for reading algorithm constants.
 4The actual constants are stored in settings.CONSTANTS_MODULE (alg/constants.py).
 5"""
 6
 7import runpy
 8
 9from chart.project import settings
10from chart.common.decorators import memoized_noargs
11
12
13@memoized_noargs
14def all_constants():
15    return runpy.run_path(settings.CONSTANTS_FILE)['data']
16
17
18def get_constant(name, sid=None, scid=None, sensing_time=None):
19    """Retrieve constant `name`.
20    If `scid` is specified and the constant has different values for different
21    scid, return the correct value.
22    If `time` is specified and the constant has different values for different time
23    ranges, retrieve the value for `time` (not implemented).
24    """
25    constants = all_constants()
26
27    if sid is not None:
28        scid = sid.scid
29
30    if name not in constants:
31        raise ValueError('Constant {name} not found'.format(name=name))
32
33    if 'value' in constants[name]:
34        if 'values' in constants[name]:
35            raise ValueError('Constant {name} has both value and values set'.format(name=name))
36
37        return constants[name]['value']
38
39    if 'values' not in constants[name]:
40        raise ValueError('Constant {name} has neither value nor values set'.format(name=name))
41
42    potentials = constants[name]['values']
43
44    for potential in reversed(potentials):
45        if 'value' not in potential:
46            raise ValueError('Value missing for constant {name}'.format(name=name))
47
48        if 'scid' in potential and potential['scid'] != scid:
49            continue
50
51        if 'sensing_start' in potential and sensing_time is None:
52            raise ValueError('Cannot retrieve constant {name} without specifying time'.format(
53                    name=name))
54
55        if 'sensing_start' in potential and sensing_time < potential['sensing_start']:
56            continue
57
58        return potential['value']
59
60    raise ValueError('Cannot find suitable constant {name} time {time} scid {scid}'.format(
61            name=name, time=sensing_time, scid=scid))
62
63
64def get_constant_description(name):
65    """Retrieve the description for constant `name`."""
66    constant = all_constants()[name]
67    return constant.get('description')
68
69
70class Constant:
71# class Constant(int):
72    """Define constants inside algorithm code.
73
74    Initial implementation of a replacement to the original Constants mechanism, which
75    is unwealdy because it's hard to convert ordinary code to constant-using code, and because
76    the old code split the values into different files."""
77
78    def __init__(self, value, description=None):
79        self._value = value
80        self.description = description
81
82    # def __new__(cls, value, description=None):
83    #     if isinstance(value, int):
84    #         return int.__new__(cls, value)
85
86    #     elif isinstance(value, dict):
87            # pass
88
89    def hello(self):
90        return 'Hello {h}'.format(h=self)
91
92    def value(self, sid=None, sensing_time=None):
93        return self._value