Skip to content
Snippets Groups Projects
Commit ab265bae authored by Karim Ahmed's avatar Karim Ahmed
Browse files

Merge branch 'fix/update_config' into 'master'

Fix/update config

See merge request detectors/pycalibration!470
parents e3897629 2b4c1836
No related branches found
No related tags found
1 merge request!470Fix/update config
...@@ -18,7 +18,6 @@ agipd_options = { ...@@ -18,7 +18,6 @@ agipd_options = {
'msg': "Range list of maximum pulse indices " 'msg': "Range list of maximum pulse indices "
"(--max-pulses start end step). " "(--max-pulses start end step). "
"3 max input elements. "}, "3 max input elements. "},
"calfile": {'typ': str},
} }
data_mapping = { data_mapping = {
...@@ -32,7 +31,11 @@ available_options = { ...@@ -32,7 +31,11 @@ available_options = {
"MID_DET_AGIPD1M-1": [agipd_options, data_mapping] "MID_DET_AGIPD1M-1": [agipd_options, data_mapping]
} }
formatter = lambda prog: argparse.HelpFormatter(prog, max_help_position=52)
def formatter(prog):
return argparse.HelpFormatter(prog, max_help_position=52)
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Request update of configuration', formatter_class=formatter) description='Request update of configuration', formatter_class=formatter)
required_args = parser.add_argument_group('required arguments') required_args = parser.add_argument_group('required arguments')
...@@ -46,6 +49,13 @@ required_args.add_argument('--cycle', type=str, help='The facility cycle.') ...@@ -46,6 +49,13 @@ required_args.add_argument('--cycle', type=str, help='The facility cycle.')
parser.add_argument('--apply', action='store_true', parser.add_argument('--apply', action='store_true',
help='Apply and push the requested ' help='Apply and push the requested '
'configuration update to the git.') 'configuration update to the git.')
parser.add_argument(
'--webservice-address',
type=str,
default="tcp://max-exfl016:5555",
help=('The port of the webservice to update '
'calibration configurations repository.')
)
# remove help calls as they will cause the argument parser to exit # remove help calls as they will cause the argument parser to exit
add_help = False add_help = False
if "-h" in sys.argv: if "-h" in sys.argv:
...@@ -58,10 +68,11 @@ if "--help" in sys.argv: ...@@ -58,10 +68,11 @@ if "--help" in sys.argv:
known, remaining = parser.parse_known_args() known, remaining = parser.parse_known_args()
args = vars(known) args = vars(known)
karabo_id = args["karabo_id"] karabo_id = args["karabo_id"]
bool_keys = [] webservice_address = args["webservice_address"]
bool_keys = []
# Avoid erros when karabo_id not yet given through the command line. # Avoid erros when karabo_id not given through the command line.
if karabo_id is not None: if karabo_id is not None:
# adding "no" bools to available options # adding "no" bools to available options
for det, val in available_options[karabo_id][0].items(): for det, val in available_options[karabo_id][0].items():
...@@ -69,38 +80,46 @@ if karabo_id is not None: ...@@ -69,38 +80,46 @@ if karabo_id is not None:
bool_keys.append(det) bool_keys.append(det)
for b in bool_keys: for b in bool_keys:
available_options[karabo_id][0]['no-{}'.format(det)] = {'typ': bool} available_options[karabo_id][0][f'no-{b}'] = {'typ': bool}
for exposed_options in available_options[karabo_id]: for exposed_options in available_options[karabo_id]:
for option, info in exposed_options.items(): for option, info in exposed_options.items():
metavar = None typ = info['typ']
choices = info.get('choices')
if info['typ'] == list: if info['typ'] == list:
nargs = '+' arguments = {
"action": 'append',
"nargs": '+',
}
typ = str typ = str
action = 'append'
# Avoid having a big line of choices in the help message. # Avoid having a big line of choices in the help message.
if 'choices' in info.keys(): if 'choices' in info.keys():
metavar = option.upper() arguments.update({
"metavar": option.upper(),
"choices": choices,
})
else: else:
action = None arguments = {
nargs = None "choices": choices,
typ = info['typ'] }
# Add help messages # Add help messages
help_msg = "" help_msg = ""
if 'msg' in info.keys(): if 'msg' in info.keys():
help_msg += info['msg'] help_msg += info['msg']
if 'choices' in info.keys():
choices = info['choices']
else:
choices = None
help_msg += f"Type: {info['typ'].__name__} ".upper()
parser.add_argument(f"--{option}", type=typ, action=action,
metavar=metavar, nargs=nargs, choices=choices,
help=help_msg)
parser.add_argument('--instrument', type=str, choices=["CALLAB"],
help='This is only used for testing purposes.')
help_msg += f"Type: {info['typ'].__name__} ".upper()
parser.add_argument(
f"--{option}",
type=typ,
help=help_msg,
**arguments
)
parser.add_argument(
'--instrument',
type=str, choices=["CALLAB"],
help='This is only used for testing purposes.'
)
if add_help: if add_help:
sys.argv.append("--help") sys.argv.append("--help")
...@@ -135,10 +154,7 @@ for key, value in args.items(): ...@@ -135,10 +154,7 @@ for key, value in args.items():
new_conf[task][instrument][karabo_id][key.replace('no-', '')] = False # noqa new_conf[task][instrument][karabo_id][key.replace('no-', '')] = False # noqa
# avoid saving the "no-"key into the updated config # avoid saving the "no-"key into the updated config
continue continue
# Assure adding an empty string for new empty
# str. updates (e.g. calfile)
if isinstance(key, str) and (value == '' or value == ' '):
value = '""'
# checking if data-mapping was updated. # checking if data-mapping was updated.
if key in data_mapping.keys(): if key in data_mapping.keys():
if 'data-mapping' not in new_conf.keys(): if 'data-mapping' not in new_conf.keys():
...@@ -160,7 +176,7 @@ print(f"Sending the following update: \n {pyaml}") ...@@ -160,7 +176,7 @@ print(f"Sending the following update: \n {pyaml}")
print("-" * 80) print("-" * 80)
con = zmq.Context() con = zmq.Context()
socket = con.socket(zmq.REQ) socket = con.socket(zmq.REQ)
con = socket.connect("tcp://max-exfl016:5555") con = socket.connect(webservice_address)
msg = "','".join(["update_conf", "SASEX", args["karabo_id"], msg = "','".join(["update_conf", "SASEX", args["karabo_id"],
instrument, args["cycle"], args["proposal"], instrument, args["cycle"], args["proposal"],
json.dumps(new_conf), str(args["apply"])]) json.dumps(new_conf), str(args["apply"])])
......
...@@ -171,9 +171,8 @@ def merge(source: Dict, destination: Dict) -> Dict: ...@@ -171,9 +171,8 @@ def merge(source: Dict, destination: Dict) -> Dict:
def change_config(config, updated_config, karabo_id, instrument, def change_config(config, updated_config, karabo_id, instrument,
cycle, proposal, apply=False) -> bytes: cycle, proposal, apply=False) -> bytes:
""" """ Change the configuration of a proposal
Change the configuration of a proposal
If no proposal specific configuration yet exists, one is first created If no proposal specific configuration yet exists, one is first created
based on the default configuration of the proposal based on the default configuration of the proposal
...@@ -182,44 +181,56 @@ def change_config(config, updated_config, karabo_id, instrument, ...@@ -182,44 +181,56 @@ def change_config(config, updated_config, karabo_id, instrument,
:param config: repo config as given in YAML config file :param config: repo config as given in YAML config file
:param updated_config: a dictionary containing the updated config :param updated_config: a dictionary containing the updated config
:param instrument: the instrument to change config for :param karabo_id: karabo detector identifier.
:param instrument: the instrument to change the config for
:param cycle: the cycle to change config for :param cycle: the cycle to change config for
:param proposal: the proposal to change config for :param proposal: the proposal to change config for
:param apply: set to True to actually commit a change, otherwise a dry-run :param apply: set to True to actually commit a change, otherwise a dry-run
is performed is performed
:return: The updated config to the requesting zmq socket :return: The updated config to the requesting zmq socket
""" """
# first check if a proposal specific config exists, if not create one
repo = Repo(config['local-path']) repo = Repo(config['local-path'])
repo.remote().pull() repo.remote().pull()
prop_dir = os.path.join(repo.working_tree_dir, cycle) # A cycle directory is created, if doesn't exists.
os.makedirs(prop_dir, exist_ok=True) cyc_dir = Path(repo.working_tree_dir, cycle)
fpath = "{}/{:06d}.yaml".format(prop_dir, int(proposal)) cyc_dir.mkdir(exist_ok=True)
if not os.path.exists(fpath): fpath = Path(cyc_dir, f"{int(proposal):06d}.yaml")
with open("{}/default.yaml".format(repo.working_tree_dir), "r") as f:
defconf = yaml.load(f.read(), Loader=yaml.FullLoader) # In case cycle/proposal.yaml doesn't exist,
# new file is created based on default.yaml
if not fpath.exists():
with open(f"{repo.working_tree_dir}/default.yaml", "r") as f:
defconf = yaml.safe_load(f)
subconf = {} subconf = {}
for action, instruments in defconf.items(): # Propsal.yaml should have dark, correct and data-mapping keys
# with all detector dictionaries of the changed instrument.
for action in ["dark", "correct"]:
subconf[action] = {} subconf[action] = {}
if action != "data-mapping": subconf[action][instrument] = defconf[action][instrument]
subconf[action][instrument] = instruments[instrument]
else: # Copy data-mapping for all detectors of an instrument.
subconf[action][karabo_id] = instruments[karabo_id] subconf["data-mapping"] = {}
for k_id in defconf["dark"][instrument].keys():
subconf["data-mapping"][k_id] = defconf["data-mapping"][k_id]
with open(fpath, "w") as wf: with open(fpath, "w") as wf:
wf.write(yaml.dump(subconf, default_flow_style=False)) wf.write(yaml.safe_dump(subconf, default_flow_style=False))
new_conf = None new_conf = None
with open(fpath, "r") as rf: with open(fpath, "r") as rf:
existing_conf = yaml.load(rf.read(), Loader=yaml.FullLoader) existing_conf = yaml.safe_load(rf)
new_conf = merge(updated_config, existing_conf) new_conf = merge(updated_config, existing_conf)
if apply: if apply:
# Apply updated configuration to the proposal.yaml
# and push it to the calibration_configurations remote reporsitory.
with open(fpath, "w") as wf: with open(fpath, "w") as wf:
wf.write(yaml.dump(new_conf, default_flow_style=False)) wf.write(yaml.safe_dump(new_conf, default_flow_style=False))
repo.index.add([fpath]) repo.index.add([str(fpath)])
repo.index.commit( repo.index.commit(
"Update to proposal YAML: {}".format(datetime.now().isoformat())) f"Update to proposal YAML: {datetime.now().isoformat()}")
repo.remote().push() repo.remote().push()
logging.info(Success.UPLOADED_CONFIG.format(cycle, proposal)) logging.info(Success.UPLOADED_CONFIG.format(cycle, proposal))
return yaml.dump(new_conf, default_flow_style=False).encode() return yaml.safe_dump(new_conf, default_flow_style=False).encode()
async def run_proc_async(cmd: List[str]) -> (int, bytes): async def run_proc_async(cmd: List[str]) -> (int, bytes):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment