Skip to content

Config updater workflow #267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/source/examples/tools_example.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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?
--------------------------------------------------------------------------------------------------------------

Expand Down
23 changes: 23 additions & 0 deletions news/configupdate.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
**Added:**

* no news added: covered in the news from the get_user_info work

**Changed:**

* <news item>

**Deprecated:**

* <news item>

**Removed:**

* <news item>

**Fixed:**

* <news item>

**Security:**

* <news item>
83 changes: 72 additions & 11 deletions src/diffpy/utils/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def get_user_info(owner_name=None, owner_email=None, owner_orcid=None):
"owner_email": "<your_associated_email>>@email.com",
"owner_orcid": "<your_associated_orcid if you would like this stored with your data>>"
}
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
Expand All @@ -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.

Expand Down
145 changes: 58 additions & 87 deletions tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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"}}),
Expand Down
Loading