Skip to content

Commit 40c3446

Browse files
authored
Merge pull request #267 from sbillinge/configupdate
Config updater workflow
2 parents 969ca54 + 8d0bf06 commit 40c3446

File tree

4 files changed

+157
-98
lines changed

4 files changed

+157
-98
lines changed

doc/source/examples/tools_example.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ After receiving the inputs, the function will write the information to
114114
the `diffpyconfig.json` file in your home directory.
115115

116116

117+
``check_and_build_global_config()`` returns ``True`` if the config file exists (whether it created it or not)
118+
and ``False`` if the config file does not exist in the user's home allowing you to develop your own
119+
workflow for handling missing config files after running it with ``skip_config_creation=True``.
120+
117121
I entered the wrong information in my config file so it always loads incorrect information, how do I fix that?
118122
--------------------------------------------------------------------------------------------------------------
119123

news/configupdate.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* no news added: covered in the news from the get_user_info work
4+
5+
**Changed:**
6+
7+
* <news item>
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/utils/tools.py

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def get_user_info(owner_name=None, owner_email=None, owner_orcid=None):
7979
"owner_email": "<your_associated_email>>@email.com",
8080
"owner_orcid": "<your_associated_orcid if you would like this stored with your data>>"
8181
}
82-
You may also store any other gloabl-level information that you would like associated with your
82+
You may also store any other global-level information that you would like associated with your
8383
diffraction data in this file
8484
8585
Parameters
@@ -103,22 +103,83 @@ def get_user_info(owner_name=None, owner_email=None, owner_orcid=None):
103103
del runtime_info[key]
104104
global_config = _load_config(Path().home() / "diffpyconfig.json")
105105
local_config = _load_config(Path().cwd() / "diffpyconfig.json")
106-
# if global_config is None and local_config is None:
107-
# print(
108-
# "No global configuration file was found containing "
109-
# "information about the user to associate with the data.\n"
110-
# "By following the prompts below you can add your name and email to this file on the current "
111-
# "computer and your name will be automatically associated with subsequent diffpy data by default.\n"
112-
# "This is not recommended on a shared or public computer. "
113-
# "You will only have to do that once.\n"
114-
# "For more information, please refer to www.diffpy.org/diffpy.utils/examples/toolsexample.html"
115-
# )
116106
user_info = global_config
117107
user_info.update(local_config)
118108
user_info.update(runtime_info)
119109
return user_info
120110

121111

112+
def check_and_build_global_config(skip_config_creation=False):
113+
"""Checks for a global diffpu config file in user's home directory and
114+
creates one if it is missing.
115+
116+
The file it looks for is called diffpyconfig.json. This can contain anything in json format, but
117+
minimally contains information about the computer owner. The information is used
118+
when diffpy objects are created and saved to files or databases to retain ownership information
119+
of datasets. For example, it is used by diffpy.utils.tools.get_user_info().
120+
121+
If the function finds no config file in the user's home directory it interrupts execution
122+
and prompts the user for name, email, and orcid information. It then creates the config file
123+
with this information inside it.
124+
125+
The function returns True if the file exists and False otherwise.
126+
127+
If you would like to check for a file but not run the file creation workflow you can set
128+
the optional argument skip_config_creation to True.
129+
130+
Parameters
131+
----------
132+
skip_config_creation: bool, optional, Default is False
133+
The bool that will override the creation workflow even if no config file exists.
134+
135+
Returns
136+
-------
137+
bool: True if the file exists and False otherwise.
138+
"""
139+
config_exists = False
140+
config_path = Path().home() / "diffpyconfig.json"
141+
if config_path.is_file():
142+
config_exists = True
143+
return config_exists
144+
if skip_config_creation:
145+
return config_exists
146+
intro_text = (
147+
"No global configuration file was found containing information about the user to "
148+
"associate with the data.\n By following the prompts below you can add your name "
149+
"and email to this file on the current "
150+
"computer and your name will be automatically associated with subsequent diffpy data by default.\n"
151+
"This is not recommended on a shared or public computer. "
152+
"You will only have to do that once.\n"
153+
"For more information, please refer to www.diffpy.org/diffpy.utils/examples/toolsexample.html"
154+
)
155+
print(intro_text)
156+
username = input("Please enter the name you would want future work to be credited to: ").strip()
157+
email = input("Please enter your email: ").strip()
158+
orcid = input("Please enter your orcid ID if you know it: ").strip()
159+
config = {
160+
"owner_name": _stringify(username),
161+
"owner_email": _stringify(email),
162+
"owner_orcid": _stringify(orcid),
163+
}
164+
if email != "" or orcid != "" or username != "":
165+
config["owner_orcid"] = _stringify(orcid)
166+
with open(config_path, "w") as f:
167+
f.write(json.dumps(config))
168+
outro_text = (
169+
f"The config file at {Path().home() / 'diffpyconfig.json'} has been created. "
170+
f"The values {config} were entered.\n"
171+
f"These values will be inserted as metadata with your data in apps that use "
172+
f"diffpy.get_user_info(). If you would like to update these values, either "
173+
f"delete the config file and this workflow will rerun next time you run this "
174+
f"program. Or you may open the config file in a text editor and manually edit the"
175+
f"entries. For more information, see: "
176+
f"https://diffpy.github.io/diffpy.utils/examples/tools_example.html"
177+
)
178+
print(outro_text)
179+
config_exists = True
180+
return config_exists
181+
182+
122183
def get_package_info(package_names, metadata=None):
123184
"""Fetches package version and updates it into (given) metadata.
124185

tests/test_tools.py

Lines changed: 58 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -5,72 +5,7 @@
55

66
import pytest
77

8-
from diffpy.utils.tools import get_package_info, get_user_info
9-
10-
# def _setup_dirs(monkeypatch, user_filesystem):
11-
# home_dir, cwd_dir = user_filesystem.home_dir, user_filesystem.cwd_dir
12-
# os.chdir(cwd_dir)
13-
# return home_dir
14-
#
15-
16-
17-
def _run_tests(inputs, expected):
18-
args = {"username": inputs[0], "email": inputs[1]}
19-
expected_username, expected_email = expected
20-
config = get_user_info(args)
21-
assert config.get("username") == expected_username
22-
assert config.get("email") == expected_email
23-
24-
25-
params_user_info_with_local_conf_file = [
26-
(["", ""], ["cwd_username", "[email protected]"]),
27-
(["cli_username", ""], ["cli_username", "[email protected]"]),
28-
(["", "[email protected]"], ["cwd_username", "[email protected]"]),
29-
([None, None], ["cwd_username", "[email protected]"]),
30-
(["cli_username", None], ["cli_username", "[email protected]"]),
31-
([None, "[email protected]"], ["cwd_username", "[email protected]"]),
32-
(["cli_username", "[email protected]"], ["cli_username", "[email protected]"]),
33-
]
34-
params_user_info_with_no_home_conf_file = [
35-
(
36-
[None, None],
37-
["input_username", "[email protected]"],
38-
["input_username", "[email protected]"],
39-
),
40-
(
41-
["cli_username", None],
42-
43-
["cli_username", "[email protected]"],
44-
),
45-
(
46-
[None, "[email protected]"],
47-
["input_username", ""],
48-
["input_username", "[email protected]"],
49-
),
50-
(
51-
["", ""],
52-
["input_username", "[email protected]"],
53-
["input_username", "[email protected]"],
54-
),
55-
(
56-
["cli_username", ""],
57-
58-
["cli_username", "[email protected]"],
59-
),
60-
(
61-
62-
["input_username", ""],
63-
["input_username", "[email protected]"],
64-
),
65-
(
66-
["cli_username", "[email protected]"],
67-
["input_username", "[email protected]"],
68-
["cli_username", "[email protected]"],
69-
),
70-
]
71-
params_user_info_no_conf_file_no_inputs = [
72-
([None, None], ["", ""], ["", ""]),
73-
]
8+
from diffpy.utils.tools import check_and_build_global_config, get_package_info, get_user_info
749

7510

7611
@pytest.mark.parametrize(
@@ -149,27 +84,63 @@ def test_get_user_info_with_local_conf_file(runtime_inputs, expected, user_files
14984
assert actual == expected
15085

15186

152-
# @pytest.mark.parametrize("inputsa, inputsb, expected", params_user_info_with_no_home_conf_file)
153-
# def test_get_user_info_with_no_home_conf_file(monkeypatch, inputsa, inputsb, expected, user_filesystem):
154-
# _setup_dirs(monkeypatch, user_filesystem)
155-
# os.remove(Path().home() / "diffpyconfig.json")
156-
# inp_iter = iter(inputsb)
157-
# monkeypatch.setattr("builtins.input", lambda _: next(inp_iter))
158-
# _run_tests(inputsa, expected)
159-
# confile = Path().home() / "diffpyconfig.json"
160-
# assert confile.is_file()
161-
#
162-
#
163-
# @pytest.mark.parametrize("inputsa, inputsb, expected", params_user_info_no_conf_file_no_inputs)
164-
# def test_get_user_info_no_conf_file_no_inputs(monkeypatch, inputsa, inputsb, expected, user_filesystem):
165-
# _setup_dirs(monkeypatch, user_filesystem)
166-
# os.remove(Path().home() / "diffpyconfig.json")
167-
# inp_iter = iter(inputsb)
168-
# monkeypatch.setattr("builtins.input", lambda _: next(inp_iter))
169-
# _run_tests(inputsa, expected)
170-
# confile = Path().home() / "diffpyconfig.json"
171-
# assert confile.exists() is False
172-
#
87+
@pytest.mark.parametrize(
88+
"test_inputs,expected",
89+
[ # Check check_and_build_global_config() builds correct config when config is found missing
90+
( # C1: user inputs valid name, email and orcid
91+
{"user_inputs": ["input_name", "[email protected]", "input_orcid"]},
92+
{"owner_email": "[email protected]", "owner_orcid": "input_orcid", "owner_name": "input_name"},
93+
),
94+
({"user_inputs": ["", "", ""]}, None), # C2: empty strings passed in, expect no config file created
95+
( # C3: just username input, expect config file but with some empty values
96+
{"user_inputs": ["input_name", "", ""]},
97+
{"owner_email": "", "owner_orcid": "", "owner_name": "input_name"},
98+
),
99+
],
100+
)
101+
def test_check_and_build_global_config(test_inputs, expected, user_filesystem, mocker):
102+
# user_filesystem[0] is tmp_dir/home_dir with the global config file in it, user_filesystem[1]
103+
# is tmp_dir/cwd_dir
104+
mocker.patch.object(Path, "home", return_value=user_filesystem[0])
105+
os.chdir(user_filesystem[1])
106+
confile = user_filesystem[0] / "diffpyconfig.json"
107+
# remove the config file from home that came with user_filesystem
108+
os.remove(confile)
109+
mocker.patch("builtins.input", side_effect=test_inputs["user_inputs"])
110+
actual_bool = check_and_build_global_config()
111+
try:
112+
with open(confile, "r") as f:
113+
actual = json.load(f)
114+
expected_bool = True
115+
except FileNotFoundError:
116+
expected_bool = False
117+
actual = None
118+
assert actual == expected
119+
assert actual_bool == expected_bool
120+
121+
122+
def test_check_and_build_global_config_file_exists(user_filesystem, mocker):
123+
mocker.patch.object(Path, "home", return_value=user_filesystem[0])
124+
os.chdir(user_filesystem[1])
125+
confile = user_filesystem[0] / "diffpyconfig.json"
126+
expected = {"owner_name": "home_ownername", "owner_email": "[email protected]", "owner_orcid": "home_orcid"}
127+
actual_bool = check_and_build_global_config()
128+
assert actual_bool is True
129+
with open(confile, "r") as f:
130+
actual = json.load(f)
131+
assert actual == expected
132+
133+
134+
def test_check_and_build_global_config_skipped(user_filesystem, mocker):
135+
mocker.patch.object(Path, "home", return_value=user_filesystem[0])
136+
os.chdir(user_filesystem[1])
137+
confile = user_filesystem[0] / "diffpyconfig.json"
138+
# remove the config file from home that came with user_filesystem
139+
os.remove(confile)
140+
actual_bool = check_and_build_global_config(skip_config_creation=True)
141+
assert actual_bool is False
142+
assert not confile.exists()
143+
173144

174145
params_package_info = [
175146
(["diffpy.utils", None], {"package_info": {"diffpy.utils": "3.3.0"}}),

0 commit comments

Comments
 (0)