diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..b57a0c6 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +from .robohash import Robohash \ No newline at end of file diff --git a/robohash.py b/robohash.py index 0a36162..af78d67 100644 --- a/robohash.py +++ b/robohash.py @@ -3,19 +3,24 @@ import os import hashlib import Image - class Robohash(object): """ Robohash is a quick way of generating unique avatars for a site. The original use-case was to create somewhat memorable images to represent a RSA key. """ - def __init__(self,string,hashcount=11): + def __init__(self,string,hashcount=11,ignoreext = True): """ Creates our Robohasher Takes in the string to make a Robohash out of. """ + + # Optionally remove an images extension before hashing. + if ignoreext is True: + string = self._remove_exts(string) + string = string.encode('utf-8') + hash = hashlib.sha512() hash.update(string) self.hexdigest = hash.hexdigest() @@ -28,12 +33,32 @@ class Robohash(object): self.iter = 4 self._create_hashes(hashcount) + self.resourcedir = os.path.dirname(__file__) + '/' # Get the list of backgrounds and RobotSets - self.sets = self._listdirs('sets') - self.bgsets = self._listdirs('backgrounds') + self.sets = self._listdirs(self.resourcedir + 'sets') + self.bgsets = self._listdirs(self.resourcedir + 'backgrounds') # Get the colors in set1 - self.colors = self._listdirs('sets/set1') + self.colors = self._listdirs(self.resourcedir + 'sets/set1') + self.format = 'png' + + def _remove_exts(self,string): + """ + Sets the string, to create the Robohash + """ + + # If the user hasn't disabled it, we will detect image extensions, such as .png, .jpg, etc. + # We'll remove them from the string before hashing. + # This ensures that /Bear.png and /Bear.bmp will send back the same image, in different formats. + + if string.lower().endswith(('.png','.gif','.jpg','.bmp','.jpeg','.ppm','.datauri')): + format = string[string.rfind('.') +1 :len(string)] + if format.lower() == 'jpg': + format = 'jpeg' + self.format = format + string = string[0:string.rfind('.')] + return string + def _create_hashes(self,count): """ @@ -55,7 +80,6 @@ class Robohash(object): Go through each subdirectory of `path`, and choose one file from each to use in our hash. Continue to increase self.iter, so we use a different 'slot' of randomness each time. """ - chosen_files = [] # Get a list of all subdirectories @@ -79,18 +103,44 @@ class Robohash(object): return chosen_files - def assemble(self,roboset=None,format='png',bgset=None,sizex=300,sizey=300): + def assemble(self,roboset=None,color=None,format=None,bgset=None,sizex=300,sizey=300): """ Build our Robot! Returns the robot image itself. """ - # Set a default set for the robot - if roboset is None: + # Allow users to manually specify a robot 'set' that they like. + # Ensure that this is one of the allowed choices, or allow all + # If they don't set one, take the first entry from sets above. + + if roboset == 'any': + roboset = self.sets[self.hasharray[1] % len(self.sets) ] + elif roboset in self.sets: + roboset = roboset + else: roboset = self.sets[0] + + + # Only set1 is setup to be color-seletable. The others don't have enough pieces in various colors. + # This could/should probably be expanded at some point.. + # Right now, this feature is almost never used. ( It was < 44 requests this year, out of 78M reqs ) + if roboset == 'set1': - randomcolor = self.colors[self.hasharray[0] % len(self.colors) ] - roboset = 'set1/' + randomcolor + if color in self.colors: + roboset = 'set1/' + color + else: + randomcolor = self.colors[self.hasharray[0] % len(self.colors) ] + roboset = 'set1/' + randomcolor + + # If they specified a background, ensure it's legal, then give it to them. + if bgset in self.bgsets: + bgset = bgset + elif bgset == 'any': + bgset = self.bgsets[ self.hasharray[2] % len(self.bgsets) ] + + # If we set a format based on extension earlier, use that. Otherwise, PNG. + if format is None: + format = self.format # Each directory in our set represents one piece of the Robot, such as the eyes, nose, mouth, etc. @@ -102,17 +152,17 @@ class Robohash(object): # First, we'll get a list of parts of our robot. - roboparts = self._get_list_of_files('sets/' + roboset) + roboparts = self._get_list_of_files(self.resourcedir + 'sets/' + roboset) # Now that we've sorted them by the first number, we need to sort each sub-category by the second. roboparts.sort(key=lambda x: x.split("#")[1]) if bgset is not None: bglist = [] - backgrounds = os.listdir('backgrounds/' + bgset) + backgrounds = os.listdir(self.resourcedir + 'backgrounds/' + bgset) backgrounds.sort() for ls in backgrounds: if not ls.startswith("."): - bglist.append('backgrounds/' + bgset + "/" + ls) + bglist.append(self.resourcedir + 'backgrounds/' + bgset + "/" + ls) background = bglist[self.hasharray[3] % len(bglist)] # Paste in each piece of the Robot. diff --git a/webfront.py b/webfront.py index b5d4c79..9b6f73b 100755 --- a/webfront.py +++ b/webfront.py @@ -223,6 +223,7 @@ class ImgHandler(tornado.web.RequestHandler): sizey = 300 format = "png" bgset = None + color = None # Normally, we pass in arguments with standard HTTP GET variables, such as # ?set=any and &size=100x100 @@ -259,17 +260,11 @@ class ImgHandler(tornado.web.RequestHandler): string = self.request.remote_ip - # If the user hasn't disabled it, detect if there is requested extension. - # If so, remove it from the string, but set the format. - # This ensures that /Bear.png and /Bear.bmp will send back the same image, in different formats. - if args.get('ignoreext','false').lower() != 'true': - if string.lower().endswith(('.png','.gif','.jpg','.bmp','.jpeg','.ppm','.datauri')): - format = string[string.rfind('.') +1 :len(string)] - if format.lower() == 'jpg': - format = 'jpeg' - string = string[0:string.rfind('.')] + # Detect if the user has passed in a flag to ignore extensions. + # Pass this along to to Robohash obj later on. + + ignoreext = args.get('ignoreext','false').lower() == 'true' - # Split the size variable in to sizex and sizey if "size" in args: sizex,sizey = args['size'].split("x") @@ -306,6 +301,7 @@ class ImgHandler(tornado.web.RequestHandler): # Create our Robohashing object r = Robohash(string) + # Allow users to manually specify a robot 'set' that they like. # Ensure that this is one of the allowed choices, or allow all # If they don't set one, take the first entry from sets above. @@ -333,25 +329,23 @@ class ImgHandler(tornado.web.RequestHandler): # Right now, this feature is almost never used. ( It was < 44 requests this year, out of 78M reqs ) if args.get('color') in r.colors: - roboset = 'set1/' + args.get('color') + roboset = 'set1' + color = args.get('color') # If they DID choose set1, randomly choose a color. - if roboset == 'set1': - randomcolor = r.colors[r.hasharray[0] % len(r.colors) ] - roboset = 'set1/' + randomcolor + if roboset == 'set1' and color is None: + color = r.colors[r.hasharray[0] % len(r.colors) ] + roboset = 'set1' - # Allow them to set a background, or default to None - if args.get('bgset') in r.bgsets: + # Allow them to set a background, or keep as None + if args.get('bgset') in r.bgsets + ['any']: bgset = args.get('bgset') - elif args.get('bgset','').lower() == 'any': - bgset = r.bgsets[ r.hasharray[2] % len(r.bgsets) ] - - + # We're going to be returning the image directly, so tell the browser to expect a binary. self.set_header("Content-Type", "image/" + format) # Build our Robot. - r.assemble(roboset=roboset,format=format,bgset=bgset,sizex=sizex,sizey=sizey) + r.assemble(roboset=roboset,format=format,bgset=bgset,color=color,sizex=sizex,sizey=sizey) # Print the Robot to the handler, as a file-like obj if r.format != 'datauri':