Coverage for src/susi/base/config/config.py: 89%
66 statements
« prev ^ index » next coverage.py v7.5.0, created at 2025-06-13 14:15 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2025-06-13 14:15 +0000
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3"""
4Module for configuration matters
6@author: hoelken, iglesias, feller
7"""
8from datetime import datetime
9import yaml
10import os
11from ..logging import Logging
13from .base import Base
14from .calibdata import CalibData
15from .cam import Cam
16from .data import Data
17from .spectropol import SpectroPol
18from .reduc import Reduc
20log = Logging.get_logger()
23class Config:
24 """
25 DataItem for configuration.
27 It's main purpose is to configure the source for the processing.
28 See documentation of instance variables for configuration options available.
29 """
31 def __init__(self):
32 #: Base configuration
33 self.base: Base = Base()
34 #: Definition where to find the data and where to write it to
35 self.data: Data = Data()
36 #: Camera specific configuration
37 self.cam: Cam = Cam.with_defaults('cam1')
38 #: Paths to calibration data as darks and flats
39 self.calib_data: CalibData = CalibData()
40 #: Configuration for the Spectro-Polarimeter
41 self.spol: SpectroPol = SpectroPol()
42 #: Configuration for other reduction stpes
43 self.reduc: Reduc = Reduc()
44 #: start time of the process
45 self.start: datetime = None
46 #: end time of the process
47 self.stop: datetime = None
49 def cam_defaults(self, c_name: str) -> None:
50 """Load the camera specific defaults for the given camera"""
51 self.cam = Cam.with_defaults(c_name)
53 @staticmethod
54 def set_log_level(level: int) -> None:
55 """Change the logging log level"""
56 Logging.set_log_level(level)
58 @staticmethod
59 def from_yaml(file_path: str):
60 """
61 Create a config from a yaml file.
63 The file must specify a `config` section. All instance variable names
64 are supported as keywords in this section.
65 The `config` section is parsed as python dictionary which is then
66 used to create the Configuration. See `from_dict` for details.
68 ### Params
69 - file_path: the location of the config file
71 ### Returns
72 the created Config
73 """
74 with open(file_path, "r") as stream:
75 data = yaml.safe_load(stream)
76 conf = Config()
77 if 'config' in data:
78 if 'cam' in data['config'] and 'name' in data['config']['cam']:
79 c = data['config']['cam']['name']
80 else:
81 c = 'cam1'
82 conf = Config.from_dict(data['config'], c)
83 for c in ['cam1', 'cam2', 'cam3']:
84 if c in data:
85 conf = Config.from_dict(data[c], c)
86 if 'start' in data:
87 conf.start = datetime.fromisoformat(data['start'])
88 if 'stop' in data:
89 conf.start = datetime.fromisoformat(data['stop'])
90 return conf
92 @staticmethod
93 def from_dict(data: dict, c_name: str = 'cam1'):
94 """
95 Create a config from a dictionary.
97 All instance variable names are supported as keywords.
98 All keywords are optional, if the keyword is not present the default will be used.
99 There are some special keys that must follow a specific syntax if specified
101 - 'data_shape': The data shape must be given as a dictionary with keys 'x' and 'y'
102 for vertical and horizontal shape. The values of both keys must be lists
103 of integers of length 2, e.g. `{'x': [1, 2], 'y': [3, 4]}`
105 - 'roi_keys': The Region Of Interest (ROI) keys must be given as a dictionary with
106 arbitrary keys. The values of both keys must be lists of length 2.
107 Example `{'x': ['X0', 'X1'], 'y': ['Y0', 'Y1'], 'lambda': ['L0', 'L1']}`
109 ### Params
110 - data: The dictionary to parse.
111 - c_name: The name of the camera to set defaults for.
113 ### Returns
114 the created Config
115 """
116 config = Config()
117 config.cam_defaults(c_name)
118 for t in config.__dict__.keys():
119 if t in data:
120 getattr(config, t).amend_from_dict(data[t])
121 return config
123 def input_pattern(self) -> str:
124 """
125 Generate the lookup pattern for the input values
127 ### Returns
128 A glob pattern to look up input files.
129 """
130 return os.path.join(
131 self.data.root, self.data.level, self.data.dataset, self.cam.name, self.data.pattern + self.data.ext
132 )
134 def is_cam3(self):
135 return self.cam.name == self.cam.C3
137 def __repr__(self) -> str:
138 txt = 'Current SUSI Datareduction config\n'
139 for t in self.__dict__.keys():
140 txt += repr(getattr(self, t)) + '\n'
141 return txt
143 def info(self) -> None:
144 """Write the current config to the log"""
145 log.info(repr(self))
147 def out_path(self):
148 """Get the out path to use"""
149 return self.data.out_path if self.data.out_path else os.path.expanduser('~')