Skip to content
Snippets Groups Projects
Commit a608cfaf authored by Hock, Martin's avatar Hock, Martin
Browse files

Merge branch '65-default-name-for-exported-plots' into 'dev'

Resolve "Default name for exported plots"

See merge request !48
parents d5b1fdff c643cb71
Branches
Tags v2.4.0
2 merge requests!52Create v0.2.2,!48Resolve "Default name for exported plots"
Pipeline #855259 waiting for manual action
...@@ -59,9 +59,9 @@ Optional parameters can be used to customize the tag process. ...@@ -59,9 +59,9 @@ Optional parameters can be used to customize the tag process.
- *prefix* : str, optional - *prefix* : str, optional
Will be added as prefix to the ID. Will be added as prefix to the ID.
- *id_method* : str, optional - *id_method* : str, optional
id_method for creating the ID. Create an ID by Unix time is referenced as 'time', create a random ID with id_method='random'. The default is 'time'. id_method for creating the ID. Create an ID by Unix time is referenced as "time", create a random ID with id_method="random". The default is "time".
- *location* : string, optional - *location* : string, optional
Location for ID to be displayed on the plot. Default is 'east'. Location for ID to be displayed on the plot. Default is "east".
- *qrcode* : boolean, optional - *qrcode* : boolean, optional
Experimental support for encoding the ID in a QR Code. Experimental support for encoding the ID in a QR Code.
- *id_on_plot* : boolean, optional - *id_on_plot* : boolean, optional
...@@ -72,13 +72,13 @@ Example: ...@@ -72,13 +72,13 @@ Example:
FIG1 = plt.figure() FIG1 = plt.figure()
FIG2 = plt.figure() FIG2 = plt.figure()
FIGS_AS_LIST = [FIG1, FIG2] FIGS_AS_LIST = [FIG1, FIG2]
FIGS_AND_IDS = tagplot(FIGS_AS_LIST, 'matplotlib', prefix='XY23_', id_method='random', location='west') FIGS_AND_IDS = tagplot(FIGS_AS_LIST, "matplotlib", prefix="XY23_", id_method="random", location="west")
``` ```
### publish() ### publish()
Save plot, data and measuring script. It is possible to export multiple figures at once. Save plot, data and measuring script. It is possible to export multiple figures at once.
`publish(figs_and_ids, src_datapath, dst_path, plot_name)` `publish(figs_and_ids, src_datapath, dst_path)`
- *figs_and_ids* must be a PlotIDTransfer object. Therefore, it can be directly passed from tagplot() to publish(). - *figs_and_ids* must be a PlotIDTransfer object. Therefore, it can be directly passed from tagplot() to publish().
- *src_datapath* specifies the path to (raw) data that should be published. It can be a string or a list of strings that specifies all files and directories which will be published. - *src_datapath* specifies the path to (raw) data that should be published. It can be a string or a list of strings that specifies all files and directories which will be published.
...@@ -90,8 +90,10 @@ Optional parameters can be used to customize the publish process. ...@@ -90,8 +90,10 @@ Optional parameters can be used to customize the publish process.
Method how the data should be stored. Available options: Method how the data should be stored. Available options:
- *centralized*: The raw data will copied only once. All other plots will reference this data via sym link. - *centralized*: The raw data will copied only once. All other plots will reference this data via sym link.
- *individual*: The complete raw data will be copied to a folder for every plot, respectively. - *individual*: The complete raw data will be copied to a folder for every plot, respectively.
- *plot_names* : str or list of str, optional
Name for the exported plot. If not provided, the corresponding IDs will be used.
Example: Example:
`publish(figs_and_ids, '/home/user/Documents/research_data', '/home/user/Documents/exported_data', 'EnergyOverTime-Plot')` `publish(figs_and_ids, "/home/user/Documents/research_data", "/home/user/Documents/exported_data", plot_names=["EnergyOverTime-Plot", "TimeOverEnergy-Plot")`
## Build ## Build
If you want to build plotID yourself, follow these steps: If you want to build plotID yourself, follow these steps:
......
...@@ -35,6 +35,6 @@ FIGS_AND_IDS = tagplot( ...@@ -35,6 +35,6 @@ FIGS_AND_IDS = tagplot(
# Export your tagged images, copy the research data that generated the images, # Export your tagged images, copy the research data that generated the images,
# specify the destination folder and give a name for the exported image files. # specify the destination folder and give a name for the exported image files.
publish(FIGS_AND_IDS, ["../README.md", "../docs", "../LICENSE"], "./data", "my_image") publish(FIGS_AND_IDS, ["../README.md", "../docs", "../LICENSE"], "./data")
# Required arguments: publish(output of tagplot(), list of files, # Required arguments: publish(output of tagplot(), list of files,
# path to destination folder, name(s) for the resulting images) # path to destination folder)
...@@ -46,6 +46,6 @@ FIGS_AND_IDS = tagplot( ...@@ -46,6 +46,6 @@ FIGS_AND_IDS = tagplot(
# %% Publish # %% Publish
publish(FIGS_AND_IDS, ["../README.md", "../docs", "../LICENSE"], "data", "my_plot") publish(FIGS_AND_IDS, ["../README.md", "../docs", "../LICENSE"], "data")
# Required arguments: publish(output of tagplot(), list of files, # Required arguments: publish(output of tagplot(), list of files,
# path to destination folder, name(s) for the resulting images) # path to destination folder)
...@@ -33,17 +33,17 @@ class PublishOptions: ...@@ -33,17 +33,17 @@ class PublishOptions:
Export the plot and copy specified files to the destiantion folder. Export the plot and copy specified files to the destiantion folder.
""" """
def __init__(self, figs_and_ids, src_datapaths, dst_path, plot_names, **kwargs): def __init__(self, figs_and_ids, src_datapaths, dst_path, **kwargs):
if not isinstance(figs_and_ids, PlotIDTransfer): if not isinstance(figs_and_ids, PlotIDTransfer):
raise RuntimeError("figs_and_ids is not an instance of " "PlotIDTransfer.") raise RuntimeError("figs_and_ids is not an instance of PlotIDTransfer.")
self.figure = figs_and_ids.figs self.figure = figs_and_ids.figs
self.figure_ids = figs_and_ids.figure_ids self.figure_ids = figs_and_ids.figure_ids
self.src_datapaths = src_datapaths self.src_datapaths = src_datapaths
self.dst_path = os.path.abspath(dst_path) self.dst_path = os.path.abspath(dst_path)
self.plot_names = plot_names
self.data_storage = kwargs.get("data_storage", "individual") self.data_storage = kwargs.get("data_storage", "individual")
self.dst_path_head, self.dst_dirname = os.path.split(self.dst_path) self.dst_path_head, self.dst_dirname = os.path.split(self.dst_path)
self.plot_names = kwargs.get("plot_names", self.figure_ids)
def __str__(self): def __str__(self):
"""Representation if an object of this class is printed.""" """Representation if an object of this class is printed."""
...@@ -91,7 +91,7 @@ class PublishOptions: ...@@ -91,7 +91,7 @@ class PublishOptions:
# Check if data_storage is a string # Check if data_storage is a string
if not isinstance(self.data_storage, str): if not isinstance(self.data_storage, str):
raise TypeError("The specified data_storage method is not a " "string.") raise TypeError("The specified data_storage method is not a string.")
def export(self): def export(self):
""" """
...@@ -160,7 +160,7 @@ class PublishOptions: ...@@ -160,7 +160,7 @@ class PublishOptions:
if delete_dir in ("yes", "y", "Yes", "YES"): if delete_dir in ("yes", "y", "Yes", "YES"):
shutil.rmtree(dst_path_invisible) shutil.rmtree(dst_path_invisible)
raise RuntimeError( raise RuntimeError(
"Publishing was unsuccessful. " "Try re-running publish." "Publishing was unsuccessful. Try re-running publish."
) from exc ) from exc
case _: case _:
raise ValueError( raise ValueError(
...@@ -233,7 +233,7 @@ class PublishOptions: ...@@ -233,7 +233,7 @@ class PublishOptions:
) )
def publish(figs_and_ids, src_datapath, dst_path, plot_name, **kwargs): def publish(figs_and_ids, src_datapath, dst_path, **kwargs):
""" """
Save plot, data and measuring script. Save plot, data and measuring script.
...@@ -245,8 +245,7 @@ def publish(figs_and_ids, src_datapath, dst_path, plot_name, **kwargs): ...@@ -245,8 +245,7 @@ def publish(figs_and_ids, src_datapath, dst_path, plot_name, **kwargs):
Path to data that should be published. Path to data that should be published.
dst_path : str dst_path : str
Path to the destination directory. Path to the destination directory.
plot_name : str or list of str
Name for the exported plot.
**kwargs : dict, optional **kwargs : dict, optional
Extra arguments for additional publish options. Extra arguments for additional publish options.
...@@ -258,14 +257,14 @@ def publish(figs_and_ids, src_datapath, dst_path, plot_name, **kwargs): ...@@ -258,14 +257,14 @@ def publish(figs_and_ids, src_datapath, dst_path, plot_name, **kwargs):
will reference this data via sym link. will reference this data via sym link.
individual [default]: The complete raw data will be copied to a individual [default]: The complete raw data will be copied to a
folder for every plot, respectively. folder for every plot, respectively.
plot_names : str or list of str, optional
Name for the exported plot. If not provided, the corresponding IDs will be used.
Returns Returns
------- -------
None. None.
""" """
publish_container = PublishOptions( publish_container = PublishOptions(figs_and_ids, src_datapath, dst_path, **kwargs)
figs_and_ids, src_datapath, dst_path, plot_name, **kwargs
)
publish_container.validate_input() publish_container.validate_input()
publish_container.export() publish_container.export()
...@@ -58,7 +58,7 @@ class TestPublish(unittest.TestCase): ...@@ -58,7 +58,7 @@ class TestPublish(unittest.TestCase):
PlotIDTransfer(FIG, "testID"), PlotIDTransfer(FIG, "testID"),
SRC_DIR, SRC_DIR,
DST_PATH + "/", DST_PATH + "/",
PIC_NAME, plot_names=PIC_NAME,
data_storage="individual", data_storage="individual",
) )
assert os.path.isfile(os.path.join(DST_PATH, "testID", PIC_NAME + ".png")) assert os.path.isfile(os.path.join(DST_PATH, "testID", PIC_NAME + ".png"))
...@@ -70,29 +70,47 @@ class TestPublish(unittest.TestCase): ...@@ -70,29 +70,47 @@ class TestPublish(unittest.TestCase):
"from a Python script. Therefore, the script cannot be " "from a Python script. Therefore, the script cannot be "
"copied.", "copied.",
) )
def test_publish_multiple_figs(self): def test_publish_multiple_figs_custom_name(self):
""" """
Test publish with multiple figures and check if all exported picture Test publish with multiple figures and check if all exported picture
files exist. files exist with the provided strings as their names.
""" """
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME_LIST) publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, plot_names=PIC_NAME_LIST)
for i, name in enumerate(PIC_NAME_LIST): for i, name in enumerate(PIC_NAME_LIST):
assert os.path.isfile(os.path.join(DST_PATH, IDS_AS_LIST[i], name + ".png")) assert os.path.isfile(os.path.join(DST_PATH, IDS_AS_LIST[i], name + ".png"))
# Skip test if tests are run from command line.
@unittest.skipIf(
not os.path.isfile(sys.argv[0]),
"Publish is not called "
"from a Python script. Therefore, the script cannot be "
"copied.",
)
def test_publish_multiple_figs_standard_name(self):
"""
Test publish with multiple figures and check if all exported picture
files exist with the IDs as their names.
"""
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH)
for identifier in IDS_AS_LIST:
assert os.path.isfile(
os.path.join(DST_PATH, identifier, identifier + ".png")
)
def test_figs_and_ids(self): def test_figs_and_ids(self):
""" """
Test if RuntimeError is raised when figs_and_ids is not an Test if RuntimeError is raised when figs_and_ids is not an
PlotIDTransfer object. PlotIDTransfer object.
""" """
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
publish("FIGS_AND_IDS", SRC_DIR, DST_PATH, PIC_NAME_LIST) publish("FIGS_AND_IDS", SRC_DIR, DST_PATH)
def test_wrong_ids(self): def test_wrong_ids(self):
"""Test if Error is raised if IDs are of wrong type.""" """Test if Error is raised if IDs are of wrong type."""
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
publish(PlotIDTransfer(FIG, 3), SRC_DIR, DST_PATH, PIC_NAME) publish(PlotIDTransfer(FIG, 3), SRC_DIR, DST_PATH)
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
publish(PlotIDTransfer(FIG, ["i", 4]), SRC_DIR, DST_PATH, PIC_NAME) publish(PlotIDTransfer(FIG, ["i", 4]), SRC_DIR, DST_PATH)
def test_publish_multiple_src_files(self): def test_publish_multiple_src_files(self):
""" """
...@@ -105,7 +123,7 @@ class TestPublish(unittest.TestCase): ...@@ -105,7 +123,7 @@ class TestPublish(unittest.TestCase):
FIGS_AND_IDS, FIGS_AND_IDS,
files_and_dir, files_and_dir,
DST_PATH, DST_PATH,
PIC_NAME_LIST, plot_names=PIC_NAME_LIST,
data_storage="individual", data_storage="individual",
) )
for identifier in IDS_AS_LIST: for identifier in IDS_AS_LIST:
...@@ -117,14 +135,14 @@ class TestPublish(unittest.TestCase): ...@@ -117,14 +135,14 @@ class TestPublish(unittest.TestCase):
def test_src_directory(self): def test_src_directory(self):
"""Test if Error is raised when source directory does not exist.""" """Test if Error is raised when source directory does not exist."""
with self.assertRaises(FileNotFoundError): with self.assertRaises(FileNotFoundError):
publish(FIGS_AND_IDS, "not_existing_folder", DST_PATH, PIC_NAME) publish(FIGS_AND_IDS, "not_existing_folder", DST_PATH)
def test_src_directory_type(self): def test_src_directory_type(self):
"""Test if Error is raised when source directory is not a string.""" """Test if Error is raised when source directory is not a string."""
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
publish(FIGS_AND_IDS, [SRC_DIR, 4], DST_PATH, PIC_NAME) publish(FIGS_AND_IDS, [SRC_DIR, 4], DST_PATH)
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
publish(FIGS_AND_IDS, 4, DST_PATH, PIC_NAME) publish(FIGS_AND_IDS, 4, DST_PATH)
def test_dst_directory(self): def test_dst_directory(self):
""" """
...@@ -132,7 +150,7 @@ class TestPublish(unittest.TestCase): ...@@ -132,7 +150,7 @@ class TestPublish(unittest.TestCase):
destination dir does not exist. destination dir does not exist.
""" """
with self.assertRaises(FileNotFoundError): with self.assertRaises(FileNotFoundError):
publish(FIGS_AND_IDS, SRC_DIR, "not_existing_folder/data", PIC_NAME) publish(FIGS_AND_IDS, SRC_DIR, "not_existing_folder/data")
def test_script_exists(self): def test_script_exists(self):
""" """
...@@ -158,8 +176,7 @@ class TestPublish(unittest.TestCase): ...@@ -158,8 +176,7 @@ class TestPublish(unittest.TestCase):
"from plotid.publish import publish\n" "from plotid.publish import publish\n"
"from plotid.plotoptions import PlotIDTransfer\n" "from plotid.plotoptions import PlotIDTransfer\n"
"publish(PlotIDTransfer(plt.figure(), 'testID2')," "publish(PlotIDTransfer(plt.figure(), 'testID2'),"
" 'test_src_folder', 'test_parent/test_dst_folder'," " 'test_src_folder', 'test_parent/test_dst_folder')",
" 'test_picture')",
], ],
capture_output=True, capture_output=True,
check=True, check=True,
...@@ -172,8 +189,7 @@ class TestPublish(unittest.TestCase): ...@@ -172,8 +189,7 @@ class TestPublish(unittest.TestCase):
"from plotid.publish import publish\n" "from plotid.publish import publish\n"
"from plotid.plotoptions import PlotIDTransfer\n" "from plotid.plotoptions import PlotIDTransfer\n"
"publish(PlotIDTransfer(plt.figure(), 'testID2'), " "publish(PlotIDTransfer(plt.figure(), 'testID2'), "
"'test_src_folder', 'test_parent/test_dst_folder', " "'test_src_folder', 'test_parent/test_dst_folder')",
"'test_picture')",
], ],
capture_output=True, capture_output=True,
) )
...@@ -199,9 +215,7 @@ class TestPublish(unittest.TestCase): ...@@ -199,9 +215,7 @@ class TestPublish(unittest.TestCase):
os.mkdir(os.path.join(DST_PATH, IDS_AS_LIST[0])) os.mkdir(os.path.join(DST_PATH, IDS_AS_LIST[0]))
# Mock user input as 'yes' # Mock user input as 'yes'
with patch("builtins.input", return_value="yes"): with patch("builtins.input", return_value="yes"):
publish( publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, data_storage="individual")
FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME, data_storage="individual"
)
# Skip test if tests are run from command line. # Skip test if tests are run from command line.
@unittest.skipIf( @unittest.skipIf(
...@@ -221,7 +235,7 @@ class TestPublish(unittest.TestCase): ...@@ -221,7 +235,7 @@ class TestPublish(unittest.TestCase):
# Mock user input as 'no' # Mock user input as 'no'
with patch("builtins.input", return_value="no"): with patch("builtins.input", return_value="no"):
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME) publish(FIGS_AND_IDS, SRC_DIR, DST_PATH)
# Skip test if tests are run from command line. # Skip test if tests are run from command line.
@unittest.skipIf( @unittest.skipIf(
...@@ -241,9 +255,7 @@ class TestPublish(unittest.TestCase): ...@@ -241,9 +255,7 @@ class TestPublish(unittest.TestCase):
# Mock user input as empty (no should be default). # Mock user input as empty (no should be default).
with patch("builtins.input", return_value=""): with patch("builtins.input", return_value=""):
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
publish( publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, data_storage="individual")
FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME, data_storage="individual"
)
# Skip test if tests are run from command line. # Skip test if tests are run from command line.
@unittest.skipIf( @unittest.skipIf(
...@@ -264,7 +276,7 @@ class TestPublish(unittest.TestCase): ...@@ -264,7 +276,7 @@ class TestPublish(unittest.TestCase):
# Mock user input as 'yes' for deleting the partial copied data # Mock user input as 'yes' for deleting the partial copied data
with patch("builtins.input", side_effect=["yes", "yes"]): with patch("builtins.input", side_effect=["yes", "yes"]):
with self.assertRaises(RuntimeError): with self.assertRaises(RuntimeError):
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME) publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, plot_names=PIC_NAME)
assert not os.path.isdir(invisible_path1) assert not os.path.isdir(invisible_path1)
os.remove("test_picture1.tmp.png") os.remove("test_picture1.tmp.png")
os.remove("test_picture2.tmp.png") os.remove("test_picture2.tmp.png")
...@@ -284,16 +296,12 @@ class TestPublish(unittest.TestCase): ...@@ -284,16 +296,12 @@ class TestPublish(unittest.TestCase):
""" """
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
publish( publish(
FIGS_AND_IDS, FIGS_AND_IDS, SRC_DIR, DST_PATH, data_storage="none_existing_method"
SRC_DIR,
DST_PATH,
PIC_NAME,
data_storage="none_existing_method",
) )
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME, data_storage=3) publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, data_storage=3)
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME, data_storage=[]) publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, data_storage=[])
def test_str(self): def test_str(self):
""" """
...@@ -301,7 +309,7 @@ class TestPublish(unittest.TestCase): ...@@ -301,7 +309,7 @@ class TestPublish(unittest.TestCase):
correct. correct.
""" """
self.maxDiff = None self.maxDiff = None
publish_obj = PublishOptions(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME) publish_obj = PublishOptions(FIGS_AND_IDS, SRC_DIR, DST_PATH)
self.assertIn( self.assertIn(
"<class 'plotid.publish.PublishOptions'>: {'figure': " "<class 'plotid.publish.PublishOptions'>: {'figure': "
"[<Figure size 640x480 with 0 Axes>, <Figure size" "[<Figure size 640x480 with 0 Axes>, <Figure size"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment