Coverage for /opt/conda/envs/apienv/lib/python3.10/site-packages/daiquiri/core/components/parameteriser.py: 90%
111 statements
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-15 02:12 +0000
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-15 02:12 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3import glob
4import os
5from flask import g
6from marshmallow import fields
7import ruamel
9from daiquiri.core import marshal
10from daiquiri.core.components import (
11 Component,
12 ComponentActor,
13 ComponentActorSchema,
14 ComponentResource,
15 actor,
16)
17from daiquiri.core.schema import ErrorSchema
18from daiquiri.core.schema.validators import ValidatedRegexp
19from daiquiri.core.schema.metadata import paginated
20from daiquiri.core.schema.parameteriser import (
21 ParameteriserConfigSchema,
22 AvailableParametersSchema,
23)
25try:
26 from bliss.scanning.scan_saving import ESRFScanSaving
27except ImportError:
28 ESRFScanSaving = None
31import logging
34logger = logging.getLogger(__name__)
37class AvailableParametersResource(ComponentResource):
38 @marshal(
39 inp={
40 "type": fields.Str(
41 required=True,
42 metadata={"description": "The type of parameters to list"},
43 )
44 },
45 out=[
46 [200, paginated(AvailableParametersSchema), "List of parameters"],
47 [400, ErrorSchema(), "Could not get list of parameters"],
48 ],
49 )
50 def get(self, type: str):
51 """Get the list of parameter sets for the selected parameter type"""
52 try:
53 params = self._parent.get_available_parameters(type)
54 return {"total": len(params), "rows": params}, 200
55 except Exception:
56 message = f"Could not get parameters for type: {type}"
57 logger.exception(message)
58 return {"error": message}, 400
61class Parameteriser(Component):
62 _base_url = "parameteriser"
63 _config_schema = ParameteriserConfigSchema()
64 _config_export = ["parametertypes"]
66 def setup(self):
67 self._parameters_actors = []
68 self.register_route(AvailableParametersResource, "")
69 self._generate_parameter_actors()
71 def reload(self):
72 self._generate_parameter_actors()
74 def _generate_parameter_actors(self):
75 """Dynamically generate parameter actor resources"""
77 def post(self, **kwargs):
78 pass
80 def preprocess(self, **kwargs):
81 kwargs["sessionid"] = g.blsession.get("sessionid")
83 return kwargs
85 if not self._config.get("actors"):
86 self._config["actors"] = {}
88 for _, actors in self._config["parametertypes"].items():
89 for actor_desc in actors:
90 actor_name = actor_desc["actor"]
92 self._config["actors"][actor_name] = actor_name
94 if actor_name in self._actors:
95 continue
97 self._actors.append(actor_name)
98 self._parameters_actors.append(actor_name)
100 fn = actor(actor_name, spawn=True, preprocess=True)(post)
102 act_res = type(
103 actor_name,
104 (ComponentResource,),
105 {"post": fn, "preprocess": preprocess},
106 )
107 self.register_actor_route(act_res, f"/{actor_name}")
109 def set_proposal(self, **data):
110 args = self._saving.eval_saving_arguments(
111 data, partial_evaulation=True, raise_on_missing=False
112 )
114 arg_map = {
115 "proposal": "proposal_name",
116 "sample": "collection_name",
117 "dataset": "dataset_name",
118 }
120 for arg, value in args.items():
121 # dont set unevaluated keys (we really only want to set the base path)
122 if "{" not in value:
123 key = arg
125 # For ESRF data policy saving we have do some arg mapping as per:
126 # daiquiri/core/saving/bliss_esrf.py
127 if arg in arg_map:
128 key = arg_map[arg]
130 setattr(self._saving.scan_saving, key, value)
132 @property
133 def root_directory(self):
134 """Return the root proposal directory"""
135 # TODO: this is a bit bliss specific
136 if isinstance(self._saving.scan_saving, ESRFScanSaving):
137 return os.path.join(
138 self._saving.scan_saving.base_path,
139 self._saving.scan_saving.proposal_name,
140 self._saving.scan_saving.beamline,
141 )
142 else:
143 return self._saving.scan_saving.base_path
145 def actor_started(self, actid, actor):
146 """Callback when an actor starts
148 Just set the correct proposal
149 """
150 if actor.name in self._parameters_actors:
151 self.set_proposal(**actor.all_data)
152 actor.update(
153 root_directory=self.root_directory,
154 parameter_root=self._config.get("root", "parameteriser"),
155 )
157 logger.info(f"Actor '{actor.name}' with id '{actid}' started")
159 def get_available_parameters(self, type):
160 self.set_proposal(**{"sessionid": g.blsession.get("sessionid")})
161 parameter_path = os.path.join(
162 self.root_directory, self._config.get("root", "parameteriser"), type
163 )
164 files = glob.glob(f"{parameter_path}/*.yml")
166 parameters = []
167 for file in files:
168 with open(file) as stream:
169 loader = ruamel.yaml.Loader(stream)
170 data = loader.get_single_data()
171 print("param", file, data)
172 parameters.append(
173 {
174 "parameter_type": data.pop("parameter_type"),
175 "instance_type": data.pop("instance_type"),
176 "name": data["name"],
177 "file_name": os.path.basename(file),
178 "directory": os.path.dirname(file),
179 "parameters": data,
180 }
181 )
183 return parameters
186class ParameteriserActorSchema(ComponentActorSchema):
187 name = ValidatedRegexp("word-dash", required=True, metadata={"title": "Name"})
188 overwrite = fields.Bool()
190 def __init__(self, *args, **kwargs):
191 super().__init__(*args, **kwargs)
193 self.Meta.cache = False
195 if not hasattr(self.Meta, "uischema"):
196 self.Meta.uischema = {}
198 self.Meta.uischema["overwrite"] = {
199 "classNames": "hidden-row",
200 "ui:widget": "hidden",
201 }
204class ParameteriserActor(ComponentActor):
205 def method(self, name, overwrite=False, **kwargs):
206 parameter_path = os.path.join(
207 self["root_directory"],
208 self["parameter_root"],
209 self.parameter_type,
210 f"{self.name}_{name}.yml",
211 )
213 if os.path.exists(parameter_path) and not overwrite:
214 raise RuntimeError(
215 f"Parameter file `{parameter_path}` already exists and not overwriting"
216 )
218 base_dir = os.path.dirname(parameter_path)
219 if not os.path.exists(base_dir):
220 os.makedirs(base_dir)
222 all_kwargs = {
223 "parameter_type": self.parameter_type,
224 "instance_type": self.name,
225 "name": name,
226 **kwargs,
227 }
229 yaml = ruamel.yaml.YAML()
230 yaml.allow_duplicate_keys = True
231 yaml.default_flow_style = False
232 yaml.indent(mapping=2, sequence=4, offset=2)
234 with open(parameter_path, "wt") as parameter_file:
235 yaml.dump(all_kwargs, stream=parameter_file)