diff --git a/doc/source/examples/tools_example.rst b/doc/source/examples/tools_example.rst index 268c26dd..5525dfc7 100644 --- a/doc/source/examples/tools_example.rst +++ b/doc/source/examples/tools_example.rst @@ -114,6 +114,10 @@ After receiving the inputs, the function will write the information to the `diffpyconfig.json` file in your home directory. +``check_and_build_global_config()`` returns ``True`` if the config file exists (whether it created it or not) +and ``False`` if the config file does not exist in the user's home allowing you to develop your own +workflow for handling missing config files after running it with ``skip_config_creation=True``. + I entered the wrong information in my config file so it always loads incorrect information, how do I fix that? -------------------------------------------------------------------------------------------------------------- diff --git a/news/configupdate.rst b/news/configupdate.rst new file mode 100644 index 00000000..0bc3ffa9 --- /dev/null +++ b/news/configupdate.rst @@ -0,0 +1,23 @@ +**Added:** + +* no news added: covered in the news from the get_user_info work + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/src/diffpy/utils/tools.py b/src/diffpy/utils/tools.py index f2f4e136..b7a4c47d 100644 --- a/src/diffpy/utils/tools.py +++ b/src/diffpy/utils/tools.py @@ -79,7 +79,7 @@ def get_user_info(owner_name=None, owner_email=None, owner_orcid=None): "owner_email": ">@email.com", "owner_orcid": ">" } - You may also store any other gloabl-level information that you would like associated with your + You may also store any other global-level information that you would like associated with your diffraction data in this file Parameters @@ -103,22 +103,83 @@ def get_user_info(owner_name=None, owner_email=None, owner_orcid=None): del runtime_info[key] global_config = _load_config(Path().home() / "diffpyconfig.json") local_config = _load_config(Path().cwd() / "diffpyconfig.json") - # if global_config is None and local_config is None: - # print( - # "No global configuration file was found containing " - # "information about the user to associate with the data.\n" - # "By following the prompts below you can add your name and email to this file on the current " - # "computer and your name will be automatically associated with subsequent diffpy data by default.\n" - # "This is not recommended on a shared or public computer. " - # "You will only have to do that once.\n" - # "For more information, please refer to www.diffpy.org/diffpy.utils/examples/toolsexample.html" - # ) user_info = global_config user_info.update(local_config) user_info.update(runtime_info) return user_info +def check_and_build_global_config(skip_config_creation=False): + """Checks for a global diffpu config file in user's home directory and + creates one if it is missing. + + The file it looks for is called diffpyconfig.json. This can contain anything in json format, but + minimally contains information about the computer owner. The information is used + when diffpy objects are created and saved to files or databases to retain ownership information + of datasets. For example, it is used by diffpy.utils.tools.get_user_info(). + + If the function finds no config file in the user's home directory it interrupts execution + and prompts the user for name, email, and orcid information. It then creates the config file + with this information inside it. + + The function returns True if the file exists and False otherwise. + + If you would like to check for a file but not run the file creation workflow you can set + the optional argument skip_config_creation to True. + + Parameters + ---------- + skip_config_creation: bool, optional, Default is False + The bool that will override the creation workflow even if no config file exists. + + Returns + ------- + bool: True if the file exists and False otherwise. + """ + config_exists = False + config_path = Path().home() / "diffpyconfig.json" + if config_path.is_file(): + config_exists = True + return config_exists + if skip_config_creation: + return config_exists + intro_text = ( + "No global configuration file was found containing information about the user to " + "associate with the data.\n By following the prompts below you can add your name " + "and email to this file on the current " + "computer and your name will be automatically associated with subsequent diffpy data by default.\n" + "This is not recommended on a shared or public computer. " + "You will only have to do that once.\n" + "For more information, please refer to www.diffpy.org/diffpy.utils/examples/toolsexample.html" + ) + print(intro_text) + username = input("Please enter the name you would want future work to be credited to: ").strip() + email = input("Please enter your email: ").strip() + orcid = input("Please enter your orcid ID if you know it: ").strip() + config = { + "owner_name": _stringify(username), + "owner_email": _stringify(email), + "owner_orcid": _stringify(orcid), + } + if email != "" or orcid != "" or username != "": + config["owner_orcid"] = _stringify(orcid) + with open(config_path, "w") as f: + f.write(json.dumps(config)) + outro_text = ( + f"The config file at {Path().home() / 'diffpyconfig.json'} has been created. " + f"The values {config} were entered.\n" + f"These values will be inserted as metadata with your data in apps that use " + f"diffpy.get_user_info(). If you would like to update these values, either " + f"delete the config file and this workflow will rerun next time you run this " + f"program. Or you may open the config file in a text editor and manually edit the" + f"entries. For more information, see: " + f"https://diffpy.github.io/diffpy.utils/examples/tools_example.html" + ) + print(outro_text) + config_exists = True + return config_exists + + def get_package_info(package_names, metadata=None): """Fetches package version and updates it into (given) metadata. diff --git a/tests/test_tools.py b/tests/test_tools.py index cf730ddd..3808537d 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -5,72 +5,7 @@ import pytest -from diffpy.utils.tools import get_package_info, get_user_info - -# def _setup_dirs(monkeypatch, user_filesystem): -# home_dir, cwd_dir = user_filesystem.home_dir, user_filesystem.cwd_dir -# os.chdir(cwd_dir) -# return home_dir -# - - -def _run_tests(inputs, expected): - args = {"username": inputs[0], "email": inputs[1]} - expected_username, expected_email = expected - config = get_user_info(args) - assert config.get("username") == expected_username - assert config.get("email") == expected_email - - -params_user_info_with_local_conf_file = [ - (["", ""], ["cwd_username", "cwd@email.com"]), - (["cli_username", ""], ["cli_username", "cwd@email.com"]), - (["", "cli@email.com"], ["cwd_username", "cli@email.com"]), - ([None, None], ["cwd_username", "cwd@email.com"]), - (["cli_username", None], ["cli_username", "cwd@email.com"]), - ([None, "cli@email.com"], ["cwd_username", "cli@email.com"]), - (["cli_username", "cli@email.com"], ["cli_username", "cli@email.com"]), -] -params_user_info_with_no_home_conf_file = [ - ( - [None, None], - ["input_username", "input@email.com"], - ["input_username", "input@email.com"], - ), - ( - ["cli_username", None], - ["", "input@email.com"], - ["cli_username", "input@email.com"], - ), - ( - [None, "cli@email.com"], - ["input_username", ""], - ["input_username", "cli@email.com"], - ), - ( - ["", ""], - ["input_username", "input@email.com"], - ["input_username", "input@email.com"], - ), - ( - ["cli_username", ""], - ["", "input@email.com"], - ["cli_username", "input@email.com"], - ), - ( - ["", "cli@email.com"], - ["input_username", ""], - ["input_username", "cli@email.com"], - ), - ( - ["cli_username", "cli@email.com"], - ["input_username", "input@email.com"], - ["cli_username", "cli@email.com"], - ), -] -params_user_info_no_conf_file_no_inputs = [ - ([None, None], ["", ""], ["", ""]), -] +from diffpy.utils.tools import check_and_build_global_config, get_package_info, get_user_info @pytest.mark.parametrize( @@ -149,27 +84,63 @@ def test_get_user_info_with_local_conf_file(runtime_inputs, expected, user_files assert actual == expected -# @pytest.mark.parametrize("inputsa, inputsb, expected", params_user_info_with_no_home_conf_file) -# def test_get_user_info_with_no_home_conf_file(monkeypatch, inputsa, inputsb, expected, user_filesystem): -# _setup_dirs(monkeypatch, user_filesystem) -# os.remove(Path().home() / "diffpyconfig.json") -# inp_iter = iter(inputsb) -# monkeypatch.setattr("builtins.input", lambda _: next(inp_iter)) -# _run_tests(inputsa, expected) -# confile = Path().home() / "diffpyconfig.json" -# assert confile.is_file() -# -# -# @pytest.mark.parametrize("inputsa, inputsb, expected", params_user_info_no_conf_file_no_inputs) -# def test_get_user_info_no_conf_file_no_inputs(monkeypatch, inputsa, inputsb, expected, user_filesystem): -# _setup_dirs(monkeypatch, user_filesystem) -# os.remove(Path().home() / "diffpyconfig.json") -# inp_iter = iter(inputsb) -# monkeypatch.setattr("builtins.input", lambda _: next(inp_iter)) -# _run_tests(inputsa, expected) -# confile = Path().home() / "diffpyconfig.json" -# assert confile.exists() is False -# +@pytest.mark.parametrize( + "test_inputs,expected", + [ # Check check_and_build_global_config() builds correct config when config is found missing + ( # C1: user inputs valid name, email and orcid + {"user_inputs": ["input_name", "input@email.com", "input_orcid"]}, + {"owner_email": "input@email.com", "owner_orcid": "input_orcid", "owner_name": "input_name"}, + ), + ({"user_inputs": ["", "", ""]}, None), # C2: empty strings passed in, expect no config file created + ( # C3: just username input, expect config file but with some empty values + {"user_inputs": ["input_name", "", ""]}, + {"owner_email": "", "owner_orcid": "", "owner_name": "input_name"}, + ), + ], +) +def test_check_and_build_global_config(test_inputs, expected, user_filesystem, mocker): + # user_filesystem[0] is tmp_dir/home_dir with the global config file in it, user_filesystem[1] + # is tmp_dir/cwd_dir + mocker.patch.object(Path, "home", return_value=user_filesystem[0]) + os.chdir(user_filesystem[1]) + confile = user_filesystem[0] / "diffpyconfig.json" + # remove the config file from home that came with user_filesystem + os.remove(confile) + mocker.patch("builtins.input", side_effect=test_inputs["user_inputs"]) + actual_bool = check_and_build_global_config() + try: + with open(confile, "r") as f: + actual = json.load(f) + expected_bool = True + except FileNotFoundError: + expected_bool = False + actual = None + assert actual == expected + assert actual_bool == expected_bool + + +def test_check_and_build_global_config_file_exists(user_filesystem, mocker): + mocker.patch.object(Path, "home", return_value=user_filesystem[0]) + os.chdir(user_filesystem[1]) + confile = user_filesystem[0] / "diffpyconfig.json" + expected = {"owner_name": "home_ownername", "owner_email": "home@email.com", "owner_orcid": "home_orcid"} + actual_bool = check_and_build_global_config() + assert actual_bool is True + with open(confile, "r") as f: + actual = json.load(f) + assert actual == expected + + +def test_check_and_build_global_config_skipped(user_filesystem, mocker): + mocker.patch.object(Path, "home", return_value=user_filesystem[0]) + os.chdir(user_filesystem[1]) + confile = user_filesystem[0] / "diffpyconfig.json" + # remove the config file from home that came with user_filesystem + os.remove(confile) + actual_bool = check_and_build_global_config(skip_config_creation=True) + assert actual_bool is False + assert not confile.exists() + params_package_info = [ (["diffpy.utils", None], {"package_info": {"diffpy.utils": "3.3.0"}}),