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 3.6KB

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