From b5ab9fec08fbd942d2d91ce7d114e31d4bbc4f91 Mon Sep 17 00:00:00 2001 From: Simon Billinge Date: Mon, 16 Dec 2024 20:09:35 -0500 Subject: [PATCH] simon tweaks to tools example --- doc/source/examples/toolsexample.rst | 174 +++++++++++++++++++++------ src/diffpy/utils/tools.py | 4 +- 2 files changed, 140 insertions(+), 38 deletions(-) diff --git a/doc/source/examples/toolsexample.rst b/doc/source/examples/toolsexample.rst index 05bac195..a56f76d3 100644 --- a/doc/source/examples/toolsexample.rst +++ b/doc/source/examples/toolsexample.rst @@ -5,69 +5,171 @@ Tools Example ############# -This example will demonstrate how diffpy.utils allows us to conveniently load and manage user and package information. +The tools module contains various tools that may be useful when you manipulate and analyze diffraction data. + +Automatically Capture User Info +=============================== + +One task we would like to do is to capture and propagate useful metadata that describes the diffraction data. +Some is essential such as wavelength and radiation type. Other metadata is useful such as information about the +sample, co-workers and so on. However, one of the most important bits of information is the name of the data owner. +For example, in ``DiffractionObjects`` this is stored in the ``metadata`` dictionary as ``username``, ``user_email``, +and ``user_orcid``. + +To reduce experimenter overhead when collecting this information, we have developed an infrastructure that helps +to capture this information automatically when you are using `DiffractionObjects` and other diffpy tools. +You may also reuse this infrastructure for your own projects using tools in this tutorial. + +This example will demonstrate how ``diffpy.utils`` allows us to conveniently load and manage user and package information. Using the tools module, we can efficiently get them in terms of a dictionary. -1) We have the function ``get_user_info`` that neatly returns a dictionary containing the username and email. - You can use this function without arguments. :: +Load user info into your program +-------------------------------- + +To use this functionality in your own code make use of the ``get_user_info`` function in +``diffpy.utils.tools`` which will search for information about the user, parse it, and return +it in a dictionary object e.g. if the user is "Jane Doe" with email "janedoe@gmail.com" and the +function can find the information, if you type this + +.. code-block:: python from diffpy.utils.tools import get_user_info user_info = get_user_info() - This function will first attempt to load the information from configuration files looking first in - the current working directory and then in the user's home directory. - If no configuration files exist, it prompts for user input and creates a configuration file in the home directory - so that the next time the program is run it will no longer have to prompt the user. - It can be passed user information which overrides looking in config files, and so could be passed - information that is entered through a gui or command line interface to override default information at runtime. - It prioritizes prompted user inputs, then current working directory config file, and finally home directory config file. +The function will return + +.. code-block:: python + + {"email": "janedoe@email.com", "username": "Jane Doe"} + + +Where does ``get_user_info()`` get the user information from? +------------------------------------------------------------- + +The function will first attempt to load the information from configuration files with the name ``diffpyconfig.json`` +on your hard-drive. +It looks first for the file in the current working directory. If it cannot find it there it will look +user's home, i.e., login, directory. To find this directory, open a terminal and a unix or mac system type :: + + cd ~ + pwd + +Or type ``Echo $HOME``. On a Windows computer :: + + echo %USERPROFILE%" + +What if no config files exist yet? +----------------------------------- + +If no configuration files can be found, the function attempts to create one in the user's home +directory. The function will pause execution and ask for a user-response to enter the information. +It will then write the config file in the user's home directory. + +In this way, the next, and subsequent times the program is run, it will no longer have to prompt the user +as it will successfully find the new config file. + +Getting user data with no config files and with no interruption of execution +---------------------------------------------------------------------------- + +If you would like get run ``get_user_data()`` but without execution interruption even if it cannot find +an input file, type + +.. code-block:: python + + user_data = get_user_data(skip_config_creation=True) + +Passing user information directly to ``get_user_data()`` +-------------------------------------------------------- - The function returns a dictionary containing the username and email information. +It can be passed user information which fully or partially overrides looking in config files +For example, in this way it would be possible to pass in information +that is entered through a gui or command line interface. E.g., -2) There are a few ways you could use to override the user information in the configuration files - * You can override existing values by passing a dictionary to the function with the keys ``"username"`` and ``"email"`` :: + .. code-block:: python - new_args = {"username": "new_username", "email": "new@example.com"} - new_user_info = get_user_info(new_args) + new_user_info = get_user_info({"username": "new_username", "email": "new@example.com"}) - This returns "new_username" and "new@example.com" instead of the - * You can update only the username or email individually, for example :: +This returns ``{"username": "new_username", "email": "new@example.com"}`` (and so, effectively, does nothing) +However, You can update only the username or email individually, for example - new_username = {"username": new_username} - new_user_info = get_user_info(new_username) +.. code-block:: python - This updates username to "new_username" while fetching the email from inputs or the configuration files. - Similarly, you can update only the email. :: + new_user_info = get_user_info({"username": new_username}) - new_email = {"email": new@email.com} - new_user_info = get_user_info(new_email) +will return ``{"username": "new_username", "email": "janedoe@gmail.com"}`` +if it found ``janedoe@gmail.com`` as the email in the config file. +Similarly, you can update only the email in the returned dictionary, - This updates the email to "new@email.com" while fetching the username from inputs or the configuration files. +.. code-block:: python -3) You can also permanently update your default configuration file manually. - Locate the file ``diffpyconfig.json``, which is usually in the user's home directory (``~/.diffpy/``) or ``~/.config/diffpy/``. - Open the file using a text editor such as nano, vim, or a graphical editor like VS Code. - Look for entries like these :: + new_user_info = get_user_info({"email": new@email.com}) + +which will return ``{"username": "Jane Doe", "email": "new@email.com"}`` +if it found ``Jane Doe`` as the user in the config file. + +I entered the wrong information in my config file so it always loads incorrect information +------------------------------------------------------------------------------------------ + +You can use of the above methods to temporarily override the incorrect information in your +global config file. However, it is easy to fix this simply by editing that file using a text +editor. + +Locate the file ``diffpyconfig.json``, in your home directory and open it in an editor :: { "username": "John Doe", "email": "john.doe@example.com" } - Then you can update the username and email as needed, make sure to save your edits. + Then you can edit the username and email as needed, make sure to save your edits. + +Automatically Capture Info about a Software Package Being Used +============================================================== + +We also have a handy tool for capturing information about a python package that is being used +to save in the metadata. To use this functionality, use he function ``get_package_info``, which +inserts or updates software package names and versions in a given metadata dictionary under +the key "package_info", e.g., -4) We also have the function ``get_package_info``, which inserts or updates package names and versions - in the given metadata dictionary under the key "package_info". - It stores the package information as {"package_info": {"package_name": "version_number"}}. - This function can be used as follows. :: +.. code-block:: python + + {"package_info": {"diffpy.utils": "0.3.0", "my_package": "0.3.1"}} + +If the installed version of the package "my_package" is 0.3.1. + +This function can be used in your code as follows + +.. code-block:: python from diffpy.utils.tools import get_package_info package_metadata = get_package_info("my_package") - You can also specify an existing dictionary to be updated with the information. :: +or + +.. code-block:: python + + package_metadata = get_package_info(["first_package", "second_package"]) + +which returns (for example) + +.. code-block:: python + + {"package_info": {"diffpy.utils": "0.3.0", "first_package": "1.0.1", "second_package": "0.0.7"}} + + +You can also specify an existing dictionary to be updated with the information. + +.. code-block:: python existing_dict = {"key": "value"} updated_dict = get_package_info("my_package", metadata=existing_dict)) - note that `"diffpy.utils"` is automatically included in the package info since the `get_user_info` function is - part of diffpy.utils. +Which returns + +.. code-block:: python + + {"key": "value", "package_info": {"diffpy.utils": "0.3.0", "my_package": "0.3.1"}} + + +Note that ``"diffpy.utils"`` is automatically included in the package info since the ``get_user_info`` function is +part of ``diffpy.utils``. diff --git a/src/diffpy/utils/tools.py b/src/diffpy/utils/tools.py index 7e5e858f..3fc10031 100644 --- a/src/diffpy/utils/tools.py +++ b/src/diffpy/utils/tools.py @@ -118,8 +118,8 @@ def get_user_info(args=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" + "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"