Skip to content
Snippets Groups Projects
Commit 6708a678 authored by Mayr, Hannes's avatar Mayr, Hannes
Browse files

Restructure and introduce additional object to transfer figures and IDs from...

Restructure and introduce additional object to transfer figures and IDs from tagplot() to publish().
parent 654327a9
No related branches found
No related tags found
7 merge requests!41Include latest changes in main branch,!37Merge dev upstream changes into improve/metadata,!34Include architecture diagram in docs,!32SAST implementation,!27Update documentation and version number,!26Merge !23, !24, !25 into main,!22Restructure and introduce additional object to transfer figures and IDs from...
Pipeline #802657 waiting for manual action
......@@ -29,7 +29,7 @@ def create_id(id_method):
case 'time':
figure_id = time.time() # UNIX Time
figure_id = hex(int(figure_id)) # convert time to hexadecimal str
time.sleep(0.5) # break for avoiding duplicate IDs
time.sleep(1) # break for avoiding duplicate IDs
case 'random':
figure_id = str(uuid.uuid4()) # creates a random UUID
figure_id = figure_id[0:8] # only use first 8 numbers
......
......@@ -48,13 +48,14 @@ IMGS_AS_LIST = [IMG1, IMG2]
# id_method='random', prefix=PROJECT_ID)
# Example for how to use tagplot with image files
[TAGGED_FIGS, ID] = tagplot(IMGS_AS_LIST, 'image', prefix=PROJECT_ID,
FIGS_AND_IDS = tagplot(IMGS_AS_LIST, 'image', prefix=PROJECT_ID,
id_method='time', location='west')
print(FIGS_AND_IDS.figure_ids)
# %% Publish
# Arguments: Source directory or files as list, destination directory, figures,
# plots or images.
publish(['../README.md', '../docs', '../LICENSE'],
'/home/chief/Dokumente/fst/plotid_python/data/',
TAGGED_FIGS, 'Bild')
publish(FIGS_AND_IDS, ['../README.md', '../docs', '../LICENSE'],
'/home/chief/Dokumente/fst/plotid_python/data/', 'Bild')
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Contains the PlotOptions class."""
"""Contains the PlotOptions and PlotIDTransfer classes."""
class PlotOptions:
......@@ -75,3 +75,29 @@ class PlotOptions:
self.figs = [self.figs]
return 0
class PlotIDTransfer:
"""
Container to transfer objects from tagplot() to publish().
Methods
-------
__init__
validate_input : Check if input is correct type.
Attributes
----------
figs : figure object or list of figures
Tagged figures.
figure_ids: str or list of str
IDs that the figures are tagged with.
"""
def __init__(self, figs, figure_ids):
self.figs = figs
self.figure_ids = figure_ids
def __str__(self):
"""Representation if an object of this class is printed."""
return str(self.__class__) + ": " + str(self.__dict__)
......@@ -31,11 +31,13 @@ class PublishOptions:
Export the plot and copy specified files to the destiantion folder.
"""
def __init__(self, src_datapaths, dst_path, figure, plot_names, **kwargs):
def __init__(self, figs_and_ids, src_datapaths, dst_path, plot_names,
**kwargs):
self.figure = figs_and_ids.figs
self.figure_ids = figs_and_ids.figure_ids
self.src_datapaths = src_datapaths
self.dst_path = dst_path
self.figure = figure
self.plot_names = plot_names
self.data_storage = kwargs.get('data_storage', 'individual')
self.dst_path_head, self.dst_dirname = os.path.split(self.dst_path)
......@@ -47,6 +49,10 @@ class PublishOptions:
self.dst_path_head, self.dst_dirname = os.path.split(
self.dst_path_head)
def __str__(self):
"""Representation if an object of this class is printed."""
return str(self.__class__) + ": " + str(self.__dict__)
def validate_input(self):
"""
Validate if input for PublishOptions is correct type.
......@@ -64,6 +70,18 @@ class PublishOptions:
None.
"""
# Check if IDs are str
if isinstance(self.figure_ids, str):
self.figure_ids = [self.figure_ids]
if isinstance(self.figure_ids, list):
for identifier in self.figure_ids:
if not isinstance(identifier, str):
raise TypeError('The list of figure_ids contains an object'
' which is not a string.')
else:
raise TypeError('The specified figure_ids are neither a string nor'
' a list of strings.')
if not os.path.isfile(sys.argv[0]):
raise FileNotFoundError('Cannot copy original python script. '
'Running publish from a shell is not '
......@@ -107,7 +125,7 @@ class PublishOptions:
def export(self):
"""
Export the plot and copy specified files to the destiantion folder.
Export the plot and copy specified files to the destination folder.
Raises
------
......@@ -217,7 +235,7 @@ class PublishOptions:
os.remove(path)
def publish(src_datapath, dst_path, figure, plot_name, **kwargs):
def publish(figs_and_ids, src_datapath, dst_path, plot_name, **kwargs):
"""
Save plot, data and measuring script.
......@@ -248,7 +266,7 @@ def publish(src_datapath, dst_path, figure, plot_name, **kwargs):
None.
"""
publish_container = PublishOptions(src_datapath, dst_path, figure,
publish_container = PublishOptions(figs_and_ids, src_datapath, dst_path,
plot_name, **kwargs)
publish_container.validate_input()
publish_container.export()
......@@ -8,7 +8,7 @@ Functions:
import os
from PIL import Image, ImageDraw, ImageFont, ImageOps
from plotid.create_id import create_id
from plotid.plotoptions import PlotOptions
from plotid.plotoptions import PlotOptions, PlotIDTransfer
def tagplot_image(plotid_object):
......@@ -24,7 +24,7 @@ def tagplot_image(plotid_object):
Returns
-------
list with figures and IDs
PlotIDTransfer object
"""
# Check if plotid_object is a valid instance of PlotOptions
if not isinstance(plotid_object, PlotOptions):
......@@ -56,4 +56,6 @@ def tagplot_image(plotid_object):
int(img.height*(1-plotid_object.position[1]))), txt)
plotid_object.figs[i] = img
return [plotid_object.figs, plotid_object.figure_ids]
figs_and_ids = PlotIDTransfer(plotid_object.figs, plotid_object.figure_ids)
return figs_and_ids
......@@ -9,7 +9,7 @@ Functions:
import matplotlib
import matplotlib.pyplot as plt
from plotid.create_id import create_id
from plotid.plotoptions import PlotOptions
from plotid.plotoptions import PlotOptions, PlotIDTransfer
def tagplot_matplotlib(plotid_object):
......@@ -52,4 +52,6 @@ def tagplot_matplotlib(plotid_object):
rotation=plotid_object.rotation,
fontsize=fontsize, color=color)
fig.tight_layout()
return [plotid_object.figs, plotid_object.figure_ids]
figs_and_ids = PlotIDTransfer(plotid_object.figs, plotid_object.figure_ids)
return figs_and_ids
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Unittests for plotoptions.
"""
import unittest
from plotid.plotoptions import PlotOptions, PlotIDTransfer
ROTATION = 270
POSITION = (100, 200)
class TestTagplot(unittest.TestCase):
"""
Class for all unittests of the plotoptions module.
"""
def test_validate_input(self):
"""
Test if input validation runs successful.
"""
PlotOptions('FIG', ROTATION, POSITION, prefix='xyz',
id_method='random').validate_input()
def test_prefix(self):
""" Test if Error is raised if prefix is not a string. """
with self.assertRaises(TypeError):
PlotOptions(['FIG'], ROTATION, POSITION, prefix=3).validate_input()
def test_data_storage(self):
""" Test if Error is raised if id_method is not a string. """
with self.assertRaises(TypeError):
PlotOptions(['FIG'], ROTATION, POSITION,
id_method=4).validate_input()
def test_str_plotoptions(self):
"""
Test if the string representation of a PlotOptions object is correct.
"""
plot_obj = PlotOptions('FIG', ROTATION, POSITION, prefix='xyz',
id_method='random')
self.assertEqual(str(plot_obj),
"<class 'plotid.plotoptions.PlotOptions'>: {'figs': "
"'FIG', 'figure_ids': [], 'rotation': 270, 'position'"
": (100, 200), 'prefix': 'xyz', 'id_method': "
"'random'}")
def test_str_plotidtransfer(self):
"""
Test if the string representation of a PlotIDTransfer object is
correct.
"""
transfer_obj = PlotIDTransfer('FIG', [])
self.assertEqual(str(transfer_obj),
"<class 'plotid.plotoptions.PlotIDTransfer'>: "
"{'figs': 'FIG', 'figure_ids': []}")
if __name__ == '__main__':
unittest.main()
......@@ -12,7 +12,8 @@ import shutil
from subprocess import run, CalledProcessError
from unittest.mock import patch
import matplotlib.pyplot as plt
from plotid.publish import publish
from plotid.publish import publish, PublishOptions
from plotid.plotoptions import PlotIDTransfer
SRC_DIR = 'test_src_folder'
......@@ -22,9 +23,11 @@ DST_DIR = 'test_dst_folder'
DST_PARENT_DIR = 'test_parent'
DST_PATH = os.path.join(DST_PARENT_DIR, DST_DIR)
INVISIBLE_PATH = os.path.join(DST_PARENT_DIR, '.' + DST_DIR)
FIG = plt.figure()
FIG2 = plt.figure()
FIG = plt.figure(figsize=[6.4, 4.8], dpi=100)
FIG2 = plt.figure(figsize=[6.4, 4.8], dpi=100)
FIGS_AS_LIST = [FIG, FIG2]
IDS_AS_LIST = ['MR05_0x63203c6f', 'MR05_0x63203c70']
FIGS_AND_IDS = PlotIDTransfer(FIGS_AS_LIST, IDS_AS_LIST)
PIC_NAME_LIST = [PIC_NAME, 'second_picture']
......@@ -46,8 +49,8 @@ class TestPublish(unittest.TestCase):
'copied.')
def test_publish(self):
""" Test publish and check if an exported picture file exists. """
publish(SRC_DIR, DST_PATH + '/', FIG, PIC_NAME,
data_storage='individual')
publish(PlotIDTransfer(FIG, 'testID'), SRC_DIR, DST_PATH + '/',
PIC_NAME, data_storage='individual')
assert os.path.isfile(os.path.join(DST_PATH, PIC_NAME + '.png'))
# Skip test if tests are run from command line.
......@@ -59,10 +62,17 @@ class TestPublish(unittest.TestCase):
Test publish with multiple figures and check if all exported picture
files exist.
"""
publish(SRC_DIR, DST_PATH, FIGS_AS_LIST, PIC_NAME_LIST)
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME_LIST)
for name in PIC_NAME_LIST:
assert os.path.isfile(os.path.join(DST_PATH, name + '.png'))
def test_wrong_ids(self):
""" Test if Error is raised if IDs are of wrong type. """
with self.assertRaises(TypeError):
publish(PlotIDTransfer(FIG, 3), SRC_DIR, DST_PATH, PIC_NAME)
with self.assertRaises(TypeError):
publish(PlotIDTransfer(FIG, ['i', 4]), SRC_DIR, DST_PATH, PIC_NAME)
def test_publish_multiple_src_files(self):
"""
Test publish with multiple source files and check
......@@ -70,7 +80,7 @@ class TestPublish(unittest.TestCase):
"""
files_and_dir = list(SRC_FILES)
files_and_dir.append(SRC_DIR)
publish(files_and_dir, DST_PATH, FIGS_AS_LIST, PIC_NAME_LIST,
publish(FIGS_AND_IDS, files_and_dir, DST_PATH, PIC_NAME_LIST,
data_storage='individual')
assert os.path.isdir(DST_PATH)
for file in SRC_FILES:
......@@ -79,19 +89,19 @@ class TestPublish(unittest.TestCase):
def test_src_directory(self):
""" Test if Error is raised when source directory does not exist."""
with self.assertRaises(FileNotFoundError):
publish('not_existing_folder', DST_PATH, FIG, PIC_NAME)
publish(FIGS_AND_IDS, 'not_existing_folder', DST_PATH, PIC_NAME)
def test_src_directory_type(self):
""" Test if Error is raised when source directory is not a string."""
with self.assertRaises(TypeError):
publish([SRC_DIR, 4], DST_PATH, FIG, PIC_NAME)
publish(FIGS_AND_IDS, [SRC_DIR, 4], DST_PATH, PIC_NAME)
with self.assertRaises(TypeError):
publish(4, DST_PATH, FIG, PIC_NAME)
publish(FIGS_AND_IDS, 4, DST_PATH, PIC_NAME)
def test_dst_directory(self):
""" Test if Error is raised when destination dir does not exist."""
with self.assertRaises(FileNotFoundError):
publish(SRC_DIR, 'not_existing_folder', FIG, PIC_NAME)
publish(FIGS_AND_IDS, SRC_DIR, 'not_existing_folder', PIC_NAME)
def test_script_exists(self):
"""
......@@ -112,14 +122,17 @@ class TestPublish(unittest.TestCase):
run([python, "-c",
"import matplotlib.pyplot as plt\n"
"from plotid.publish import publish\n"
"publish('test_src_folder', 'test_parent/test_dst_folder',"
" plt.figure(), 'test_picture')"],
"from plotid.plotoptions import PlotIDTransfer\n"
"publish(PlotIDTransfer(plt.figure(), 'testID2'),"
" 'test_src_folder', 'test_parent/test_dst_folder',"
" 'test_picture')"],
capture_output=True, check=True)
process = run([python, "-c",
"import matplotlib.pyplot as plt\n"
"from plotid.publish import publish\n"
"publish('test_src_folder', "
"'test_parent/test_dst_folder', plt.figure(), "
"from plotid.plotoptions import PlotIDTransfer\n"
"publish(PlotIDTransfer(plt.figure(), 'testID2'), "
"'test_src_folder', 'test_parent/test_dst_folder', "
"'test_picture')"],
capture_output=True)
assert ("FileNotFoundError: Cannot copy original python script. "
......@@ -138,7 +151,7 @@ class TestPublish(unittest.TestCase):
os.mkdir(DST_PATH)
# Mock user input as 'yes'
with patch('builtins.input', return_value='yes'):
publish(SRC_DIR, DST_PATH, FIG, PIC_NAME,
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME,
data_storage='individual')
# Skip test if tests are run from command line.
......@@ -154,7 +167,7 @@ class TestPublish(unittest.TestCase):
# Mock user input as 'no'
with patch('builtins.input', return_value='no'):
with self.assertRaises(RuntimeError):
publish(SRC_DIR, DST_PATH, FIG, PIC_NAME)
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME)
# Skip test if tests are run from command line.
@unittest.skipIf(not os.path.isfile(sys.argv[0]), 'Publish is not called '
......@@ -169,7 +182,7 @@ class TestPublish(unittest.TestCase):
# Mock user input as empty (no should be default).
with patch('builtins.input', return_value=''):
with self.assertRaises(RuntimeError):
publish(SRC_DIR, DST_PATH, FIG, PIC_NAME,
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME,
data_storage='individual')
# Skip test if tests are run from command line.
......@@ -186,29 +199,50 @@ class TestPublish(unittest.TestCase):
# Mock user input as 'yes'
with patch('builtins.input', return_value='yes'):
with self.assertRaises(RuntimeError):
publish(SRC_DIR, DST_PATH, FIG, PIC_NAME)
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME)
assert not os.path.isdir(INVISIBLE_PATH)
os.remove('test_picture1.png')
os.remove('test_picture2.png')
def test_plot_names(self):
""" Test if Error is raised if plot_name is not a string. """
with self.assertRaises(TypeError):
publish(SRC_DIR, DST_PATH, FIG, 7.6, data_storage='individual')
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, 7.6,
data_storage='individual')
with self.assertRaises(TypeError):
publish(SRC_DIR, DST_PATH, FIG, (), data_storage='individual')
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, ())
with self.assertRaises(TypeError):
publish(SRC_DIR, DST_PATH, FIG, ['test', 3])
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, ['test', 3])
def test_data_storage(self):
"""
Test if Error is raised when unsupported storage method was chosen.
"""
with self.assertRaises(ValueError):
publish(SRC_DIR, DST_PATH, FIG, PIC_NAME,
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME,
data_storage='none_existing_method')
with self.assertRaises(TypeError):
publish(SRC_DIR, DST_PATH, FIG, PIC_NAME, data_storage=3)
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME, data_storage=3)
with self.assertRaises(TypeError):
publish(SRC_DIR, DST_PATH, FIG, PIC_NAME, data_storage=[])
publish(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME, data_storage=[])
def test_str(self):
"""
Test if the string representation of a PublishOptions object is
correct.
"""
self.maxDiff = None
publish_obj = PublishOptions(FIGS_AND_IDS, SRC_DIR, DST_PATH, PIC_NAME)
self.assertEqual(str(publish_obj),
"<class 'plotid.publish.PublishOptions'>: {'figure': "
"[<Figure size 640x480 with 0 Axes>, <Figure size"
" 640x480 with 0 Axes>], 'figure_ids': "
"['MR05_0x63203c6f', 'MR05_0x63203c70'], "
"'src_datapaths': 'test_src_folder', 'dst_path': "
"'test_parent/test_dst_folder', 'plot_names': "
"'test_picture', 'data_storage': 'individual', "
"'dst_path_head': 'test_parent', 'dst_dirname': "
"'test_dst_folder'}")
def tearDown(self):
""" Delete all files created in setUp. """
......
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Unittests for tagplot
Unittests for tagplot.
"""
import os
......
......@@ -42,9 +42,11 @@ class TestTagplotImage(unittest.TestCase):
options = PlotOptions(IMGS_AS_LIST, ROTATION, POSITION,
prefix=PROJECT_ID, id_method=METHOD)
options.validate_input()
[figs, _] = tagplot_image(options)
self.assertIsInstance(figs[0], PngImagePlugin.PngImageFile)
self.assertIsInstance(figs[1], JpegImagePlugin.JpegImageFile)
figs_and_ids = tagplot_image(options)
self.assertIsInstance(figs_and_ids.figs[0],
PngImagePlugin.PngImageFile)
self.assertIsInstance(figs_and_ids.figs[1],
JpegImagePlugin.JpegImageFile)
def test_single_image(self):
"""
......@@ -53,8 +55,9 @@ class TestTagplotImage(unittest.TestCase):
"""
options = PlotOptions(IMG1, ROTATION, POSITION)
options.validate_input()
[figs, _] = tagplot_image(options)
self.assertIsInstance(figs[0], PngImagePlugin.PngImageFile)
figs_and_ids = tagplot_image(options)
self.assertIsInstance(figs_and_ids.figs[0],
PngImagePlugin.PngImageFile)
def test_image_not_str(self):
""" Test if Error is raised if wrong type of image is given. """
......
......@@ -32,9 +32,9 @@ class TestTagplotMatplotlib(unittest.TestCase):
options = PlotOptions(FIGS_AS_LIST, ROTATION, POSITION,
prefix=PROJECT_ID, id_method=METHOD)
options.validate_input()
[figs, _] = tagplot_matplotlib(options)
self.assertIsInstance(figs[0], Figure)
self.assertIsInstance(figs[1], Figure)
figs_and_ids = tagplot_matplotlib(options)
self.assertIsInstance(figs_and_ids.figs[0], Figure)
self.assertIsInstance(figs_and_ids.figs[1], Figure)
def test_single_mplfigure(self):
"""
......@@ -43,8 +43,8 @@ class TestTagplotMatplotlib(unittest.TestCase):
"""
options = PlotOptions(FIG1, ROTATION, POSITION)
options.validate_input()
[figs, _] = tagplot_matplotlib(options)
self.assertIsInstance(figs[0], Figure)
figs_and_ids = tagplot_matplotlib(options)
self.assertIsInstance(figs_and_ids.figs[0], Figure)
def test_mplerror(self):
""" Test if Error is raised if wrong type of figures is given. """
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment