diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/param/__init__.py | 56 | ||||
| -rw-r--r-- | lib/param/bool_.py | 37 | ||||
| -rw-r--r-- | lib/param/color.py | 34 | ||||
| -rw-r--r-- | lib/param/enum.py | 19 | ||||
| -rw-r--r-- | lib/param/float_.py | 39 | ||||
| -rw-r--r-- | lib/param/json.py | 14 | ||||
| -rw-r--r-- | lib/params.py | 2 | ||||
| -rw-r--r-- | lib/pb/__init__.py | 208 | ||||
| -rwxr-xr-x | lib/pb/generate.py | 113 | ||||
| -rwxr-xr-x | lib/pb/gradient.py | 47 | ||||
| -rwxr-xr-x | lib/pb/grid.py | 70 | ||||
| -rwxr-xr-x | lib/pb/pattern.py | 74 | ||||
| -rw-r--r-- | lib/pb/pb.py | 202 | ||||
| -rw-r--r-- | lib/s3cli.py | 39 |
14 files changed, 504 insertions, 450 deletions
diff --git a/lib/param/__init__.py b/lib/param/__init__.py index 2c05726..10ea7a6 100644 --- a/lib/param/__init__.py +++ b/lib/param/__init__.py @@ -4,55 +4,55 @@ import sys from config import WORKING_DIR class BadParamError(Exception): - pass + pass class Param(object): def __init__(self, classname="", **kwargs): - self._working_dir = WORKING_DIR - self._now = kwargs.get("now", str(int(time.time()))); - self._classname = classname - for key, value in kwargs.items(): - setattr(self, key, value) + self._working_dir = WORKING_DIR + self._now = kwargs.get("now", str(int(time.time()))); + self._classname = classname + for key, value in kwargs.items(): + setattr(self, key, value) def __nonzero__(self): - return True if self.value else False + return True if self.value else False def __str__(self): - return str(self.value) + return str(self.value) def __eq__(self, other): - return self.value == other + return self.value == other def __ne__(self, other): - return self.value != other + return self.value != other def set_val(self, value): - try: - self.value = value - except Exception as e: - self.err_warn("Unable to set value {}".format(value)) + try: + self.value = value + except Exception as e: + self.err_warn("Unable to set value {}".format(value)) def err_warn(self, s, error=None): - self._error_log(s, error=error); - raise BadParamError("%s - %s" % (self._classname, s)) - + self._error_log(s, error=error); + raise BadParamError("%s - %s" % (self._classname, s)) + def __getattr__(self, key): - try: - return self.__getattribute__(key); - except AttributeError: - return None + try: + return self.__getattribute__(key); + except AttributeError: + return None def err_fatal(self, s, error=None): - self._log(s, error, fatal=True); - sys.exit(1); + self._log(s, error, fatal=True); + sys.exit(1); def _error_log(self, s, error=None, fatal=False): - message = "ERROR - BAD PARAM" - if fatal: message += "- [FATAL] -" - sys.stderr.write("{}:{} - {}\n".format(message, self._classname, s)) - if error: - sys.stderr.write("PARAM ERROR: {}\n".format(str(error))) + message = "ERROR - BAD PARAM" + if fatal: message += "- [FATAL] -" + sys.stderr.write("{}:{} - {}\n".format(message, self._classname, s)) + if error: + sys.stderr.write("PARAM ERROR: {}\n".format(str(error))) from param.int_ import Int from param.raw import Raw diff --git a/lib/param/bool_.py b/lib/param/bool_.py index acce969..394dea6 100644 --- a/lib/param/bool_.py +++ b/lib/param/bool_.py @@ -1,19 +1,24 @@ +"""Defines the bool param type""" from param import Param import re -import sys class Bool(Param): - def __init__(self, value, classname=""): - super(Bool, self).__init__(classname=classname) - if value: - self.value = self._bool_correct(value) - else: - self.value = False - def _bool_correct(self, b): - if type(b) == str or type(b) == unicode: - if re.match(r'true', b, re.IGNORECASE): - return True - elif re.match(r'false', b, re.IGNORECASE): - return False - elif type(b) == bool: - return b - self.err_warn("Not a bool: %s" % str(b)) + """Defines the bool param type + Args: + value: the value of the bool (string or python bool) + classname: the name of the class to which the param belongs + """ + def __init__(self, value, classname=""): + super(Bool, self).__init__(classname=classname) + if value: + self.value = self._bool_correct(value) + else: + self.value = False + def _bool_correct(self, b): + if isinstance(b, str) or isinstance(b, unicode): + if re.match(r'true', b, re.IGNORECASE): + return True + elif re.match(r'false', b, re.IGNORECASE): + return False + elif isinstance(b, bool): + return b + self.err_warn("Not a bool: %s" % str(b)) diff --git a/lib/param/color.py b/lib/param/color.py index a339b7f..1c62955 100644 --- a/lib/param/color.py +++ b/lib/param/color.py @@ -1,17 +1,23 @@ +"""Defines the color param type""" from param import Param import re class Color(Param): - def __init__(self, value, classname=""): - super(Color, self).__init__(classname=classname) - try: - self.value = self._color_sanitize(value) - except Exception as e: - self.err_warn("Unable to sanitize the color: %s" % str(value)) - self.err_warn(str(e)) - def _color_sanitize(self, s): - if s == "": - return "transparent" - if re.match('(rgba?\([0-9]+,[0-9]+,[0-9]+\))|([a-zA-Z]+)|(\#[A-Ha-h0-9]+)', s): - return s.replace(' ', ''); - else: - self.err_warn("Not a color: {}\n".format(s)) + """Defines the color param type + Args: + value: the value of the color (string) + classname: the name of the class to which the param belongs + """ + def __init__(self, value, classname=""): + super(Color, self).__init__(classname=classname) + try: + self.value = self._color_sanitize(value) + except Exception as e: + self.err_warn("Unable to sanitize the color: %s" % str(value)) + self.err_warn(str(e)) + def _color_sanitize(self, s): + if s == "": + return "transparent" + if re.match('(rgba?\([0-9]+,[0-9]+,[0-9]+\))|([a-zA-Z]+)|(\#[A-Ha-h0-9]+)', s): + return s.replace(' ', '') + else: + self.err_warn("Not a color: {}\n".format(s)) diff --git a/lib/param/enum.py b/lib/param/enum.py index 49e11f6..c68adc9 100644 --- a/lib/param/enum.py +++ b/lib/param/enum.py @@ -1,8 +1,15 @@ +"""Defines the enum param type""" + from param import Param -import sys class Enum(Param): - def __init__(self, value, enum_values=[], classname=""): - super(Enum,self).__init__(classname=classname) - if value and value not in enum_values: - return self.err_warn("Value %s not in enum values" % str(value)) - self.value = value + """Defines the enum param type + Args: + value: the value of the param + enum_values: an array of possible value types + classname: name of the class that the param belongs to + """ + def __init__(self, value, enum_values=[], classname=""): + super(Enum, self).__init__(classname=classname) + if value and value not in enum_values: + self.err_warn("Value %s not in enum values" % str(value)) + self.value = value diff --git a/lib/param/float_.py b/lib/param/float_.py index f5d1816..88ed066 100644 --- a/lib/param/float_.py +++ b/lib/param/float_.py @@ -1,19 +1,24 @@ +"""Defines the float param type""" from param import Param - class Float(Param): - def __init__(self, value, classname=""): - self._classname = classname - super(Float, self).__init__(classname=classname) - try: - if value: - self.value = float(value) - else: - self.value = 0.0 - except Exception as e: - self.err_warn("Not a float: %s" % str(value)) - self.err_warn(str(e)) - def __int__(self): - return int(self.value) - - def __float__(self): - return float(self.value) + """Defines the float param type + Args: + value: the value of the Float + classname: the name of the class to which the param belongs + """ + def __init__(self, value, classname=""): + self._classname = classname + super(Float, self).__init__(classname=classname) + try: + if value: + self.value = float(value) + else: + self.value = 0.0 + except Exception as e: + self.err_warn("Not a float: %s" % str(value)) + self.err_warn(str(e)) + def __int__(self): + return int(self.value) + + def __float__(self): + return float(self.value) diff --git a/lib/param/json.py b/lib/param/json.py index af9b775..08db3e9 100644 --- a/lib/param/json.py +++ b/lib/param/json.py @@ -1,7 +1,15 @@ +"""Defines the json param type""" from param import Param import simplejson as json class Json(Param): - def __init__(self, value, classname=""): - super(Json, self).__init__(classname=classname) - self.value = json.loads(value) + """Defines the json param type. Loads in a + json value and parses it. + + Args: + value: a json string + classname: name of the class to which the param belongs + """ + def __init__(self, value, classname=""): + super(Json, self).__init__(classname=classname) + self.value = json.loads(value) diff --git a/lib/params.py b/lib/params.py index 78a65eb..cd8bc26 100644 --- a/lib/params.py +++ b/lib/params.py @@ -38,7 +38,7 @@ class Params(object): try: value = None if key in classkwargs: - value = classkwargs[key] or def_dict[key].get('default', None) + value = classkwargs.get(key, None) or def_dict[key].get('default', None) elif 'default' in def_dict[key]: value = def_dict[key]['default'] if def_dict[key]['type'] == "bool": diff --git a/lib/pb/__init__.py b/lib/pb/__init__.py index 41b9155..d87fb7e 100644 --- a/lib/pb/__init__.py +++ b/lib/pb/__init__.py @@ -1,200 +1,8 @@ -import re -from config import * -import time -import urllib, urllib2 -import sys, os -import random -from subprocess import Popen, PIPE, call -from params import Params -import sha -import simplejson as json -from s3cli import S3Cli -from db import Db -BASE_URL = "http://i.asdf.us" - -Request = urllib2.Request -urlencode = urllib.urlencode -urlopen = urllib2.urlopen - -_max_filename_length = 20 - -class PbProcessError(Exception): - pass - -class Pb(object): - def __init__(self, **kwargs): - self._input_kwargs = kwargs - self._now = str(int(time.time())) - self.params = Params(classname=self.__class__.__name__, now=self._now) - self._files_created = [] - self.commands = [] - self._working_dir = WORKING_DIR - self._tag = self.__class__.__name__ - self._hashdir = None - self._db_url_param = None - - def _filename_create(self, url=None, namepart="", extension=""): - if url: - _basename = os.path.basename(url) - namepart = re.split(r'\.', _basename)[0] - namepart = self._url_sanitize(namepart)[0:_max_filename_length] - name = "" - if namepart: name += "%s-" % namepart - name += "%s_%s" % (self.__class__.__name__, self._now) - if self.params.username: name += "_%s" % self.params.username - if extension: name += ".%s" % extension - return name - - def _filepath_create(self, filename, directory=WORKING_DIR): - return os.path.join(directory, filename) - - def _filename_filepath_create(self, url=None, namepart="", directory=WORKING_DIR, extension=""): - _filename = self._filename_create(url=url, namepart=namepart, extension=extension) - _filepath = self._filepath_create(_filename, directory=directory) - return _filename, _filepath - - def _tempfilepath_create(self, namepart="temp", directory=WORKING_DIR, extension=""): - _filename = self._filename_create(namepart=namepart, extension=extension) - return self._filepath_create(_filename, directory=directory) - - def _hashdir_create(self): - self._hashdir = sha.new(self.filename).hexdigest()[:2] - - def _url_sanitize(self, s): - return re.sub(r'\W+', '', s) - - def _call_cmd(self, cmd): - try: - cmd = map(lambda i: str(i), cmd) - call(cmd) - self.commands.append(" ".join(cmd)) - except Exception: - raise Exception("Unable to call cmd {}".format(str(cmd))) - - def _dimensions(self, filepath): - try: - ident = (Popen([BIN_IDENTIFY, filepath], stdout=PIPE).communicate()[0]).split(" ") - return ident[2].split("x") - except Exception as e: - self.err_warn("Unable to get file dimensions:\n") - self.err_ward(str(e)) - - def _file_dimensions(self): - self.file_width, self.file_height = self._dimensions(self.filepath) - - def _width_and_height_set(self, filepath=None, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT): - if filepath: - self.width, self.height = self._dimensions(filepath) - return - self.width = width - self.height = height - - def _file_size_get(self): - try: - self.file_size = os.stat(self.filepath)[6] - except Exception as e: - self.err_warn("Couldn't determine filesize of %s\n" % self.filepath) - self.err_warn(str(e)) - - def _file_read(self, filepath): - f = open(filepath, 'r') - data = f.read() - f.close() - return data - - def err_warn(self, s): - sys.stderr.write("ERROR:{} - {}\n".format(self.__class__.__name__, s)) - raise PbProcessError - - def _cleanup(self): - if not self._files_created: return - map(lambda n: os.remove(n), self._files_created) - - def _file_clean_local(self): - os.remove(self.filepath) - - def err_fatal(self, s): - sys.stderr.write("ERROR[FATAL]:%s - %s\n".format(self.__class__.__name__, s)) - sys.exit(1) - - @classmethod - def example_run(cls, params=None, verbose=True): - example_params = params or cls.example_params - if not example_params: - raise AttributeError("Must supply test params to test %s" % cls.__name__) - b = cls(**example_params) - b.create() - if verbose: - sys.stderr.write("generated %s\n" % b.filepath) - sys.stderr.write("files created %s\n" % b._files_created) - sys.stderr.write("commands:\n %s\n" % ";\n ".join(b.commands)) - return b - - @staticmethod - def gif_frames(filepath): - try: - info = Popen([BIN_IDENTIFY, filepath], stdout=PIPE).communicate()[0] - frames = filter((lambda x: x), map( - (lambda x: x.split(" ")[0]), - (info).split('\n') - )) - return frames - except Exception as e: - self.err_warn("couldn't get gif frames") - raise e - - def _choose_gif_frame(self, filepath): - _gif_frames = Pb.gif_frames(filepath) - frame = random.choice(_gif_frames) - self._call_cmd([BIN_CONVERT, frame, filepath]) - - def db_send(self, remote_addr=None, db_connection=None): - try: - db = db_connection or Db() - except Exception as e: - sys.stderr.write("Could not connect to db:\n{}".format(e)) - sys.exit(1) - try: - - _insert_data = { - 'date' : self._now, - 'remote_addr' : remote_addr, - 'username' : str(self.params.username), - 'url' : self._db_url_param, - 'directory' : self._hashdir, - 'oldfile' : None, - 'newfile' : self.filename, - 'dataobj' : json.dumps(dict(self._input_kwargs)), - 'cmd' : "; ".join(self.commands), - 'tag' : self._tag, - } - db.insert_cmd(**_insert_data) - except Exception as e: - self.err_warn("Problem sending to database:\n %s" % str(e)) - - def file_s3move(self): - self._hashdir_create() - s3cli = S3Cli() - s3cli.s3move(self.filepath, "im/{}/{}".format(self._hashdir, self.filename)) - self._file_clean_local() - - def file_dict(self): - return { - 'url' : "%s/im/%s/%s" % (BASE_URL, self._hashdir, self.filename), - 'size' : self.file_size, - 'width' : "%spx" % self.file_width, - 'height' : "%spx" % self.file_height, - } - - def create(self): - #base methods FIXME move into File class - self._file_dimensions() - self._file_size_get() - self._cleanup() - -from pb.grid import PbGrid -from pb.breaker import PbBreaker -from pb.pattern import PbPattern -from pb.generate import PbGenerate -from pb.landscape import PbLandscape -from pb.gradient import PbGradient +"""Convenience for relative imports""" +from .pb import Pb +from .grid import PbGrid +from .breaker import PbBreaker +from .pattern import PbPattern +from .generate import PbGenerate +from .landscape import PbLandscape +from .gradient import PbGradient diff --git a/lib/pb/generate.py b/lib/pb/generate.py index 12e3de1..5fc1402 100755 --- a/lib/pb/generate.py +++ b/lib/pb/generate.py @@ -1,23 +1,21 @@ #!/usr/bin/python2.7 -import sys -import os -from config import * +from config import BIN_CONVERT, OUTPUT_IMAGE_TYPES, DEFAULT_FINALFORMAT from pb import Pb -_default_tag = "im" +_DEFAULT_TAG = "im" -_gravity_params = [ +_GRAVITY_PARAMS = [ "NorthWest", "North", "NorthEast", "West", "Center", "East", "SouthWest", "South", "SouthEast" ] -_gravity_default = "Center" -_compose_params = [ +_GRAVITY_DEFAULT = "Center" +_COMPOSE_PARAMS = [ "Over", "ATop", "Dst_Over", "Dst_In", "Dst_Out", "Multiply", "Screen", "Divide", "Plus", "Difference", "Exclusion", "Pin_Light", "Lighten", "Darken", "Overlay", "Hard_Light", "Soft_Light", "Linear_Dodge", "Linear_Burn", "Color_Dodge", "Color_Burn" ] -_dispose_params = ["None", "Previous", "Background"] -_dispose_default = "None" +_DISPOSE_PARAMS = ["None", "Previous", "Background"] +_DISPOSE_DEFAULT = "None" class PbGenerate(Pb): #{{{ example params example_params = { @@ -49,59 +47,68 @@ class PbGenerate(Pb): #}}} def __init__(self, **kwargs): super(PbGenerate, self).__init__(**kwargs) + """ + Used to assert the value-types of the incoming parameters. + Types are defined as in their individual params classes. + """ _definitions = { - """ - Used to assert the value-types of the incoming parameters. - Types are defined as in their individual params classes. - """ #IMAGES - "url": {'type': "img_url"}, - "background": {'type': "img_url"}, + 'url': {'type': 'img_url'}, + 'background': {'type': 'img_url'}, #BOOLS - "coalesce": {'type': "bool"}, - "nearest": {'type': "bool"}, - "merge_early": {'type': "bool"}, - "flip": {'type': "bool"}, - "flop": {'type': "bool"}, - "tile": {'type': "bool"}, - "transparent": {'type': "bool"}, + 'coalesce': {'type': 'bool'}, + 'nearest': {'type': 'bool'}, + 'merge_early': {'type': 'bool'}, + 'flip': {'type': 'bool'}, + 'flop': {'type': 'bool'}, + 'tile': {'type': 'bool'}, + 'transparent': {'type': 'bool'}, #COLORS - "black": {'type': "color", 'default': 'black'}, - "white": {'type': "color", 'default': 'white'}, - "subtract": {'type': "color"}, + 'black': {'type': 'color', 'default': 'black'}, + 'white': {'type': 'color', 'default': 'white'}, + 'subtract': {'type': 'color'}, #INTS - "fuzz": {'type': "int"}, - "width": {'type': "int"}, - "height": {'type': "int"}, - "brightness": {'type': "int"}, - "contrast": {'type': "int"}, - "saturation": {'type': "int"}, - "rotate": {'type': "int"}, - "hue": {'type': "int"}, + 'fuzz': {'type': 'int'}, + 'width': {'type': 'int'}, + 'height': {'type': 'int'}, + 'brightness': {'type': 'int'}, + 'contrast': {'type': 'int'}, + 'saturation': {'type': 'int'}, + 'rotate': {'type': 'int'}, + 'hue': {'type': 'int'}, #ENUMS - "compose": {'type': "enum", 'enum_values': _compose_params, 'default': "Atop"}, - "gravity": {'type': "enum", 'enum_values': _gravity_params, 'default': _gravity_default}, - "dispose": {'type': "enum", 'enum_values': _dispose_params, 'default': "None"}, - "format": {'type': "enum", 'enum_values': OUTPUT_IMAGE_TYPES, 'default': DEFAULT_FINALFORMAT}, + 'compose': {'type': 'enum', 'enum_values': _COMPOSE_PARAMS, 'default': 'Atop'}, + 'gravity': { + 'type': 'enum', 'enum_values': _GRAVITY_PARAMS, 'default': _GRAVITY_DEFAULT + }, + 'dispose': { + 'type': 'enum', 'enum_values': _DISPOSE_PARAMS, 'default': 'None' + }, + 'format': { + 'type': 'enum', 'enum_values': OUTPUT_IMAGE_TYPES, 'default': DEFAULT_FINALFORMAT + }, #STRINGS "username": {'type': "string"}, "callback": {'type': "string"}, } - """Definitions and arguments are merged into attributes of the params object""" - self.params.definitions_import(_definitions, kwargs, classname=self.__class__.__name__) + self.params.definitions_import( + _definitions, kwargs, classname=self.__class__.__name__ + ) - """Used for the database tag column. Allows for tracking of the type + """Used for the database tag column. Allows for tracking of the type of overlay method used.""" - self.tag = _default_tag - if self.params.background: self.tag = self.params.compose - if self.params.transparent: self.tag = self.params.transparent + self.tag = _DEFAULT_TAG + if self.params.background: + self.tag = self.params.compose + if self.params.transparent: + self.tag = self.params.transparent self.filename, self.filepath = self._filename_filepath_create( url=self.params.url['url'], extension=self.params.format @@ -109,7 +116,7 @@ class PbGenerate(Pb): self._db_url_param = str(self.params.url['url']) - def _composite (self): + def _composite(self): """Imagemagick composite command""" cmd = [ BIN_CONVERT, self.params.background['path'], @@ -124,22 +131,26 @@ class PbGenerate(Pb): def _convert(self): """Imagemagick convert command""" cmd = [BIN_CONVERT, self.params.url['path']] - if self.params.rotate: cmd += ["-rotate", self.params.rotate] - if self.params.flip: cmd += ["-flip"] - if self.params.flop: cmd += ["-flop"] + if self.params.rotate: + cmd += ["-rotate", self.params.rotate] + if self.params.flip: + cmd += ["-flip"] + if self.params.flop: + cmd += ["-flop"] if self.params.transparent: if self.params.fuzz: cmd += ["-fuzz", "{}%".format(self.params.fuzz)] cmd += ["-transparent", self.params.subtract] if self.params.width or self.params.height: if self.params.nearest and self.params.format == "gif": - cmd += ["-coalesce","+map","-interpolate","Nearest","-interpolative-resize"] + cmd += ["-coalesce", "+map", "-interpolate", "Nearest", "-interpolative-resize"] else: cmd.append("-resize") cmd += ["{}x{}".format(self.params.width or "", self.params.height or "")] if self.params.black != "black" or self.params.white != 'white': - cmd += ["+level-colors" , "{},{}".format(self.params.black, self.params.white)] - if self.params.contrast: cmd += ['-contrast-stretch', self.params.contrast] + cmd += ["+level-colors", "{},{}".format(self.params.black, self.params.white)] + if self.params.contrast: + cmd += ['-contrast-stretch', self.params.contrast] if self.params.brightness or self.params.saturation or self.params.hue: cmd += [ "-modulate", "{},{},{}".format( @@ -148,7 +159,7 @@ class PbGenerate(Pb): self.params.hue or 100 ) ] - cmd.append("-coalesce"); #why? #FIXME + cmd.append("-coalesce")#why? #FIXME cmd += [self.filepath] self._call_cmd(cmd) diff --git a/lib/pb/gradient.py b/lib/pb/gradient.py index 02d57d3..c2a9066 100755 --- a/lib/pb/gradient.py +++ b/lib/pb/gradient.py @@ -1,11 +1,13 @@ -#!/usr/bin/python2.7
-from config import *
+"""Creates a gradient image and adds effects to it"""
+
+from config import DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FINALFORMAT, \
+ BIN_CONVERT, BEVELBORDER, OUTPUT_IMAGE_TYPES
from pb import Pb
-_default_color_1 = "white"
-_default_color_2 = "black"
+_DEFAULT_COLOR_1 = "white"
+_DEFAULT_COLOR_2 = "black"
-_default_bevel_percent = "12"
+_DEFAULT_BEVEL_PERCENT = "12"
_halftone_values = {
"checkeredfade": "h6x6a",
@@ -39,8 +41,8 @@ class PbGradient(Pb): _definitions = {
'width': {'type':'int', 'default': DEFAULT_WIDTH},
'height': {'type':'int', 'default' : DEFAULT_HEIGHT},
- 'color1': {'type':'color', 'default': _default_color_1},
- 'color2': {'type':'color', 'default': _default_color_2},
+ 'color1': {'type':'color', 'default': _DEFAULT_COLOR_1},
+ 'color2': {'type':'color', 'default': _DEFAULT_COLOR_2},
'stripes': {'type':'bool'},
'stripenumber': {'type':'int', 'default': 0},
'stripeintensity': {'type':'int', 'default': 0},
@@ -57,14 +59,14 @@ class PbGradient(Pb): 'flatout', 'flatinner', 'evenlyframed', 'biginner',
'bigouter', 'dramaticflatout', 'dramaticflatinner',
]},
- 'percentbeveled': {'type':'int', 'default': _default_bevel_percent},
+ 'percentbeveled': {'type':'int', 'default': _DEFAULT_BEVEL_PERCENT},
'tilt': {'type':'int'},
'rotate': {'type':'int'},
'flip': {'type':'bool'},
'flop': {'type':'bool'},
'filetype': {
- 'type':'enum',
- 'enum_values':OUTPUT_IMAGE_TYPES,
+ 'type': 'enum',
+ 'enum_values': OUTPUT_IMAGE_TYPES,
'default': DEFAULT_FINALFORMAT
},
'gradienttype': {'type':'enum', 'enum_values':[
@@ -95,11 +97,16 @@ class PbGradient(Pb): "{}x{}".format(self.params.width, self.params.height)
])
- if self.params.rotate: cmd.extend(["-rotate", self.params.rotate])
- if self.params.tilt: cmd.extend(["-distort", "SRT", self.params.tilt])
- if self.params.flip == "true": cmd.append("-flip")
- if self.params.flop == "true": cmd.append("-flop")
- if self.params.contrast: cmd.extend(["-contrast-stretch", self.params.contrast])
+ if self.params.rotate:
+ cmd.extend(["-rotate", self.params.rotate])
+ if self.params.tilt:
+ cmd.extend(["-distort", "SRT", self.params.tilt])
+ if self.params.flip == "true":
+ cmd.append("-flip")
+ if self.params.flop == "true":
+ cmd.append("-flop")
+ if self.params.contrast:
+ cmd.extend(["-contrast-stretch", self.params.contrast])
_gradients = {
"gradient" : ["gradient:{}-{}".format(self.params.color1, self.params.color2)],
"canvas" : ["canvas:{}".format(self.params.color1)],
@@ -113,18 +120,18 @@ class PbGradient(Pb): ],
"mirrored" : [
"plasma:{}-{}".format(self.params.color1, self.params.color2),
- "\(", "+clone", "-flop", "\)",
- "append"
+ "\(", "+clone", "-flop", "\)",
+ "append"
],
"plasmawash" : [
"plasma:{}-{}".format(self.params.color1, self.params.color2),
- "-set","colorspace","HSB"
+ "-set", "colorspace", "HSB"
],
"gradientwash" : [
"gradient:{}-{}".format(self.params.color1, self.params.color2),
"-set", "colorspace", "HSB"
],
- "noise" : ["xc:","+noise","Random","-virtual-pixel","tile"]
+ "noise" : ["xc:", "+noise", "Random", "-virtual-pixel", "tile"]
}
cmd += _gradients[str(self.params.gradienttype)]
@@ -163,7 +170,7 @@ class PbGradient(Pb): return {
"flatout": ["-s", bevpercentval, "-m", "outer"],
"flatinner": ["-s", bevpercentval, "-m", "inner"],
- "evenlyframed": ["-s ", bevpercentval, "-m", "split"],
+ "evenlyframed": ["-s ", bevpercentval, "-m", "split"],
"biginner": ["-s", bevpercentval, "-m", "outer", "-c", "50", "-b", "red", "-a", "25"],
"bigouter": ["-s", bevpercentval, "-m", "split", "-c", "50", "-b", "red", "-a", "25"],
"dramaticflatout": ["-s", bevpercentval, "-m", "outer", "-a", "25", "-b", "blue"],
diff --git a/lib/pb/grid.py b/lib/pb/grid.py index 0fafd1c..d8b1aaa 100755 --- a/lib/pb/grid.py +++ b/lib/pb/grid.py @@ -1,36 +1,32 @@ -import sys -import re -import os -import simplejson as json -import random from config import DEFAULT_FINALFORMAT, DEFAULT_HEIGHT,\ - DEFAULT_WIDTH, WORKING_DIR, OUTPUT_IMAGE_TYPES + DEFAULT_WIDTH, OUTPUT_IMAGE_TYPES from config import THREEDROTATE, GRID, BIN_CONVERT, BIN_COMPOSITE -import tempfile from pb import Pb -_default_line_color = "silver" +_DEFAULT_LINE_COLOR = "silver" class PbGrid(Pb): + """ + Creates or overlays a grid on an image, and adds 3D perspective + """ example_params = { - 'bgimage' : 'http://i.asdf.us/im/1a/imBreak_1424909483_xx_abridged___.gif', - 'planebgimage' : 'http://i.imgur.com/FICZtph.png', - 'tilt' : '30', - 'spacing' : '30', - 'hlines' : 'true', - 'roll' : '30', - 'shadow' : 'true', - 'trim' : 'true' + 'bgimage': 'http://i.asdf.us/im/1a/imBreak_1424909483_xx_abridged___.gif', + 'planebgimage': 'http://i.imgur.com/FICZtph.png', + 'tilt': '30', + 'spacing': '30', + 'hlines': 'true', + 'roll': '30', + 'shadow': 'true', + 'trim': 'true' } - def __init__(self, **kwargs ): - super(PbGrid,self).__init__(**kwargs) + def __init__(self, **kwargs): + super(PbGrid, self).__init__(**kwargs) _definitions = { 'width': {'type':'int'}, 'height': {'type':'int'}, 'linethickness': {'type':'int', 'default': 1}, 'opacity': {'type':'float', "default": 1.0}, 'linecolor': {'type':'color', 'default': 'whitesmoke'}, - 'linethickness': {'type':'int'}, 'spacing': {'type':'int', 'default': 10}, 'vlines': {'type':'bool'}, 'hlines': {'type':'bool'}, @@ -72,13 +68,15 @@ class PbGrid(Pb): url=self.params.planebgimage['url'], extension=self.params.finalformat ) else: - self.filename, self.filepath = self._filename_filepath_create( - extension=self.params.finalformat - ) + self.filename, self.filepath = self._filename_filepath_create( + extension=self.params.finalformat + ) self._db_url_param = str( filter( - lambda n: n, [self.params.imageinstead, self.params.planebgimage, self.params.bgimage, "NULL"] + lambda n: n, [ + self.params.imageinstead, self.params.planebgimage, self.params.bgimage, "NULL" + ] )[0] ) @@ -106,25 +104,31 @@ class PbGrid(Pb): cmd += ["-s", "{},{}".format(height, self.params.spacing)] else: cmd += ["-s", self.params.spacing] - cmd += [ "-c", self.params.linecolor or _default_line_color] - if self.params.linethickness: cmd += ['-t', self.params.linethickness] - if self.params.opacity: cmd += ['-o', self.params.opacity] - cmd += [ self.filepath, self.filepath ] + cmd += ["-c", self.params.linecolor or _DEFAULT_LINE_COLOR] + if self.params.linethickness: + cmd += ['-t', self.params.linethickness] + if self.params.opacity: + cmd += ['-o', self.params.opacity] + cmd += [self.filepath, self.filepath] self._call_cmd(cmd) def _shadow_cmd(self): - #convert 1.png \( +clone -background black -shadow 110x1+9+9 \) +swap -background none -layers merge +repage 2.png + """ + convert 1.png \ + \( +clone -background black -shadow 110x1+9+9 \) \ + +swap -background none -layers merge +repage 2.png + """ cmd = [ BIN_CONVERT, self.filepath, - "(","+clone","-background","black","-shadow","100x2+20+10",")", - "+swap","-background","none","-layers","merge","+repage" , + "(", "+clone", "-background", "black", "-shadow", "100x2+20+10", ")", + "+swap", "-background", "none", "-layers", "merge", "+repage", self.filepath ] self._call_cmd(cmd) - def _threed_rotate_cmd (self): + def _threed_rotate_cmd(self): #3rd step--run 3Drotate cmd = [THREEDROTATE] if self.params.swing: cmd += ["pan={}".format(self.params.swing)] @@ -140,11 +144,11 @@ class PbGrid(Pb): cmd += ["bgcolor=none"] cmd += ["skycolor={}".format(self.params.skycolor or 'none')] if self.params.transition: cmd += ["vp={}".format(self.params.transition)] - cmd += [ self.filepath, self.filepath ] + cmd += [self.filepath, self.filepath] self._call_cmd(cmd) - def _trim_cmd (self): + def _trim_cmd(self): cmd = [BIN_CONVERT, self.filepath, "-trim", "+repage", self.filepath] self._call_cmd(cmd) diff --git a/lib/pb/pattern.py b/lib/pb/pattern.py index a9ab879..a17142c 100755 --- a/lib/pb/pattern.py +++ b/lib/pb/pattern.py @@ -1,41 +1,28 @@ -import os -import sys -import random -import re -import urllib -from config import * +from config import BIN_CONVERT, BIN_COMPOSITE from pb import Pb - -import simplejson as json from PIL import Image -import uuid -_fuse_mode="Pin_Light" +_FUSE_MODE="Pin_Light" class PbPattern(Pb): example_params = { - # "pattern_url" : "http://asdf.us/impattern/patterns/1.png", "pattern_data" : '{"matrix":[["0","0","0","0","0","1","0","0","0","0"],["0","0","0","0","1","1","1","0","0","0"],["0","0","1","1","1","0","1","0","0","0"],["0","1","1","0","0","0","0","0","0","0"],["0","1","0","0","1","0","0","0","0","0"],["0","1","0","0","1","0","0","0","1","0"],["0","1","0","0","1","1","0","0","1","0"],["0","1","0","0","0","1","1","1","1","0"],["0","1","1","1","1","0","0","0","0","0"],["0","0","0","0","1","0","0","0","0","0"]],"width":"10","height":"10"}', - # "username" : "garfield", "image_url" : "http://i.asdf.us/im/be/PinkHijab_1425078647_reye.gif", +# "username" : "garfield", +# "pattern_url" : "http://asdf.us/impattern/patterns/1.png", } - example_params = { - "username":"pepper", - "pattern_data":"", - "pattern_url":"http://asdf.us/impattern/patterns/63.png", - "image_url":"http://i.asdf.us/im/b4/PbGradientwhiteblack-PbGrid_1443161086_pepper.png", - - } def __init__(self, **kwargs): - super(PbPattern,self).__init__(**kwargs); + super(PbPattern, self).__init__(**kwargs) _definitions = { - 'image_url': { 'type':'img_url' }, - 'pattern_url': { 'type':'img_url' }, - 'pattern_data': { 'type':'json' }, - 'username': { 'type':'string' }, + 'image_url': {'type':'img_url'}, + 'pattern_url': {'type':'img_url'}, + 'pattern_data': {'type':'json'}, + 'username': {'type':'string'}, } - self.params.definitions_import(_definitions, kwargs, classname=self.__class__.__name__); - self.filename, self.filepath = self._filename_filepath_create(url=self.params.image_url['url'], extension=self.params.image_url['mimetype']) + self.params.definitions_import(_definitions, kwargs, classname=self.__class__.__name__) + self.filename, self.filepath = self._filename_filepath_create( + url=self.params.image_url['url'], extension=self.params.image_url['mimetype'] + ) if self.params.pattern_data: _pattern_filename, self._pattern_filepath = self._filename_filepath_create(namepart="pattern") self._from_pattern_data() @@ -50,18 +37,17 @@ class PbPattern(Pb): def _from_pattern_data(self): def boolToColor(boolean): if boolean: - return (0,0,0,255); + return (0, 0, 0, 255) else: - return (255,255,255,255) - specs = self.params.pattern_data.value; + return (255, 255, 255, 255) + specs = self.params.pattern_data.value if int(specs['width']) > 100 or int(specs['height']) > 100: - raise ValueError - sys.stderr.write("height and width need to be less than 100 px") - img = Image.new('RGBA', (int(specs['width']), int(specs['height']))); - pixels = img.load(); + self.err_warn("height and width need to be less than 100 px") + img = Image.new('RGBA', (int(specs['width']), int(specs['height']))) + pixels = img.load() for i in range(0, len(specs['matrix'])): for j in range(0, len(specs['matrix'][i])): - pixels[j,i] = boolToColor(int(specs['matrix'][i][j])); + pixels[j, i] = boolToColor(int(specs['matrix'][i][j])) img.save(self._pattern_filepath, "PNG") @@ -74,23 +60,27 @@ class PbPattern(Pb): #second step use the Canvas as a background def _make_mask(self): #tile the pattern pattern on the canvas - cmd = [BIN_COMPOSITE,"-tile", self._pattern_filepath, self.filepath, self.filepath]; + cmd = [BIN_COMPOSITE, "-tile", self._pattern_filepath, self.filepath, self.filepath] self._call_cmd(cmd) #fuse the tiled file to create a mask #convert thebg.gif -compose Dst_In null: thefile.gif -matte -layers composite new.gif - cmd = [BIN_CONVERT, self.filepath, "-compose", "Dst_In", "null:", - self.params.image_url['path'], "-matte", "-layers", "composite", self.filepath] + cmd = [ + BIN_CONVERT, self.filepath, "-compose", "Dst_In", "null:", + self.params.image_url['path'], "-matte", "-layers", "composite", self.filepath + ] self._call_cmd(cmd) #third step - def _fuse_mask(self, fuse_mode=_fuse_mode): - cmd = [BIN_CONVERT, "-dispose", "2", self.filepath, "null:", + def _fuse_mask(self, fuse_mode=_FUSE_MODE): + cmd = [ + BIN_CONVERT, "-dispose", "2", self.filepath, "null:", self.params.image_url['path'], "-matte", "-compose", fuse_mode, "-layers", "composite", - self.filepath] + self.filepath + ] self._call_cmd(cmd) def create(self): - self._make_canvas(); + self._make_canvas() self._make_mask() - self._fuse_mask(); + self._fuse_mask() super(PbPattern, self).create() diff --git a/lib/pb/pb.py b/lib/pb/pb.py new file mode 100644 index 0000000..109bfb0 --- /dev/null +++ b/lib/pb/pb.py @@ -0,0 +1,202 @@ +"""contains only the Pb class which is used by the Pb.* modules for inheritance""" + +import re +from config import WORKING_DIR, BIN_CONVERT, BIN_IDENTIFY,\ + DEFAULT_HEIGHT, DEFAULT_WIDTH +import time +import urllib, urllib2 +import sys, os +import random +from subprocess import Popen, PIPE, call +from params import Params +import sha +import simplejson as json +from s3cli import S3Cli +from db import Db +BASE_URL = "http://i.asdf.us" + +_MAX_FILENAME_LENGTH = 20 + +class PbProcessError(Exception): + pass + +class Pb(object): + """Default Pb class. USED ONLY FOR INHERITANCE""" + def __init__(self, **kwargs): + self._input_kwargs = kwargs + self._now = str(int(time.time())) + self.params = Params(classname=self.__class__.__name__, now=self._now) + self._files_created = [] + self.commands = [] + self._working_dir = WORKING_DIR + self._tag = self.__class__.__name__ + self._hashdir = None + self._db_url_param = None + + #FIXME move to separate class + self.file_size = None + self.width = None + self.height = None + self.filename = None + self.filepath = None + self.file_height = None + self.file_width = None + + def _filename_create(self, url=None, namepart="", extension=""): + if url: + _basename = os.path.basename(url) + namepart = re.split(r'\.', _basename)[0] + namepart = self._url_sanitize(namepart)[0:_MAX_FILENAME_LENGTH] + name = "" + if namepart: name += "%s-" % namepart + name += "%s_%s" % (self.__class__.__name__, self._now) + if self.params.username: name += "_%s" % self.params.username + if extension: name += ".%s" % extension + return name + + def _filepath_create(self, filename, directory=WORKING_DIR): + return os.path.join(directory, filename) + + def _filename_filepath_create(self, url=None, namepart="", directory=WORKING_DIR, extension=""): + _filename = self._filename_create(url=url, namepart=namepart, extension=extension) + _filepath = self._filepath_create(_filename, directory=directory) + return _filename, _filepath + + def _tempfilepath_create(self, namepart="temp", directory=WORKING_DIR, extension=""): + _filename = self._filename_create(namepart=namepart, extension=extension) + return self._filepath_create(_filename, directory=directory) + + def _hashdir_create(self): + self._hashdir = sha.new(self.filename).hexdigest()[:2] + + def _url_sanitize(self, s): + return re.sub(r'\W+', '', s) + + def _call_cmd(self, cmd): + try: + cmd = map(lambda i: str(i), cmd) + call(cmd) + self.commands.append(" ".join(cmd)) + except Exception: + raise Exception("Unable to call cmd {}".format(str(cmd))) + + def _dimensions(self, filepath): + try: + ident = (Popen([BIN_IDENTIFY, filepath], stdout=PIPE).communicate()[0]).split(" ") + return ident[2].split("x") + except Exception as e: + self.err_warn("Unable to get file dimensions:\n") + self.err_ward(str(e)) + + def _file_dimensions(self): + self.file_width, self.file_height = self._dimensions(self.filepath) + + def _width_and_height_set(self, filepath=None, width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT): + if filepath: + self.width, self.height = self._dimensions(filepath) + return + self.width = width + self.height = height + + def _file_size_get(self): + try: + self.file_size = os.stat(self.filepath)[6] + except Exception as e: + self.err_warn("Couldn't determine filesize of %s\n" % self.filepath) + self.err_warn(str(e)) + + def _file_read(self, filepath): + f = open(filepath, 'r') + data = f.read() + f.close() + return data + + def err_warn(self, s): + sys.stderr.write("ERROR:{} - {}\n".format(self.__class__.__name__, s)) + raise PbProcessError + + def _cleanup(self): + if not self._files_created: return + map(lambda n: os.remove(n), self._files_created) + + def _file_clean_local(self): + os.remove(self.filepath) + + def err_fatal(self, s): + sys.stderr.write("ERROR[FATAL]:%s - %s\n" % (self.__class__.__name__, s)) + sys.exit(1) + + @classmethod + def example_run(cls, params=None, verbose=True): + example_params = params or cls.example_params + if not example_params: + raise AttributeError("Must supply test params to test %s" % cls.__name__) + b = cls(**example_params) + b.create() + if verbose: + sys.stderr.write("generated %s\n" % b.filepath) + sys.stderr.write("files created %s\n" % b._files_created) + sys.stderr.write("commands:\n %s\n" % ";\n ".join(b.commands)) + return b + + @staticmethod + def gif_frames(filepath): + try: + info = Popen([BIN_IDENTIFY, filepath], stdout=PIPE).communicate()[0] + frames = filter((lambda x: x), map( + (lambda x: x.split(" ")[0]), + (info).split('\n') + )) + return frames + except Exception as e: + self.err_warn("couldn't get gif frames") + raise e + + def _choose_gif_frame(self, filepath): + _gif_frames = Pb.gif_frames(filepath) + frame = random.choice(_gif_frames) + self._call_cmd([BIN_CONVERT, frame, filepath]) + + def db_send(self, remote_addr=None, db_connection=None): + try: + db = db_connection or Db() + except Exception as e: + sys.stderr.write("Could not connect to db:\n{}".format(e)) + sys.exit(1) + try: + + _insert_data = { + 'date' : self._now, + 'remote_addr' : remote_addr, + 'username' : str(self.params.username), + 'url' : self._db_url_param, + 'directory' : self._hashdir, + 'oldfile' : None, + 'newfile' : self.filename, + 'dataobj' : json.dumps(dict(self._input_kwargs)), + 'cmd' : "; ".join(self.commands), + 'tag' : self._tag, + } + db.insert_cmd(**_insert_data) + except Exception as e: + self.err_warn("Problem sending to database:\n %s" % str(e)) + + def file_s3move(self): + self._hashdir_create() + s3cli = S3Cli() + s3cli.s3move(self.filepath, "im/{}/{}".format(self._hashdir, self.filename)) + self._file_clean_local() + + def file_dict(self): + return { + 'url' : "%s/im/%s/%s" % (BASE_URL, self._hashdir, self.filename), + 'size' : self.file_size, + 'width' : "%spx" % self.file_width, + 'height' : "%spx" % self.file_height, + } + + def create(self): + #base methods FIXME move into File class + self._file_dimensions() + self._file_size_get() + self._cleanup() diff --git a/lib/s3cli.py b/lib/s3cli.py index 70ba24c..6620bf1 100644 --- a/lib/s3cli.py +++ b/lib/s3cli.py @@ -1,24 +1,25 @@ -from config import * +"""All s3 related methods stored here""" +from config import AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, BUCKET_NAME import sys from boto.s3.connection import S3Connection from boto.s3.key import Key class S3Cli(object): - def __init__(self): - try: - self.conn = S3Connection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, is_secure=False) - self.bucket = self.conn.get_bucket(BUCKET_NAME) - except Exception as e: - sys.stderr.write("Could not connect to s3\n"); - sys.stderr.write(str(e)) - sys.exit(1) + def __init__(self): + try: + self.conn = S3Connection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, is_secure=False) + self.bucket = self.conn.get_bucket(BUCKET_NAME) + except Exception as e: + sys.stderr.write("Could not connect to s3\n") + sys.stderr.write(str(e)) + sys.exit(1) - def s3move(self, filename,objectname): - try: - k = Key(self.bucket) - k.key = objectname - k.set_contents_from_filename(filename) - k.set_acl('public-read') - k.storage_class = 'REDUCED_REDUNDANCY' - except Exception as e: - sys.stderr.write(str(e)); - sys.exit(1) + def s3move(self, filename, objectname): + try: + k = Key(self.bucket) + k.key = objectname + k.set_contents_from_filename(filename) + k.set_acl('public-read') + k.storage_class = 'REDUCED_REDUNDANCY' + except Exception as e: + sys.stderr.write(str(e)) + sys.exit(1) |
