You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils.py 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import os
  2. import zlib
  3. import tarfile
  4. import zipfile
  5. import tempfile
  6. import shutil
  7. import Image as PImage
  8. from django.core.files import File
  9. from django.core.exceptions import FieldError
  10. from django.db.models import get_model
  11. from django.utils.translation import ugettext_lazy as _
  12. from oscar.apps.catalogue.exceptions import (
  13. ImageImportError, IdenticalImageError, InvalidImageArchive)
  14. Category = get_model('catalogue', 'category')
  15. Product = get_model('catalogue', 'product')
  16. ProductImage = get_model('catalogue', 'productimage')
  17. class Importer(object):
  18. allowed_extensions = ['.jpeg','.jpg','.gif','.png']
  19. def __init__(self, logger, field):
  20. self.logger = logger
  21. self._field = field
  22. def handle(self, dirname):
  23. stats = {
  24. 'num_processed': 0,
  25. 'num_skipped': 0,
  26. 'num_invalid': 0}
  27. image_dir, filenames = self._get_image_files(dirname)
  28. if image_dir:
  29. for filename in filenames:
  30. try:
  31. lookup_value = self._get_lookup_value_from_filename(filename)
  32. self._process_image(image_dir, filename, lookup_value)
  33. stats['num_processed'] += 1
  34. except Product.MultipleObjectsReturned:
  35. self.logger.warning("Multiple products matching %s='%s', skipping" % (self._field, lookup_value))
  36. stats['num_skipped'] += 1
  37. except Product.DoesNotExist:
  38. self.logger.warning("No item matching %s='%s'" % (self._field, lookup_value))
  39. stats['num_skipped'] += 1
  40. except IdenticalImageError:
  41. self.logger.warning(" - Identical image already exists for %s='%s', skipping" % (self._field, lookup_value))
  42. stats['num_skipped'] += 1
  43. except IOError, e:
  44. raise ImageImportError(_('%(filename)s is not a valid image (%(error)s)') % {
  45. 'filename': filename, 'error': e})
  46. stats['num_invalid'] += 1
  47. except FieldError, e:
  48. raise ImageImportError(e)
  49. self._process_image(image_dir, filename)
  50. if image_dir != dirname:
  51. shutil.rmtree(image_dir)
  52. else:
  53. raise InvalidImageArchive(_('%s is not a valid image archive') % dirname)
  54. self.logger.info("Finished image import: %(num_processed)d imported, %(num_skipped)d skipped" % stats)
  55. def _get_image_files(self, dirname):
  56. filenames = []
  57. image_dir = self._extract_images(dirname)
  58. if image_dir:
  59. for filename in os.listdir(image_dir):
  60. ext = os.path.splitext(filename)[1]
  61. if os.path.isfile(os.path.join(image_dir, filename)) and ext in self.allowed_extensions:
  62. filenames.append(filename)
  63. return image_dir, filenames
  64. def _extract_images(self, dirname):
  65. '''
  66. Returns path to directory containing images in dirname if successful.
  67. Returns empty string if dirname does not exist, or could not be opened.
  68. Assumes that if dirname is a directory, then it contains images.
  69. If dirname is an archive (tar/zip file) then the path returned is to a
  70. temporary directory that should be deleted when no longer required.
  71. '''
  72. if os.path.isdir(dirname):
  73. return dirname
  74. ext = os.path.splitext(dirname)[1]
  75. if ext in ['.gz', '.tar']:
  76. image_dir = tempfile.mkdtemp()
  77. try:
  78. tar_file = tarfile.open(dirname)
  79. tar_file.extractall(image_dir)
  80. tar_file.close()
  81. return image_dir
  82. except (tarfile.TarError, zlib.error):
  83. return ""
  84. elif ext == '.zip':
  85. image_dir = tempfile.mkdtemp()
  86. try:
  87. zip_file = zipfile.ZipFile(dirname)
  88. zip_file.extractall(image_dir)
  89. zip_file.close()
  90. return image_dir
  91. except (zlib.error, zipfile.BadZipfile, zipfile.LargeZipFile):
  92. return ""
  93. # unknown archive - perhaps this should be treated differently
  94. return ""
  95. def _process_image(self, dirname, filename, lookup_value):
  96. file_path = os.path.join(dirname, filename)
  97. trial_image = PImage.open(file_path)
  98. trial_image.verify()
  99. kwargs = {self._field: lookup_value}
  100. item = Product._default_manager.get(**kwargs)
  101. new_data = open(file_path).read()
  102. next_index = 0
  103. for existing in item.images.all():
  104. next_index = existing.display_order + 1
  105. try:
  106. if new_data == existing.original.read():
  107. raise IdenticalImageError()
  108. except IOError:
  109. # File probably doesn't exist
  110. existing.delete()
  111. new_file = File(open(file_path))
  112. im = ProductImage(product=item, display_order=next_index)
  113. im.original.save(filename, new_file, save=False)
  114. im.save()
  115. self.logger.info(' - Image added to "%s"' % item)
  116. def _fetch_item(self, filename):
  117. kwargs = {self._field: self._get_lookup_value_from_filename(filename)}
  118. return Product._default_manager.get(**kwargs)
  119. def _get_lookup_value_from_filename(self, filename):
  120. return os.path.splitext(filename)[0]