Skip to content

Commit

Permalink
Add resize logic for identicon
Browse files Browse the repository at this point in the history
  • Loading branch information
Lorena Mesa committed Jul 17, 2024
1 parent e95e686 commit 29d847d
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 17 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
47 changes: 32 additions & 15 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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)
18 changes: 17 additions & 1 deletion test/sample_cases_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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()

0 comments on commit 29d847d

Please sign in to comment.