Skip to content

Commit bd71eda

Browse files
authored
Merge pull request diffpy#168 from yucongalicechen/muD
feat: make muD optional and have different options for specifying muD
2 parents 103f4dc + 19e64f6 commit bd71eda

File tree

4 files changed

+135
-59
lines changed

4 files changed

+135
-59
lines changed

news/muD-options.rst

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* <news item>
4+
5+
**Changed:**
6+
7+
* Made muD an optional argument and provided different options (manually entry / z-scan file path) for users to specify muD
8+
9+
**Deprecated:**
10+
11+
* <news item>
12+
13+
**Removed:**
14+
15+
* <news item>
16+
17+
**Fixed:**
18+
19+
* <news item>
20+
21+
**Security:**
22+
23+
* <news item>

src/diffpy/labpdfproc/labpdfprocapp.py

+28-15
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,8 @@
1313
from diffpy.utils.parsers.loaddata import loadData
1414

1515

16-
def define_arguments():
16+
def _define_arguments():
1717
args = [
18-
{
19-
"name": ["mud"],
20-
"help": "Value of mu*D for your sample. Required.",
21-
"type": float,
22-
},
2318
{
2419
"name": ["input"],
2520
"help": (
@@ -150,20 +145,37 @@ def define_arguments():
150145
),
151146
"default": None,
152147
},
153-
{
154-
"name": ["-z", "--z-scan-file"],
155-
"help": "Path to the z-scan file to be loaded "
156-
"to determine the mu*D value.",
157-
"default": None,
158-
"widget": "FileChooser",
159-
},
160148
]
161149
return args
162150

163151

152+
def _add_mud_selection_group(p, is_gui=False):
153+
"""Current Options:
154+
1. Manually enter muD (`--mud`).
155+
2. Estimate muD from a z-scan file (`-z` or `--z-scan-file`).
156+
"""
157+
g = p.add_argument_group("Options for setting mu*D value (Required)")
158+
g = g.add_mutually_exclusive_group(required=True)
159+
g.add_argument(
160+
"--mud",
161+
type=float,
162+
help="Enter the mu*D value manually.",
163+
**({"widget": "DecimalField"} if is_gui else {}),
164+
)
165+
g.add_argument(
166+
"-z",
167+
"--z-scan-file",
168+
help="Provide the path to the z-scan file to be loaded "
169+
"to determine the mu*D value.",
170+
**({"widget": "FileChooser"} if is_gui else {}),
171+
)
172+
return p
173+
174+
164175
def get_args(override_cli_inputs=None):
165176
p = ArgumentParser()
166-
for arg in define_arguments():
177+
p = _add_mud_selection_group(p, is_gui=False)
178+
for arg in _define_arguments():
167179
kwargs = {
168180
key: value
169181
for key, value in arg.items()
@@ -177,7 +189,8 @@ def get_args(override_cli_inputs=None):
177189
@Gooey(required_cols=1, optional_cols=1, program_name="Labpdfproc GUI")
178190
def gooey_parser():
179191
p = GooeyParser()
180-
for arg in define_arguments():
192+
p = _add_mud_selection_group(p, is_gui=True)
193+
for arg in _define_arguments():
181194
kwargs = {key: value for key, value in arg.items() if key != "name"}
182195
p.add_argument(*arg["name"], **kwargs)
183196
args = p.parse_args()

src/diffpy/labpdfproc/tools.py

+29-10
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,34 @@ def set_xtype(args):
251251
return args
252252

253253

254+
def _estimate_mud_from_zscan(args):
255+
"""Compute mu*D based on the given z-scan file.
256+
257+
Parameters
258+
----------
259+
args : argparse.Namespace
260+
The arguments from the parser.
261+
262+
Returns
263+
-------
264+
args : argparse.Namespace
265+
The updated arguments with mu*D.
266+
"""
267+
filepath = Path(args.z_scan_file).resolve()
268+
if not filepath.is_file():
269+
raise FileNotFoundError(
270+
f"Cannot find {args.z_scan_file}. "
271+
f"Please specify a valid file path."
272+
)
273+
args.z_scan_file = str(filepath)
274+
args.mud = compute_mud(filepath)
275+
return args
276+
277+
254278
def set_mud(args):
255-
"""Compute mu*D based on the given z-scan file, if provided.
279+
"""Compute and set mu*D based on different options.
280+
Current options include manually entering a value,
281+
or estimating from a z-scan file.
256282
257283
Parameters
258284
----------
@@ -265,14 +291,7 @@ def set_mud(args):
265291
The updated arguments with mu*D.
266292
"""
267293
if args.z_scan_file:
268-
filepath = Path(args.z_scan_file).resolve()
269-
if not filepath.is_file():
270-
raise FileNotFoundError(
271-
f"Cannot find {args.z_scan_file}. "
272-
f"Please specify a valid file path."
273-
)
274-
args.z_scan_file = str(filepath)
275-
args.mud = compute_mud(filepath)
294+
return _estimate_mud_from_zscan(args)
276295
return args
277296

278297

@@ -390,13 +409,13 @@ def preprocessing_args(args):
390409
args : argparse.Namespace
391410
The updated argparse Namespace with arguments preprocessed.
392411
"""
412+
args = set_mud(args)
393413
args = load_package_info(args)
394414
args = load_user_info(args)
395415
args = set_input_lists(args)
396416
args = set_output_directory(args)
397417
args = set_wavelength(args)
398418
args = set_xtype(args)
399-
args = set_mud(args)
400419
args = load_user_metadata(args)
401420
return args
402421

tests/test_tools.py

+55-34
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def test_set_input_lists(inputs, expected, user_filesystem):
113113
base_dir.resolve() / expected_path for expected_path in expected
114114
]
115115

116-
cli_inputs = ["2.5"] + inputs
116+
cli_inputs = inputs + ["--mud", "2.5"]
117117
actual_args = get_args(cli_inputs)
118118
actual_args = set_input_lists(actual_args)
119119
assert sorted(actual_args.input_paths) == sorted(expected_paths)
@@ -159,7 +159,7 @@ def test_set_input_lists(inputs, expected, user_filesystem):
159159
def test_set_input_files_bad(inputs, expected_error_msg, user_filesystem):
160160
base_dir = Path(user_filesystem)
161161
os.chdir(base_dir)
162-
cli_inputs = ["2.5"] + inputs
162+
cli_inputs = inputs + ["--mud", "2.5"]
163163
actual_args = get_args(cli_inputs)
164164
with pytest.raises(FileNotFoundError, match=re.escape(expected_error_msg)):
165165
actual_args = set_input_lists(actual_args)
@@ -177,7 +177,7 @@ def test_set_input_files_bad(inputs, expected_error_msg, user_filesystem):
177177
def test_set_output_directory(inputs, expected, user_filesystem):
178178
os.chdir(user_filesystem)
179179
expected_output_directory = Path(user_filesystem) / expected[0]
180-
cli_inputs = ["2.5", "data.xy"] + inputs
180+
cli_inputs = ["data.xy", "--mud", "2.5"] + inputs
181181
actual_args = get_args(cli_inputs)
182182
actual_args = set_output_directory(actual_args)
183183
assert actual_args.output_directory == expected_output_directory
@@ -187,7 +187,13 @@ def test_set_output_directory(inputs, expected, user_filesystem):
187187

188188
def test_set_output_directory_bad(user_filesystem):
189189
os.chdir(user_filesystem)
190-
cli_inputs = ["2.5", "data.xy", "--output-directory", "good_data.chi"]
190+
cli_inputs = [
191+
"data.xy",
192+
"--mud",
193+
"2.5",
194+
"--output-directory",
195+
"good_data.chi",
196+
]
191197
actual_args = get_args(cli_inputs)
192198
with pytest.raises(FileExistsError):
193199
actual_args = set_output_directory(actual_args)
@@ -247,7 +253,7 @@ def test_set_output_directory_bad(user_filesystem):
247253
],
248254
)
249255
def test_set_wavelength(inputs, expected):
250-
cli_inputs = ["2.5", "data.xy"] + inputs
256+
cli_inputs = ["data.xy", "--mud", "2.5"] + inputs
251257
actual_args = get_args(cli_inputs)
252258
actual_args = set_wavelength(actual_args)
253259
assert actual_args.wavelength == expected["wavelength"]
@@ -286,7 +292,7 @@ def test_set_wavelength(inputs, expected):
286292
],
287293
)
288294
def test_set_wavelength_bad(inputs, expected_error_msg):
289-
cli_inputs = ["2.5", "data.xy"] + inputs
295+
cli_inputs = ["data.xy", "--mud", "2.5"] + inputs
290296
actual_args = get_args(cli_inputs)
291297
with pytest.raises(ValueError, match=re.escape(expected_error_msg)):
292298
actual_args = set_wavelength(actual_args)
@@ -302,50 +308,63 @@ def test_set_wavelength_bad(inputs, expected_error_msg):
302308
],
303309
)
304310
def test_set_xtype(inputs, expected_xtype):
305-
cli_inputs = ["2.5", "data.xy"] + inputs
311+
cli_inputs = ["data.xy", "--mud", "2.5"] + inputs
306312
actual_args = get_args(cli_inputs)
307313
actual_args = set_xtype(actual_args)
308314
assert actual_args.xtype == expected_xtype
309315

310316

311317
def test_set_xtype_bad():
312-
cli_inputs = ["2.5", "data.xy", "--xtype", "invalid"]
318+
cli_inputs = ["data.xy", "--mud", "2.5", "--xtype", "invalid"]
313319
actual_args = get_args(cli_inputs)
314320
with pytest.raises(
315321
ValueError,
316322
match=re.escape(
317-
f"Unknown xtype: invalid. " f"Allowed xtypes are {*XQUANTITIES, }."
323+
f"Unknown xtype: invalid. Allowed xtypes are {*XQUANTITIES, }."
318324
),
319325
):
320326
actual_args = set_xtype(actual_args)
321327

322328

323-
def test_set_mud(user_filesystem):
324-
cli_inputs = ["2.5", "data.xy"]
325-
actual_args = get_args(cli_inputs)
326-
actual_args = set_mud(actual_args)
327-
assert actual_args.mud == pytest.approx(2.5, rel=1e-4, abs=0.1)
328-
assert actual_args.z_scan_file is None
329-
329+
@pytest.mark.parametrize(
330+
"inputs, expected_mud",
331+
[
332+
# C1: user enters muD manually, expect to return the same value
333+
(["--mud", "2.5"], 2.5),
334+
# C2: user provides a z-scan file, expect to estimate through the file
335+
(["--z-scan-file", "test_dir/testfile.xy"], 3),
336+
],
337+
)
338+
def test_set_mud(user_filesystem, inputs, expected_mud):
330339
cwd = Path(user_filesystem)
331-
test_dir = cwd / "test_dir"
332340
os.chdir(cwd)
333-
inputs = ["--z-scan-file", "test_dir/testfile.xy"]
334-
expected = [3, str(test_dir / "testfile.xy")]
335-
cli_inputs = ["2.5", "data.xy"] + inputs
341+
cli_inputs = ["data.xy"] + inputs
336342
actual_args = get_args(cli_inputs)
337343
actual_args = set_mud(actual_args)
338-
assert actual_args.mud == pytest.approx(expected[0], rel=1e-4, abs=0.1)
339-
assert actual_args.z_scan_file == expected[1]
344+
assert actual_args.mud == pytest.approx(expected_mud, rel=1e-4, abs=0.1)
340345

341346

342-
def test_set_mud_bad():
343-
cli_inputs = ["2.5", "data.xy", "--z-scan-file", "invalid file"]
347+
@pytest.mark.parametrize(
348+
"inputs, expected",
349+
[
350+
# C1: user provides an invalid z-scan file,
351+
# expect FileNotFoundError and message to specify a valid file path
352+
(
353+
["--z-scan-file", "invalid file"],
354+
[
355+
FileNotFoundError,
356+
"Cannot find invalid file. Please specify a valid file path.",
357+
],
358+
),
359+
],
360+
)
361+
def test_set_mud_bad(user_filesystem, inputs, expected):
362+
expected_error, expected_error_msg = expected
363+
cwd = Path(user_filesystem)
364+
os.chdir(cwd)
365+
cli_inputs = ["data.xy"] + inputs
344366
actual_args = get_args(cli_inputs)
345-
with pytest.raises(
346-
FileNotFoundError,
347-
match="Cannot find invalid file. " "Please specify a valid file path.",
348-
):
367+
with pytest.raises(expected_error, match=expected_error_msg):
349368
actual_args = set_mud(actual_args)
350369

351370

@@ -370,12 +389,12 @@ def test_set_mud_bad():
370389
],
371390
)
372391
def test_load_user_metadata(inputs, expected):
373-
expected_args = get_args(["2.5", "data.xy"])
392+
expected_args = get_args(["data.xy", "--mud", "2.5"])
374393
for expected_pair in expected:
375394
setattr(expected_args, expected_pair[0], expected_pair[1])
376395
delattr(expected_args, "user_metadata")
377396

378-
cli_inputs = ["2.5", "data.xy"] + inputs
397+
cli_inputs = ["data.xy", "--mud", "2.5"] + inputs
379398
actual_args = get_args(cli_inputs)
380399
actual_args = load_user_metadata(actual_args)
381400
assert actual_args == expected_args
@@ -411,7 +430,7 @@ def test_load_user_metadata(inputs, expected):
411430
],
412431
)
413432
def test_load_user_metadata_bad(inputs, expected_error_msg):
414-
cli_inputs = ["2.5", "data.xy"] + inputs
433+
cli_inputs = ["data.xy", "--mud", "2.5"] + inputs
415434
actual_args = get_args(cli_inputs)
416435
with pytest.raises(ValueError, match=re.escape(expected_error_msg)):
417436
actual_args = load_user_metadata(actual_args)
@@ -474,8 +493,9 @@ def test_load_user_info(monkeypatch, inputs, expected, user_filesystem):
474493
os.chdir(cwd)
475494

476495
cli_inputs = [
477-
"2.5",
478496
"data.xy",
497+
"--mud",
498+
"2.5",
479499
"--username",
480500
inputs["username"],
481501
"--email",
@@ -497,7 +517,7 @@ def test_load_package_info(mocker):
497517
"3.3.0" if package_name == "diffpy.utils" else "1.2.3"
498518
),
499519
)
500-
cli_inputs = ["2.5", "data.xy"]
520+
cli_inputs = ["data.xy", "--mud", "2.5"]
501521
actual_args = get_args(cli_inputs)
502522
actual_args = load_package_info(actual_args)
503523
assert actual_args.package_info == {
@@ -523,8 +543,9 @@ def test_load_metadata(mocker, user_filesystem):
523543
),
524544
)
525545
cli_inputs = [
526-
"2.5",
527546
".",
547+
"--mud",
548+
"2.5",
528549
"--anode-type",
529550
"Mo",
530551
"--user-metadata",

0 commit comments

Comments
 (0)