Procházet zdrojové kódy

Moved import_catalogue into the partner app

master
David Winterbottom před 14 roky
rodič
revize
7a2a4e2eef

+ 0
- 1
examples/vanilla/settings.py Zobrazit soubor

@@ -200,7 +200,6 @@ INSTALLED_APPS = (
200 200
     'oscar.apps.promotions',
201 201
     'oscar.apps.reports',
202 202
     'oscar.apps.search',
203
-    'oscar.apps.catalogue_import',
204 203
     'pyzen',
205 204
 )
206 205
 

+ 0
- 0
oscar/apps/catalogue_import/__init__.py Zobrazit soubor


+ 0
- 2
oscar/apps/catalogue_import/exceptions.py Zobrazit soubor

@@ -1,2 +0,0 @@
1
-class CatalogueImportException(Exception):
2
-    pass

+ 0
- 0
oscar/apps/catalogue_import/management/__init__.py Zobrazit soubor


+ 0
- 0
oscar/apps/catalogue_import/management/commands/__init__.py Zobrazit soubor


+ 0
- 0
oscar/apps/catalogue_import/models.py Zobrazit soubor


+ 0
- 5
oscar/apps/catalogue_import/tests/__init__.py Zobrazit soubor

@@ -1,5 +0,0 @@
1
-from oscar.apps.catalogue_import.tests.command import *
2
-
3
-__test__ = {
4
-    'command': [command]
5
-}

+ 0
- 124
oscar/apps/catalogue_import/utils.py Zobrazit soubor

@@ -1,124 +0,0 @@
1
-import os
2
-import csv
3
-import sys
4
-from decimal import Decimal as D
5
-
6
-from oscar.core.loading import import_module
7
-
8
-import_module('catalogue_import.exceptions', ['CatalogueImportException'], locals())
9
-import_module('product.models', ['ItemClass', 'Item'], locals())
10
-import_module('partner.models', ['Partner', 'StockRecord'], locals())
11
-
12
-
13
-class Importer(object):
14
-    u"""A catalogue importer object"""
15
-    
16
-    _flush = False
17
-    
18
-    def __init__(self, logger, delimiter=",", flush=False):
19
-        self.logger = logger
20
-        self._delimiter = delimiter
21
-        self._flush = flush
22
-        if flush:
23
-            self.logger.info(" - Flushing product data before import")
24
-    
25
-    def handle(self, file_path=None):
26
-        u"""Handles the actual import process"""
27
-        if not file_path:
28
-            raise CatalogueImportException("No file path supplied")
29
-        
30
-        if self._flush is True:
31
-            self._flush_product_data()
32
-        Validator().validate(file_path)
33
-        self._import(file_path)
34
-        
35
-    def _flush_product_data(self):
36
-        u"""Flush out product and stock models"""
37
-        ItemClass.objects.all().delete()
38
-        Item.objects.all().delete()
39
-        Partner.objects.all().delete()
40
-        StockRecord.objects.all().delete()
41
-
42
-    def _import(self, file_path):
43
-        u"""Imports given file"""
44
-        stats = {'new_items': 0,
45
-                 'updated_items': 0
46
-                 }
47
-        row_number = 0
48
-        for row in csv.reader(open(file_path,'rb'), delimiter=self._delimiter, quotechar='"', escapechar='\\'):
49
-            row_number += 1
50
-            self._import_row(row_number, row, stats)
51
-        msg = "\tNew items: %d\n\tUpdated items: %d" % (stats['new_items'], stats['updated_items'])
52
-        self.logger.info(msg)
53
-    
54
-    def _import_row(self, row_number, row, stats):
55
-        if len(row) != 4 and len(row) != 8:
56
-            self.logger.error("Row number %d has an invalid number of fields, skipping..." % row_number)
57
-            return
58
-        item = self._create_item(*row[:4], stats=stats)
59
-        if len(row) == 8:
60
-            # With stock data
61
-            self._create_stockrecord(item, *row[4:8], stats=stats)
62
-            
63
-    def _create_item(self, upc, title, description, item_class, stats):
64
-        # Ignore any entries that are NULL
65
-        if description == 'NULL':
66
-            description = ''
67
-        
68
-        # Create item class and item
69
-        item_class,_ = ItemClass.objects.get_or_create(name=item_class)
70
-        try:
71
-            item = Item.objects.get(upc=upc)
72
-            stats['updated_items'] += 1
73
-        except Item.DoesNotExist:
74
-            item = Item()
75
-            stats['new_items'] += 1
76
-        item.upc = upc
77
-        item.title = title
78
-        item.description = description
79
-        item.item_class = item_class
80
-        item.save()
81
-        return item
82
-        
83
-    def _create_stockrecord(self, item, partner_name, partner_sku, price_excl_tax, num_in_stock, stats):            
84
-        # Create partner and stock record
85
-        partner, _ = Partner.objects.get_or_create(name=partner_name)
86
-        try:
87
-            stock = StockRecord.objects.get(partner_sku=partner_sku)
88
-        except StockRecord.DoesNotExist:
89
-            stock = StockRecord()
90
-        
91
-        stock.product = item
92
-        stock.partner = partner
93
-        stock.partner_sku = partner_sku
94
-        stock.price_excl_tax = D(price_excl_tax)
95
-        stock.num_in_stock = num_in_stock
96
-        stock.save()
97
-        
98
-        
99
-class Validator(object):
100
-    u"""A catalogue importer file object"""
101
-    
102
-    def validate(self, file_path):
103
-        self._exists(file_path)
104
-        self._is_file(file_path)
105
-        self._is_readable(file_path)
106
-    
107
-    def _exists(self, file_path):
108
-        u"""Check whether a file exists"""
109
-        if not os.path.exists(file_path):
110
-            raise CatalogueImportException("%s does not exist" % (file_path))
111
-        
112
-    def _is_file(self, file_path):
113
-        u"""Check whether file is actually a file type"""
114
-        if not os.path.isfile(file_path):
115
-            raise CatalogueImportException("%s is not a file" % (file_path))
116
-        
117
-    def _is_readable(self, file_path):
118
-        u"""Check file is readable"""
119
-        try:
120
-            f = open(file_path, 'r')
121
-            f.close()
122
-        except:
123
-            raise CatalogueImportException("%s is not readable" % (file_path))
124
-        

+ 2
- 2
oscar/apps/partner/exceptions.py Zobrazit soubor

@@ -1,2 +1,2 @@
1
-class StockImportException(Exception):
2
-    pass
1
+class ImportException(Exception):
2
+    pass

oscar/apps/catalogue_import/fixtures/books-small-semicolon.csv → oscar/apps/partner/fixtures/books-small-semicolon.csv Zobrazit soubor


oscar/apps/catalogue_import/fixtures/books-small.csv → oscar/apps/partner/fixtures/books-small.csv Zobrazit soubor


oscar/apps/catalogue_import/management/commands/import_catalogue.py → oscar/apps/partner/management/commands/import_catalogue.py Zobrazit soubor

@@ -5,8 +5,8 @@ from optparse import make_option
5 5
 from django.core.management.base import BaseCommand, CommandError
6 6
 
7 7
 from oscar.core.loading import import_module
8
-import_module('catalogue_import.utils', ['Importer'], locals())
9
-import_module('catalogue_import.exceptions', ['CatalogueImportException'], locals())
8
+import_module('partner.utils', ['CatalogueImporter'], locals())
9
+import_module('partner.exceptions', ['CatalogueImportException'], locals())
10 10
 
11 11
 
12 12
 class Command(BaseCommand):
@@ -32,7 +32,7 @@ class Command(BaseCommand):
32 32
             raise CommandError("Please select a CSV file to import")
33 33
         
34 34
         logger.info("Starting catalogue import")
35
-        importer = Importer(logger, delimiter=options.get('delimiter'), flush=options.get('flush'))
35
+        importer = CatalogueImporter(logger, delimiter=options.get('delimiter'), flush=options.get('flush'))
36 36
         for file_path in args:
37 37
             logger.info(" - Importing records from '%s'" % file_path)
38 38
             try:

+ 44
- 0
oscar/apps/partner/management/commands/import_stock.py Zobrazit soubor

@@ -0,0 +1,44 @@
1
+import logging
2
+import sys
3
+import os
4
+from optparse import make_option
5
+
6
+from django.core.management.base import BaseCommand, CommandError
7
+
8
+from oscar.core.loading import import_module
9
+import_module('partner.utils', ['StockImporter'], locals())
10
+import_module('partner.exceptions', ['StockImportException'], locals())
11
+
12
+
13
+class Command(BaseCommand):
14
+    
15
+    args = '<partner> /path/to/file1.csv'
16
+    help = 'For updating stock for a partner based on a CSV file'
17
+    
18
+    option_list = BaseCommand.option_list + (
19
+        make_option('--delimiter',
20
+            dest='delimiter',
21
+            default=",",
22
+            help='Delimiter used within CSV file(s)'),
23
+        )
24
+
25
+    def handle(self, *args, **options):
26
+        if len(args) != 2:
27
+            raise CommandError('Command requires a partner and a path to a csv file') 
28
+        
29
+        logger = self._get_logger()
30
+        
31
+        try:
32
+            importer = StockImporter(logger, partner=args[0], delimiter=options.get('delimiter'))
33
+            logger.info("Starting stock import")
34
+            logger.info(" - Importing records from '%s'" % args[1])
35
+            importer.handle(args[1])
36
+        except StockImportException, e:
37
+            raise CommandError(str(e))
38
+            
39
+    def _get_logger(self):
40
+        logger = logging.getLogger('oscar.apps.partner.import_stock')
41
+        stream = logging.StreamHandler(self.stdout)
42
+        logger.addHandler(stream)
43
+        logger.setLevel(logging.DEBUG)
44
+        return logger

+ 7
- 0
oscar/apps/partner/tests/__init__.py Zobrazit soubor

@@ -0,0 +1,7 @@
1
+from oscar.apps.partner.tests.model_tests import *
2
+from oscar.apps.partner.tests.import_catalogue import *
3
+
4
+__test__ = {
5
+    'model_tests': [model_tests],
6
+    'import_catalogue': [import_catalogue],
7
+}

oscar/apps/catalogue_import/tests/command.py → oscar/apps/partner/tests/import_catalogue.py Zobrazit soubor

@@ -3,8 +3,8 @@ from decimal import Decimal as D
3 3
 from django.test import TestCase
4 4
 import logging
5 5
 
6
-from oscar.apps.catalogue_import.utils import Importer
7
-from oscar.apps.catalogue_import.exceptions import CatalogueImportException
6
+from oscar.apps.partner.utils import CatalogueImporter
7
+from oscar.apps.partner.exceptions import ImportException
8 8
 from oscar.apps.product.models import ItemClass, Item
9 9
 from oscar.apps.partner.models import Partner, StockRecord
10 10
 from oscar.test.helpers import create_product
@@ -23,21 +23,21 @@ logger.addHandler(NullHandler())
23 23
 class CommandEdgeCasesTest(TestCase):
24 24
 
25 25
     def setUp(self):
26
-        self.importer = Importer(logger)
26
+        self.importer = CatalogueImporter(logger)
27 27
 
28 28
     def test_sending_no_file_argument_raises_exception(self):
29 29
         self.importer.afile = None
30
-        with self.assertRaises(CatalogueImportException):
30
+        with self.assertRaises(ImportException):
31 31
             self.importer.handle()
32 32
 
33 33
     def test_sending_directory_as_file_raises_exception(self):
34 34
         self.importer.afile = "/tmp"
35
-        with self.assertRaises(CatalogueImportException):
35
+        with self.assertRaises(ImportException):
36 36
             self.importer.handle()
37 37
 
38 38
     def test_importing_nonexistant_file_raises_exception(self):
39 39
         self.importer.afile = "/tmp/catalogue-import.zgvsfsdfsd"
40
-        with self.assertRaises(CatalogueImportException):
40
+        with self.assertRaises(ImportException):
41 41
             self.importer.handle()
42 42
 
43 43
 
@@ -51,7 +51,7 @@ class ImportSmokeTest(TestCase):
51 51
     
52 52
     
53 53
     def setUp(self):
54
-        self.importer = Importer(logger)
54
+        self.importer = CatalogueImporter(logger)
55 55
         self.importer.handle(TEST_BOOKS_CSV)
56 56
         self.item = Item.objects.get(upc='9780115531446')
57 57
         
@@ -101,7 +101,7 @@ class ImportSmokeTest(TestCase):
101 101
 class ImportSemicolonDelimitedFileTest(TestCase):
102 102
     
103 103
     def setUp(self):
104
-        self.importer = Importer(logger, delimiter=";")
104
+        self.importer = CatalogueImporter(logger, delimiter=";")
105 105
         
106 106
     def test_import(self):
107 107
         self.importer.handle(TEST_BOOKS_SEMICOLON_CSV)
@@ -110,7 +110,7 @@ class ImportSemicolonDelimitedFileTest(TestCase):
110 110
 class ImportWithFlushTest(TestCase):
111 111
     
112 112
     def setUp(self):
113
-        self.importer = Importer(logger, flush=True)
113
+        self.importer = CatalogueImporter(logger, flush=True)
114 114
 
115 115
     def test_items_are_flushed_by_importer(self):
116 116
         upc = "0000000000000"

oscar/apps/partner/tests.py → oscar/apps/partner/tests/model_tests.py Zobrazit soubor

@@ -9,6 +9,7 @@ from oscar.apps.product.models import Item, ItemClass
9 9
 from oscar.apps.partner.models import Partner, StockRecord
10 10
 from oscar.test.helpers import create_product
11 11
 
12
+
12 13
 class DummyWrapper(object):
13 14
     
14 15
     def availability(self, stockrecord):
@@ -50,6 +51,7 @@ class DefaultWrapperTests(unittest.TestCase):
50 51
         date = datetime.date.today() + datetime.timedelta(days=7)
51 52
         self.assertEquals(date, product.stockrecord.dispatch_date)
52 53
     
54
+    
53 55
 class CustomWrapperTests(unittest.TestCase):    
54 56
 
55 57
     def setUp(self):

+ 96
- 7
oscar/apps/partner/utils.py Zobrazit soubor

@@ -5,10 +5,12 @@ from decimal import Decimal as D
5 5
 
6 6
 from oscar.core.loading import import_module
7 7
 
8
-import_module('partner.exceptions', ['StockImportException'], locals())
8
+import_module('partner.exceptions', ['ImportException'], locals())
9 9
 import_module('partner.models', ['Partner', 'StockRecord'], locals())
10
+import_module('product.models', ['ItemClass', 'Item'], locals())
10 11
 
11
-class Importer(object):
12
+
13
+class StockImporter(object):
12 14
     
13 15
     def __init__(self, logger, partner, delimiter):
14 16
         self.logger = logger
@@ -17,12 +19,12 @@ class Importer(object):
17 19
         try:
18 20
             self._partner = Partner.objects.get(name=partner)
19 21
         except Partner.DoesNotExist:
20
-            raise StockImportException("Partner named '%s' does not exist" % partner)
22
+            raise ImportException("Partner named '%s' does not exist" % partner)
21 23
     
22 24
     def handle(self, file_path=None):
23 25
         u"""Handles the actual import process"""
24 26
         if not file_path:
25
-            raise StockImportException("No file path supplied")
27
+            raise ImportException("No file path supplied")
26 28
         Validator().validate(file_path)
27 29
         self._import(file_path)
28 30
 
@@ -77,6 +79,93 @@ class Importer(object):
77 79
             stats['updated_items'] += 1
78 80
         else:
79 81
             stats['unchanged_items'] += 1
82
+
83
+
84
+class CatalogueImporter(object):
85
+    u"""A catalogue importer object"""
86
+    
87
+    _flush = False
88
+    
89
+    def __init__(self, logger, delimiter=",", flush=False):
90
+        self.logger = logger
91
+        self._delimiter = delimiter
92
+        self._flush = flush
93
+        if flush:
94
+            self.logger.info(" - Flushing product data before import")
95
+    
96
+    def handle(self, file_path=None):
97
+        u"""Handles the actual import process"""
98
+        if not file_path:
99
+            raise ImportException("No file path supplied")
100
+        
101
+        if self._flush is True:
102
+            self._flush_product_data()
103
+        Validator().validate(file_path)
104
+        self._import(file_path)
105
+        
106
+    def _flush_product_data(self):
107
+        u"""Flush out product and stock models"""
108
+        ItemClass.objects.all().delete()
109
+        Item.objects.all().delete()
110
+        Partner.objects.all().delete()
111
+        StockRecord.objects.all().delete()
112
+
113
+    def _import(self, file_path):
114
+        u"""Imports given file"""
115
+        stats = {'new_items': 0,
116
+                 'updated_items': 0
117
+                 }
118
+        row_number = 0
119
+        for row in csv.reader(open(file_path,'rb'), delimiter=self._delimiter, quotechar='"', escapechar='\\'):
120
+            row_number += 1
121
+            self._import_row(row_number, row, stats)
122
+        msg = "\tNew items: %d\n\tUpdated items: %d" % (stats['new_items'], stats['updated_items'])
123
+        self.logger.info(msg)
124
+    
125
+    def _import_row(self, row_number, row, stats):
126
+        if len(row) != 4 and len(row) != 8:
127
+            self.logger.error("Row number %d has an invalid number of fields (%d), skipping..." % (row_number, len(row)))
128
+            return
129
+        item = self._create_item(*row[:4], stats=stats)
130
+        if len(row) == 8:
131
+            # With stock data
132
+            self._create_stockrecord(item, *row[4:8], stats=stats)
133
+            
134
+    def _create_item(self, upc, title, description, item_class, stats):
135
+        # Ignore any entries that are NULL
136
+        if description == 'NULL':
137
+            description = ''
138
+        
139
+        # Create item class and item
140
+        item_class,_ = ItemClass.objects.get_or_create(name=item_class)
141
+        try:
142
+            item = Item.objects.get(upc=upc)
143
+            stats['updated_items'] += 1
144
+        except Item.DoesNotExist:
145
+            item = Item()
146
+            stats['new_items'] += 1
147
+        item.upc = upc
148
+        item.title = title
149
+        item.description = description
150
+        item.item_class = item_class
151
+        item.save()
152
+        return item
153
+        
154
+    def _create_stockrecord(self, item, partner_name, partner_sku, price_excl_tax, num_in_stock, stats):            
155
+        # Create partner and stock record
156
+        partner, _ = Partner.objects.get_or_create(name=partner_name)
157
+        try:
158
+            stock = StockRecord.objects.get(partner_sku=partner_sku)
159
+        except StockRecord.DoesNotExist:
160
+            stock = StockRecord()
161
+        
162
+        stock.product = item
163
+        stock.partner = partner
164
+        stock.partner_sku = partner_sku
165
+        stock.price_excl_tax = D(price_excl_tax)
166
+        stock.num_in_stock = num_in_stock
167
+        stock.save()    
168
+    
80 169
         
81 170
 class Validator(object):
82 171
     def validate(self, file_path):
@@ -87,12 +176,12 @@ class Validator(object):
87 176
     def _exists(self, file_path):
88 177
         u"""Check whether a file exists"""
89 178
         if not os.path.exists(file_path):
90
-            raise StockImportException("%s does not exist" % (file_path))
179
+            raise ImportException("%s does not exist" % (file_path))
91 180
         
92 181
     def _is_file(self, file_path):
93 182
         u"""Check whether file is actually a file type"""
94 183
         if not os.path.isfile(file_path):
95
-            raise StockImportException("%s is not a file" % (file_path))
184
+            raise ImportException("%s is not a file" % (file_path))
96 185
         
97 186
     def _is_readable(self, file_path):
98 187
         u"""Check file is readable"""
@@ -100,4 +189,4 @@ class Validator(object):
100 189
             f = open(file_path, 'r')
101 190
             f.close()
102 191
         except:
103
-            raise StockImportException("%s is not readable" % (file_path))
192
+            raise ImportException("%s is not readable" % (file_path))

+ 0
- 1
oscar/tests.py Zobrazit soubor

@@ -10,6 +10,5 @@ from oscar.apps.shipping.tests import *
10 10
 from oscar.apps.customer.tests import *
11 11
 from oscar.apps.discount.tests import *
12 12
 from oscar.apps.promotions.tests import *
13
-from oscar.apps.catalogue_import.tests import *
14 13
 
15 14
 from oscar.core.tests import *

Načítá se…
Zrušit
Uložit