HCE Project Python language Distributed Tasks Manager Application, Distributed Crawler Application and client API bindings.  2.0.0-chaika
Hierarchical Cluster Engine Python language binding
MediaLimitsHandler.py
Go to the documentation of this file.
1 """
2 HCE project, Python bindings, Distributed Tasks Manager application.
3 MediaLimitsHandler Class content main functional for check media limits
4 
5 @package: dc_processor
6 @file MediaLimitsHandler.py
7 @author Alexander Vybornyh <alexander.hce.cluster@gmail.com>
8 @link: http://hierarchical-cluster-engine.com/
9 @copyright: Copyright &copy; 2013-2016 IOIX Ukraine
10 @license: http://hierarchical-cluster-engine.com/license/
11 @since: 0.1
12 """
13 
14 import os
15 import json
16 import base64
17 import tempfile
18 import psutil
19 import requests
20 from PIL import Image
21 
22 import app.Utils as Utils # pylint: disable=F0401
23 
24 logger = Utils.MPLogger().getLogger()
25 
26 class MediaLimitsHandler(object):
27  # #Constants used in class
28  TEMP_FILE_SUFFIX = '.jpg'
29  BINARY_IMAGE_SEARCH_STR = 'base64,'
30  # types name of limits
31  MEDIA_LIMITS_TYPE_NAME = 'img'
32 
33 
34  # # Internal class for image limits properties
35  class ImageLimits(object):
36  # #Constants used in class
37  CONTENT_TYPE_NAME = 'Content-Type'
38  MIN_WIDTH_NAME = 'min_width'
39  MAX_WIDTH_NAME = 'max_width'
40  MIN_HEIGHT_NAME = 'min_height'
41  MAX_HEIGHT_NAME = 'max_height'
42  MIN_RATIO = 'min_ratio'
43  MAX_RATIO = 'max_ratio'
44  MIN_COLORS = 'min_colors'
45  # dictionary with property names
46  propertyNamesDict = {
47  'contentType':CONTENT_TYPE_NAME,
48  'minWidth':MIN_WIDTH_NAME,
49  'maxWidth':MAX_WIDTH_NAME,
50  'minHeight':MIN_HEIGHT_NAME,
51  'maxHeight':MAX_HEIGHT_NAME,
52  'minRatio':MIN_RATIO,
53  'maxRatio':MAX_RATIO,
54  'minColors':MIN_COLORS}
55 
56  def __init__(self):
57  self.contentType = None
58  self.minWidth = None
59  self.maxWidth = None
60  self.minHeight = None
61  self.maxHeight = None
62  self.minRatio = None
63  self.maxRatio = None
64  self.minColors = None
65 
66 
67  # # Internal class for image properties
68  class ImageProperty(object):
69  def __init__(self):
70  self.width = None
71  self.height = None
72  self.ratio = None
73  self.colors = None
74  self.format = None
75 
76  # Constructor
77  # @param propertyString - contains string with json format
78  def __init__(self, propertyString):
79  # initialization variable content of limits structure
80  self.mediaLimits = self.__getMediaLimits(self.__loadProperty(propertyString))
81 
82 
83  # # Get media limits object
84  #
85  # @param propertyDict - property dict
86  # @return instance of necessary type (ImageLimits...)
87  def __getMediaLimits(self, propertyDict):
88  # variable for result
89  ret = None
90  try:
91  if not isinstance(propertyDict, dict):
92  raise Exception("Wrong type of property: %s" % str(type(property)))
93 
94  for key, value in propertyDict.items():
95  logger.debug("Enter to read property '%s'", str(key))
96  if self.MEDIA_LIMITS_TYPE_NAME == key:
97  if isinstance(value, dict):
98  ret = self.__getImageLimits(value)
99  else:
100  logger.debug("Wrong type of limits: '%s'. Skipped", str(type(value)))
101  else:
102  logger.error("Unsupported name of limits: '%s'. Skipped", str(key))
103 
104  logger.debug("Leave to read property '%s'", str(key))
105 
106  except Exception, err:
107  logger.error("Error: %s", str(err))
108 
109  return ret
110 
111 
112  # # load property from input json
113  #
114  # @param propertyString - contains string with json format
115  # @return object properties
116  def __loadProperty(self, propertyString):
117  # variable for result
118  ret = None
119  try:
120  if propertyString is None or not isinstance(propertyString, basestring):
121  raise Exception('Wrong type %s of property' % str(type(propertyString)))
122 
123  ret = json.loads(propertyString)
124  except Exception, err:
125  logger.error("Initialisation class '%s' was failed. Error: '%s'", self.__class__.__name__, str(err))
126 
127  return ret
128 
129 
130  # # Get image limits property
131  #
132  # @param limitsDict - limits dict
133  # @return ImageLimits instance if success or othewise None
134  def __getImageLimits(self, limitsDict):
135  # variable for result
136  imageLimits = MediaLimitsHandler.ImageLimits()
137 
138  for key, value in MediaLimitsHandler.ImageLimits.propertyNamesDict.items():
139  if value in limitsDict.keys() and hasattr(imageLimits, key):
140  setattr(imageLimits, key, limitsDict[value])
141 
142  return imageLimits
143 
144 
145  # # Get image and save to file
146  #
147  # @param content - binary content of image
148  # @return temporary file name
149  def __saveImageToFile(self, content):
150  # variable for result
151  ret = None
152  try:
153  if content is None:
154  raise Exception("Image content wasn't get for save on disk.")
155 
156  # save to file
157  tmpf = tempfile.NamedTemporaryFile(suffix=self.TEMP_FILE_SUFFIX, delete=False)
158  tmpf.file.write(content)
159  tmpf.close()
160  logger.debug('Image saved to file: ' + tmpf.name)
161  # save name
162  ret = tmpf.name
163  except Exception, err:
164  logger.debug("Error: %s", str(err))
165 
166  return ret
167 
168 
169  # #Load image
170  #
171  # @param urlString - url string of image
172  # @param binaryType - boolean flag is binary image
173  # @return binary content of image
174  def __loadImage(self, urlString, binaryType=False):
175  # variable for result
176  ret = None
177  try:
178  if binaryType:
179  # extract binary image
180  pos = urlString.find(self.BINARY_IMAGE_SEARCH_STR)
181  if pos > -1:
182  s = urlString[pos + len(self.BINARY_IMAGE_SEARCH_STR):]
183  ret = base64.b64decode(s)
184  else:
185  # send get request
186  res = requests.get(urlString)
187  ret = res.content
188  except Exception, err:
189  logger.debug("Error: %s", str(err))
190 
191  return ret
192 
193 
194  # # Get image property
195  #
196  # @param urlString - url string of image
197  # @param binaryType - boolean flag is binary image
198  # @return ImageProperty instance if success or othewise None
199  def __getImageProperty(self, urlString, binaryType=False):
200  # variable for result
201  ret = None
202  logger.debug("Url string: %s", str(urlString))
203  # load image and save to file
204  imageFileName = self.__saveImageToFile(self.__loadImage(urlString, binaryType))
205  if imageFileName is not None:
206  try:
207  # open file for extract data
208  imageFile = Image.open(imageFileName)
209  if imageFile is None:
210  raise Exception("Cannot open image file: '%s'", str(imageFileName))
211 
212  # create instance
213  imageProperty = MediaLimitsHandler.ImageProperty()
214  # set format of image
215  imageProperty.format = imageFile.format
216  # set width
217  if len(imageFile.size) > 0:
218  imageProperty.width = imageFile.size[0]
219  # set height
220  if len(imageFile.size) > 1:
221  imageProperty.height = imageFile.size[1]
222 
223  if imageProperty.width > 0 and imageProperty.height > 0:
224  # set ratio
225  imageProperty.ratio = float(imageProperty.height) / imageProperty.width
226  # set colors count
227  size = imageProperty.width * imageProperty.height
228  vm = psutil.virtual_memory()
229  logger.debug("Image size = %s, available memory = %s", str(size), str(vm.available))
230  if vm.available < size:
231  logger.debug("!!! Not enough memory for get colors... Skipped")
232  else:
233  colors = imageFile.getcolors(size)
234  if colors is not None:
235  imageProperty.colors = len(colors)
236 
237  ret = imageProperty
238  except Exception, err:
239  logger.debug("Error: %s", str(err))
240  finally:
241  if os.path.isfile(imageFileName):
242  os.remove(imageFileName)
243  logger.debug('Remove file: %s', str(imageFileName))
244 
245  return ret
246 
247 
248  # # check allowed limits for image
249  #
250  # @param imageLimits - instance of ImageLimits class
251  # @param imageProperty - instance of ImageProperty class
252  # @return True if allowed or otherwise False
253  def __isAllowedImage(self, imageLimits, imageProperty):
254  # variable for result
255  ret = True
256  try:
257  if isinstance(imageLimits, MediaLimitsHandler.ImageLimits) and \
258  isinstance(imageProperty, MediaLimitsHandler.ImageProperty):
259 
260  if imageProperty.width is not None and imageLimits.maxWidth is not None and \
261  int(imageProperty.width) > int(imageLimits.maxWidth):
262  raise Exception("Parameter 'width' has not allowed value: %s > %s" % \
263  (str(imageProperty.width), str(imageLimits.maxWidth)))
264 
265  if imageProperty.width is not None and imageLimits.minWidth is not None and \
266  int(imageProperty.width) < int(imageLimits.minWidth):
267  raise Exception("Parameter 'width' has not allowed value: %s < %s" % \
268  (str(imageProperty.width), str(imageLimits.minWidth)))
269 
270  if imageProperty.height is not None and imageLimits.maxHeight is not None and \
271  int(imageProperty.height) > int(imageLimits.maxHeight):
272  raise Exception("Parameter 'height' has not allowed value: %s > %s" % \
273  (str(imageProperty.height), str(imageLimits.maxHeight)))
274 
275  if imageProperty.height is not None and imageLimits.minHeight is not None and \
276  int(imageProperty.height) < int(imageLimits.minHeight):
277  raise Exception("Parameter 'height' has not allowed value: %s < %s" % \
278  (str(imageProperty.height), str(imageLimits.minHeight)))
279 
280  if imageProperty.ratio is not None and imageLimits.maxRatio is not None and \
281  float(imageProperty.ratio) > float(imageLimits.maxRatio):
282  raise Exception("Parameter 'ratio' has not allowed value: %s > %s" % \
283  (str(imageProperty.ratio), str(imageLimits.maxRatio)))
284 
285  if imageProperty.ratio is not None and imageLimits.minRatio is not None and \
286  float(imageProperty.ratio) < float(imageLimits.minRatio):
287  raise Exception("Parameter 'ratio' has not allowed value: %s < %s" % \
288  (str(imageProperty.ratio), str(imageLimits.minRatio)))
289 
290  if imageProperty.colors is not None and imageLimits.minColors is not None and \
291  int(imageProperty.colors) < int(imageLimits.minColors):
292  raise Exception("Parameter 'colors' has not allowed value: %s < %s" % \
293  (str(imageProperty.colors), str(imageLimits.minColors)))
294 
295  if imageProperty.format is not None and imageLimits.contentType is not None and \
296  isinstance(imageProperty.format, basestring) and isinstance(imageLimits.contentType, list):
297  if not imageProperty.format.lower() in \
298  [contentTypeName.lower() for contentTypeName in imageLimits.contentType]:
299  raise Exception("Parameter 'format' = '%s' has not allowed value by '%s' = %s" % \
300  (str(imageProperty.format),
301  str(MediaLimitsHandler.ImageLimits.propertyNamesDict['contentType']),
302  str(imageLimits.contentType)))
303 
304  except Exception, err:
305  logger.debug("%s", str(err))
306  ret = False
307 
308  return ret
309 
310 
311  # # Dump object variables to logger
312  #
313  # @param obj - object instance
314  # @return - None
315  def __dumpObj(self, obj):
316  msg = 'Instance %s has:' % (str(type(obj)))
317  if obj is not None:
318  for name, value in obj.__dict__.items():
319  msg += ("\n'%s' = %s") % (str(name), str(value))
320  logger.debug(msg)
321 
322 
323  # # Check allowed limits
324  #
325  # @param urlString - url string of image
326  # @param binaryType - boolean flag is binary image
327  # @return True if allowed or otherwise False
328  def isAllowedLimits(self, urlString, binaryType=False):
329  # variable for result
330  ret = True
331 
332  if self.mediaLimits is not None:
333  if isinstance(self.mediaLimits, MediaLimitsHandler.ImageLimits):
334  # Get image property
335  imageProperty = self.__getImageProperty(urlString, binaryType)
336  if imageProperty is None:
337  ret = False
338  else:
339  # Dump objects
340  self.__dumpObj(self.mediaLimits)
341  self.__dumpObj(imageProperty)
342 
343  # Check image limits
344  ret = self.__isAllowedImage(self.mediaLimits, imageProperty)
345  else:
346  pass # reserved for other types
347 
348  return ret
def __getImageProperty(self, urlString, binaryType=False)
def __isAllowedImage(self, imageLimits, imageProperty)
def __loadImage(self, urlString, binaryType=False)
def isAllowedLimits(self, urlString, binaryType=False)