Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import os
  2. from decimal import Decimal as D
  3. from django.db.transaction import commit_on_success
  4. from django.utils.translation import ugettext_lazy as _
  5. from oscar.apps.catalogue.categories import create_from_breadcrumbs
  6. from oscar.apps.dashboard.reports.csv_utils import CsvUnicodeReader
  7. from oscar.core.loading import get_class, get_classes
  8. ImportError = get_class('partner.exceptions', 'ImportError')
  9. Partner, StockRecord = get_classes('partner.models', ['Partner',
  10. 'StockRecord'])
  11. ProductClass, Product, Category, ProductCategory = get_classes(
  12. 'catalogue.models', ('ProductClass', 'Product', 'Category',
  13. 'ProductCategory'))
  14. class StockImporter(object):
  15. def __init__(self, logger, partner, delimiter):
  16. self.logger = logger
  17. self._delimiter = delimiter
  18. try:
  19. self._partner = Partner.objects.get(name=partner)
  20. except Partner.DoesNotExist:
  21. name_list = ", ".join([d['name']
  22. for d in Partner.objects.values('name')])
  23. raise ImportError(_("Partner named '%(partner)s' does not exist"
  24. " (existing partners: %(list)s)")
  25. % {'partner': partner, 'list': name_list})
  26. def handle(self, file_path=None):
  27. u"""Handles the actual import process"""
  28. if not file_path:
  29. raise ImportError(_("No file path supplied"))
  30. Validator().validate(file_path)
  31. self._import(file_path)
  32. def _import(self, file_path):
  33. u"""Imports given file"""
  34. stats = {'updated_items': 0,
  35. 'unchanged_items': 0,
  36. 'unmatched_items': 0}
  37. row_number = 0
  38. for row in CsvUnicodeReader(open(file_path, 'rb'),
  39. delimiter=self._delimiter, quotechar='"',
  40. escapechar='\\'):
  41. row_number += 1
  42. self._import_row(row_number, row, stats)
  43. msg = "\tUpdated items: %d\n\tUnchanged items: %d\n" \
  44. "\tUnmatched items: %d" % (stats['updated_items'],
  45. stats['unchanged_items'],
  46. stats['unmatched_items'])
  47. self.logger.info(msg)
  48. def _import_row(self, row_number, row, stats):
  49. if len(row) != 3:
  50. self.logger.error("Row number %d has an invalid number of fields,"
  51. " skipping..." % row_number)
  52. else:
  53. self._update_stockrecord(*row[:3], row_number=row_number,
  54. stats=stats)
  55. def _update_stockrecord(self, partner_sku, price_excl_tax, num_in_stock,
  56. row_number, stats):
  57. try:
  58. stock = StockRecord.objects.get(partner=self._partner,
  59. partner_sku=partner_sku)
  60. except StockRecord.DoesNotExist:
  61. stats['unmatched_items'] += 1
  62. self.logger.error("\t - Row %d: StockRecord for partner '%s' and"
  63. " sku '%s' does not exist, skipping..."
  64. % (row_number, self._partner, partner_sku))
  65. return
  66. price_changed = False
  67. if stock.price_excl_tax != D(price_excl_tax):
  68. stock.price_excl_tax = D(price_excl_tax)
  69. price_changed = True
  70. stock_changed = False
  71. if stock.num_in_stock != int(num_in_stock):
  72. stock.num_in_stock = num_in_stock
  73. stock_changed = True
  74. if price_changed or stock_changed:
  75. stock.save()
  76. msg = " SKU %s:" % (partner_sku)
  77. if price_changed:
  78. msg += '\n - Price set to %s' % (price_excl_tax)
  79. if stock_changed:
  80. msg += '\n - Stock set to %s' % num_in_stock
  81. self.logger.info(msg)
  82. stats['updated_items'] += 1
  83. else:
  84. stats['unchanged_items'] += 1
  85. # Deprecated
  86. class CatalogueImporter(object):
  87. """
  88. A catalogue importer object
  89. """
  90. _flush = False
  91. def __init__(self, logger, delimiter=",", flush=False):
  92. self.logger = logger
  93. self._delimiter = delimiter
  94. self._flush = flush
  95. def handle(self, file_path=None):
  96. u"""Handles the actual import process"""
  97. if not file_path:
  98. raise ImportError(_("No file path supplied"))
  99. Validator().validate(file_path)
  100. if self._flush is True:
  101. self.logger.info(" - Flushing product data before import")
  102. self._flush_product_data()
  103. self._import(file_path)
  104. def _flush_product_data(self):
  105. u"""Flush out product and stock models"""
  106. ProductClass.objects.all().delete()
  107. Product.objects.all().delete()
  108. Partner.objects.all().delete()
  109. StockRecord.objects.all().delete()
  110. @commit_on_success
  111. def _import(self, file_path):
  112. u"""Imports given file"""
  113. stats = {'new_items': 0,
  114. 'updated_items': 0}
  115. row_number = 0
  116. for row in CsvUnicodeReader(open(file_path, 'rb'),
  117. delimiter=self._delimiter, quotechar='"',
  118. escapechar='\\'):
  119. row_number += 1
  120. self._import_row(row_number, row, stats)
  121. msg = "New items: %d, updated items: %d" % (stats['new_items'],
  122. stats['updated_items'])
  123. self.logger.info(msg)
  124. def _import_row(self, row_number, row, stats):
  125. if len(row) != 5 and len(row) != 9:
  126. self.logger.error("Row number %d has an invalid number of fields"
  127. " (%d), skipping..." % (row_number, len(row)))
  128. return
  129. item = self._create_item(*row[:5], stats=stats)
  130. if len(row) == 9:
  131. # With stock data
  132. self._create_stockrecord(item, *row[5:9], stats=stats)
  133. def _create_item(self, product_class, category_str, upc, title,
  134. description, stats):
  135. # Ignore any entries that are NULL
  136. if description == 'NULL':
  137. description = ''
  138. # Create item class and item
  139. product_class, __ \
  140. = ProductClass.objects.get_or_create(name=product_class)
  141. try:
  142. item = Product.objects.get(upc=upc)
  143. stats['updated_items'] += 1
  144. except Product.DoesNotExist:
  145. item = Product()
  146. stats['new_items'] += 1
  147. item.upc = upc
  148. item.title = title
  149. item.description = description
  150. item.product_class = product_class
  151. item.save()
  152. # Category
  153. cat = create_from_breadcrumbs(category_str)
  154. ProductCategory.objects.create(product=item, category=cat)
  155. return item
  156. def _create_stockrecord(self, item, partner_name, partner_sku,
  157. price_excl_tax, num_in_stock, stats):
  158. # Create partner and stock record
  159. partner, _ = Partner.objects.get_or_create(
  160. name=partner_name)
  161. try:
  162. stock = StockRecord.objects.get(partner_sku=partner_sku)
  163. except StockRecord.DoesNotExist:
  164. stock = StockRecord()
  165. stock.product = item
  166. stock.partner = partner
  167. stock.partner_sku = partner_sku
  168. stock.price_excl_tax = D(price_excl_tax)
  169. stock.num_in_stock = num_in_stock
  170. stock.save()
  171. class Validator(object):
  172. def validate(self, file_path):
  173. self._exists(file_path)
  174. self._is_file(file_path)
  175. self._is_readable(file_path)
  176. def _exists(self, file_path):
  177. u"""Check whether a file exists"""
  178. if not os.path.exists(file_path):
  179. raise ImportError(_("%s does not exist") % (file_path))
  180. def _is_file(self, file_path):
  181. u"""Check whether file is actually a file type"""
  182. if not os.path.isfile(file_path):
  183. raise ImportError(_("%s is not a file") % (file_path))
  184. def _is_readable(self, file_path):
  185. u"""Check file is readable"""
  186. try:
  187. f = open(file_path, 'r')
  188. f.close()
  189. except:
  190. raise ImportError(_("%s is not readable") % (file_path))