From 29d847dc541bcf7b5cffa6b86f3be452048e63b5 Mon Sep 17 00:00:00 2001 From: Lorena Mesa Date: Wed, 17 Jul 2024 05:37:17 +0000 Subject: [PATCH] Add resize logic for identicon --- README.md | 2 +- src/main.py | 47 ++++++++++++++++++++++++++------------- test/sample_cases_test.py | 18 ++++++++++++++- 3 files changed, 50 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index fd7090e..299bc62 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Users often work collaboratively in digital environments where a profile picture ## Identicon Requirements 1. The identicon's should be symmetrical meaning the left horizontal half is equal to the right horizontal half. -2. The identicon is 5X5 pixels, following the standard specified for [GitHub identicons](https://github.blog/2013-08-14-identicons/) +2. The identicon is 5X5 pixels, following the standard specified for [GitHub identicons](https://github.blog/2013-08-14-identicons/), so we'll generate square identicons only with a default of 250X250 pixels 3. Identicon's should use accessible colors as specified by [W3](https://www.w3.org/WAI/WCAG21/Techniques/general/G207) ## TODO: diff --git a/src/main.py b/src/main.py index 8429b7d..6bf7375 100644 --- a/src/main.py +++ b/src/main.py @@ -53,11 +53,12 @@ def _generate_image_fill_color(self, md5hash_str: str) -> tuple: """ return tuple(int(md5hash_str[i:i+2], base=16) for i in range(0, 2*3, 2)) - def draw_image(self, filename: str=None) -> Image: + def draw_image(self, filename: str=None, dimensions: int=0) -> Image: """ Function that generates a grid - a list of lists - indicating which pixels are to be filled and uses the md5hash_str to generate an image fill color. Function creates a PIL Image, drawing it, - and saving it. + and saving it. By default a 250 pixel by 250 pixel identicon is created, if upon executing the code + a dimensions parameter is passed in the image will be resized. :param filename: filename of PIL png image generated :return: None @@ -66,6 +67,7 @@ def draw_image(self, filename: str=None) -> Image: fill_color: tuple = self._generate_image_fill_color(self.md5hash_str) grid: list[list] = self._build_grid() + # Default to a 250X250 pixel image SQUARE: int = 50 size: tuple = (5 * 50, 5 * 50) bg_color: tuple = (214,214,214) @@ -88,40 +90,55 @@ def draw_image(self, filename: str=None) -> Image: if not filename: filename: str = 'example' - - # TODO: Confirm overwrite file is one of same name exists + + if dimensions: + wpercent: float = (dimensions / float(image.size[0])) + hsize: int = int((float(image.size[1]) * float(wpercent))) + image = image.resize((dimensions, hsize), Image.Resampling.LANCZOS) + image.save(f'{filename}.png') + if __name__ == '__main__': parser = argparse.ArgumentParser( description="Generate an identicon with Python 3.", usage="""Example: python main.py -s='931D387731bBbC988B31220' or add the optional -o flag to specify name of identicon - image generated such as python main.py -s='931D387731bBbC988B31220' -o='my_identicon.jpg'.""" + image generated such as python main.py -s='931D387731bBbC988B31220' -o='my_identicon.jpg'." Additionally can specify the + square dimensions in pixels for the identicon such as python main.py -s='931D387731bBbC988B31220' -d 150.""" ) - def len_gt_zero(input_str: str): if len(input_str) > 0: return input_str - raise argparse.ArgumentTypeError("Input string must have length greater than 0 in order to generate an identicon.") - + else: + raise argparse.ArgumentTypeError("Outfile filename must have length greater than 0 in order to generate an identicon.") + def dimensions_gt_zero(input_dimensions: str): + if not input_dimensions.isdigit(): + raise argparse.ArgumentTypeError("Input square dimension (same height and width) must be a legal int value.") + elif int(input_dimensions) >= 1: + return int(input_dimensions) + else: + raise argparse.ArgumentTypeError("Input square dimension (same height and width) must be greater than 1.") parser.add_argument( "-s", "--string", - default="", type=str, required=True, - help="An input string used to generate an identicon.", + help="An input string used to generate a squaer identicon.", ) parser.add_argument( "-o", "--output", - default="", - type=str, - required=False, - help="Name for output identicon image generated.", + type=len_gt_zero, + help="Name for output square identicon image generated.", + ) + parser.add_argument( + "-d", + "--dimensions", + type=dimensions_gt_zero, + help="Optional dimensionals parameter for outputing square identicon image generated." ) args = parser.parse_args() identicon = Identicon(input_str=args.string) - identicon.draw_image(filename=args.output) + identicon.draw_image(filename=args.output, dimensions=args.dimensions) diff --git a/test/sample_cases_test.py b/test/sample_cases_test.py index a4421ed..b05677e 100644 --- a/test/sample_cases_test.py +++ b/test/sample_cases_test.py @@ -19,6 +19,11 @@ def test_ui_fails_to_create_identicon_with_input_string_missing(self): with self.assertRaises(subprocess.CalledProcessError) as context: subprocess.check_output(f"python3 {PROJECT_ROOT}/src/main.py", shell=True, stderr=subprocess.STDOUT).strip() self.assertIn("main.py: error: the following arguments are required: -s/--string", context.exception.output.decode('utf-8')) + + def test_ui_fails_to_create_identicon_with_dimensions_lt_1(self): + with self.assertRaises(subprocess.CalledProcessError) as context: + subprocess.check_output(f"python3 {PROJECT_ROOT}/src/main.py -d 0", shell=True, stderr=subprocess.STDOUT).strip() + self.assertIn("main.py: error: argument -d/--dimensions: Input square dimension (same height and width) must be greater than 1.", context.exception.output.decode('utf-8')) class TestHappyPath(unittest.TestCase): @@ -71,7 +76,18 @@ def test_does_not_create_same_identicon_for_different_input_strings(self): remove(f"{PROJECT_ROOT}/john.png") remove(f"{PROJECT_ROOT}/jane.png") + def test_successfully_resizes_identicon_gt_250_when_dimensions_provided(self): + identicon_john = Identicon("john") + identicon_john.draw_image(filename="john", dimensions=300) + + # Assertions + generated_john = Image.open(f"{PROJECT_ROOT}/john.png", mode="r") + self.assertIsInstance(generated_john, PngImagePlugin.PngImageFile) + self.assertEqual(generated_john.size, (300, 300)) + + # Cleanup + remove(f"{PROJECT_ROOT}/john.png") -if __name__ == '__maipython -m unittest__': +if __name__ == "__main__": unittest.main() \ No newline at end of file