| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- import os
- import csv
- from decimal import Decimal as D
-
- from django.utils.translation import ugettext_lazy as _
-
- from oscar.apps.catalogue.categories import create_from_breadcrumbs
- from oscar.core.loading import get_class, get_classes
- ImportError = get_class('partner.exceptions', 'ImportError')
- Partner, StockRecord = get_classes('partner.models', ('Partner', 'StockRecord'))
- ProductClass, Product, Category, ProductCategory = get_classes(
- 'catalogue.models', ('ProductClass', 'Product', 'Category',
- 'ProductCategory'))
-
-
- class StockImporter(object):
-
- def __init__(self, logger, partner, delimiter):
- self.logger = logger
- self._delimiter = delimiter
-
- try:
- self._partner = Partner.objects.get(name=partner)
- except Partner.DoesNotExist:
- name_list = ", ".join([d['name'] for d in Partner.objects.values('name')])
- raise ImportError(_("Partner named '%(partner)s' does not exist (existing partners: %(list)s)") % {
- 'partner': partner, 'list': name_list})
-
- def handle(self, file_path=None):
- u"""Handles the actual import process"""
- if not file_path:
- raise ImportError(_("No file path supplied"))
- Validator().validate(file_path)
- self._import(file_path)
-
- def _import(self, file_path):
- u"""Imports given file"""
- stats = {'updated_items': 0,
- 'unchanged_items': 0,
- 'unmatched_items': 0}
- row_number = 0
- for row in csv.reader(open(file_path, 'rb'), delimiter=self._delimiter, quotechar='"', escapechar='\\'):
- row_number += 1
- self._import_row(row_number, row, stats)
- msg = "\tUpdated items: %d\n\tUnchanged items: %d\n\tUnmatched items: %d" % (
- stats['updated_items'],
- stats['unchanged_items'],
- stats['unmatched_items'])
- self.logger.info(msg)
-
- def _import_row(self, row_number, row, stats):
- if len(row) != 3:
- self.logger.error("Row number %d has an invalid number of fields, skipping..." % row_number)
- else:
- self._update_stockrecord(*row[:3], row_number=row_number, stats=stats)
-
- def _update_stockrecord(self, partner_sku, price_excl_tax, num_in_stock, row_number, stats):
- try:
- stock = StockRecord.objects.get(partner=self._partner, partner_sku=partner_sku)
- except StockRecord.DoesNotExist:
- stats['unmatched_items'] += 1
- self.logger.error("\t - Row %d: StockRecord for partner '%s' and sku '%s' does not exist, skipping..." % (row_number, self._partner, partner_sku))
- return
-
- price_changed = False
- if stock.price_excl_tax != D(price_excl_tax):
- stock.price_excl_tax = D(price_excl_tax)
- price_changed = True
-
- stock_changed = False
- if stock.num_in_stock != int(num_in_stock):
- stock.num_in_stock = num_in_stock
- stock_changed = True
-
- if price_changed or stock_changed:
- stock.save()
-
- msg = " SKU %s:" % (partner_sku)
- if price_changed:
- msg += '\n - Price set to %s' % (price_excl_tax)
- if stock_changed:
- msg += '\n - Stock set to %s' % num_in_stock
- self.logger.info(msg)
- stats['updated_items'] += 1
- else:
- stats['unchanged_items'] += 1
-
-
- # Deprecated
- class CatalogueImporter(object):
- """
- A catalogue importer object
- """
-
- _flush = False
-
- def __init__(self, logger, delimiter=",", flush=False):
- self.logger = logger
- self._delimiter = delimiter
- self._flush = flush
-
- def handle(self, file_path=None):
- u"""Handles the actual import process"""
- if not file_path:
- raise ImportError(_("No file path supplied"))
- Validator().validate(file_path)
- if self._flush is True:
- self.logger.info(" - Flushing product data before import")
- self._flush_product_data()
- self._import(file_path)
-
- def _flush_product_data(self):
- u"""Flush out product and stock models"""
- ProductClass.objects.all().delete()
- Product.objects.all().delete()
- Partner.objects.all().delete()
- StockRecord.objects.all().delete()
-
- def _import(self, file_path):
- u"""Imports given file"""
- stats = {'new_items': 0,
- 'updated_items': 0}
- row_number = 0
- for row in csv.reader(open(file_path,'rb'), delimiter=self._delimiter, quotechar='"', escapechar='\\'):
- row_number += 1
- self._import_row(row_number, row, stats)
- msg = "New items: %d, updated items: %d" % (stats['new_items'], stats['updated_items'])
- self.logger.info(msg)
-
- def _import_row(self, row_number, row, stats):
- if len(row) != 4 and len(row) != 8:
- self.logger.error("Row number %d has an invalid number of fields (%d), skipping..." % (row_number, len(row)))
- return
- item = self._create_item(*row[:4], stats=stats)
- if len(row) == 8:
- # With stock data
- self._create_stockrecord(item, *row[4:8], stats=stats)
-
- def _create_item(self, upc, title, description, product_class, stats):
- # Ignore any entries that are NULL
- if description == 'NULL':
- description = ''
-
- # Create item class and item
- product_class,_ = ProductClass.objects.get_or_create(name=product_class)
- try:
- item = Product.objects.get(upc=upc)
- stats['updated_items'] += 1
- except Product.DoesNotExist:
- item = Product()
- stats['new_items'] += 1
- item.upc = upc
- item.title = title
- item.description = description
- item.product_class = product_class
- item.save()
-
- # Category
- cat = create_from_breadcrumbs('Books > Fiction')
- ProductCategory.objects.create(product=item, category=cat)
-
- return item
-
- def _create_stockrecord(self, item, partner_name, partner_sku, price_excl_tax, num_in_stock, stats):
- # Create partner and stock record
- partner, _ = Partner.objects.get_or_create(name=partner_name)
- try:
- stock = StockRecord.objects.get(partner_sku=partner_sku)
- except StockRecord.DoesNotExist:
- stock = StockRecord()
-
- stock.product = item
- stock.partner = partner
- stock.partner_sku = partner_sku
- stock.price_excl_tax = D(price_excl_tax)
- stock.num_in_stock = num_in_stock
- stock.save()
-
-
- class Validator(object):
-
- def validate(self, file_path):
- self._exists(file_path)
- self._is_file(file_path)
- self._is_readable(file_path)
-
- def _exists(self, file_path):
- u"""Check whether a file exists"""
- if not os.path.exists(file_path):
- raise ImportError(_("%s does not exist") % (file_path))
-
- def _is_file(self, file_path):
- u"""Check whether file is actually a file type"""
- if not os.path.isfile(file_path):
- raise ImportError(_("%s is not a file") % (file_path))
-
- def _is_readable(self, file_path):
- u"""Check file is readable"""
- try:
- f = open(file_path, 'r')
- f.close()
- except:
- raise ImportError(_("%s is not readable") % (file_path))
|