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.

models.py 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import os
  2. import re
  3. from django.utils.translation import ugettext_lazy as _
  4. from django.db import models
  5. from django.utils.timezone import now
  6. from oscar.core.compat import AUTH_USER_MODEL
  7. Product = models.get_model('catalogue', 'Product')
  8. class RangeProductFileUpload(models.Model):
  9. range = models.ForeignKey('offer.Range', related_name='file_uploads',
  10. verbose_name=_("Range"))
  11. filepath = models.CharField(_("File Path"), max_length=255)
  12. size = models.PositiveIntegerField(_("Size"))
  13. uploaded_by = models.ForeignKey(AUTH_USER_MODEL,
  14. verbose_name=_("Uploaded By"))
  15. date_uploaded = models.DateTimeField(_("Date Uploaded"), auto_now_add=True)
  16. PENDING, FAILED, PROCESSED = 'Pending', 'Failed', 'Processed'
  17. choices = (
  18. (PENDING, PENDING),
  19. (FAILED, FAILED),
  20. (PROCESSED, PROCESSED),
  21. )
  22. status = models.CharField(_("Status"), max_length=32, choices=choices,
  23. default=PENDING)
  24. error_message = models.CharField(_("Error Message"), max_length=255,
  25. blank=True)
  26. # Post-processing audit fields
  27. date_processed = models.DateTimeField(_("Date Processed"), null=True)
  28. num_new_skus = models.PositiveIntegerField(_("Number of New SKUs"),
  29. null=True)
  30. num_unknown_skus = models.PositiveIntegerField(_("Number of Unknown SKUs"),
  31. null=True)
  32. num_duplicate_skus = models.PositiveIntegerField(
  33. _("Number of Duplicate SKUs"), null=True)
  34. class Meta:
  35. ordering = ('-date_uploaded',)
  36. verbose_name = _("Range Product Uploaded File")
  37. verbose_name_plural = _("Range Product Uploaded Files")
  38. @property
  39. def filename(self):
  40. return os.path.basename(self.filepath)
  41. def mark_as_failed(self, message=None):
  42. self.date_processed = now()
  43. self.error_message = message
  44. self.status = self.FAILED
  45. self.save()
  46. def mark_as_processed(self, num_new, num_unknown, num_duplicate):
  47. self.status = self.PROCESSED
  48. self.date_processed = now()
  49. self.num_new_skus = num_new
  50. self.num_unknown_skus = num_unknown
  51. self.num_duplicate_skus = num_duplicate
  52. self.save()
  53. def was_processing_successful(self):
  54. return self.status == self.PROCESSED
  55. def process(self):
  56. """
  57. Process the file upload and add products to the range
  58. """
  59. all_ids = set(self.extract_ids())
  60. products = self.range.included_products.all()
  61. existing_skus = products.values_list('stockrecord__partner_sku',
  62. flat=True)
  63. existing_skus = set(filter(bool, existing_skus))
  64. existing_upcs = products.values_list('upc', flat=True)
  65. existing_upcs = set(filter(bool, existing_upcs))
  66. existing_ids = existing_skus.union(existing_upcs)
  67. new_ids = all_ids - existing_ids
  68. products = Product._default_manager.filter(
  69. models.Q(stockrecord__partner_sku__in=new_ids) |
  70. models.Q(upc__in=new_ids))
  71. for product in products:
  72. self.range.add_product(product)
  73. # Processing stats
  74. found_skus = products.values_list('stockrecord__partner_sku',
  75. flat=True)
  76. found_skus = set(filter(bool, found_skus))
  77. found_upcs = set(filter(bool, products.values_list('upc', flat=True)))
  78. found_ids = found_skus.union(found_upcs)
  79. missing_ids = new_ids - found_ids
  80. dupes = set(all_ids).intersection(existing_ids)
  81. self.mark_as_processed(products.count(), len(missing_ids), len(dupes))
  82. def extract_ids(self):
  83. """
  84. Extract all SKU- or UPC-like strings from the file
  85. """
  86. for line in open(self.filepath, 'r'):
  87. for id in re.split('[^\w:\.-]', line):
  88. if id:
  89. yield id
  90. def delete_file(self):
  91. os.unlink(self.filepath)