diff --git a/.gitignore b/.gitignore index b809595..99e9fc6 100644 --- a/.gitignore +++ b/.gitignore @@ -143,3 +143,4 @@ artel/static/ # media artel/media/* +artel/store/data/* diff --git a/artel/store/forms.py b/artel/store/forms.py index 27cbe40..2f45ad9 100644 --- a/artel/store/forms.py +++ b/artel/store/forms.py @@ -5,7 +5,7 @@ from django.db.models import Model from store.models import ( ProductTemplate, - ProductTemplateParam, + ProductTemplateParamValue, Product, PaymentMethod, DeliveryMethod @@ -71,11 +71,16 @@ class ButtonToggleSelect(forms.RadioSelect): class ProductTemplateConfigForm(forms.Form): def _create_dynamic_fields(self, template: ProductTemplate): - category_params = template.category.category_params.all() - for param in category_params: + template_params = template.template_params.all() + for param in template_params: + queryset = ProductTemplateParamValue.objects.filter(param=param) + if queryset.count() >= 4: + widget = forms.Select(attrs={"class": "form-select"}) + else: + widget = ButtonToggleSelect(attrs={"class": "btn-group btn-group-toggle"}) self.fields[param.key] = forms.ModelChoiceField( - queryset=ProductTemplateParam.objects.filter(param=param), - widget=ButtonToggleSelect(attrs={"class": "btn-group btn-group-toggle"}), + queryset=queryset, + widget=widget, ) def __init__( diff --git a/artel/store/loader.py b/artel/store/loader.py index 214e42c..0aacc9f 100644 --- a/artel/store/loader.py +++ b/artel/store/loader.py @@ -1,12 +1,14 @@ import logging +import time import requests import pandas as pd from django.core.files.base import ContentFile - +from django.conf import settings from store.models import ( ProductTemplate, ProductTemplateParam, + ProductTemplateParamValue, Product, ProductImage ) @@ -29,15 +31,24 @@ class TemplateLoader(BaseLoader): class ProductLoader(BaseLoader): + def _clear(self): + Product.objects.all().delete() + + def __init__(self, path, param_names, clear=False): + super().__init__(path) + self.param_names = param_names + if clear: + self._clear() + def _get_images(self, row) -> list[ContentFile]: - urls = row["images"] + url = row["images"] images = [] - for url in urls: - response = requests.get(url, stream=True) - if response.status_code == 200: - data = response.raw - file_name = url.split("/")[-1] - image = ContentFile(data, name=file_name) + response = requests.get( + url+"/preview", stream=True + ) + if response.status_code == 200: + data = response.content + image = ContentFile(data, name=row["template"]) images.append(image) return images @@ -47,10 +58,11 @@ class ProductLoader(BaseLoader): name = row["name"] available = bool(row["available"]) params = [] - for param in row["params"]: - key, value = param - param = ProductTemplateParam.objects.get(param__key=key, value=value) - params.append(param) + for key in self.param_names: + value = row[key] + param, _ = ProductTemplateParam.objects.get_or_create(key=key, template=template) + param_value, _ = ProductTemplateParamValue.objects.get_or_create(param=param, value=value) + params.append(param_value) product = Product.objects.get_or_create_by_params(template=template, params=params) product.price = price product.name = name @@ -66,6 +78,7 @@ class ProductLoader(BaseLoader): data = self.load_data() products = [] for _, row in data.iterrows(): + time.sleep(5) try: product = self._process_row(row) except Exception as e: diff --git a/artel/store/management/commands/load_products.py b/artel/store/management/commands/load_products.py index a277ad0..84197e0 100644 --- a/artel/store/management/commands/load_products.py +++ b/artel/store/management/commands/load_products.py @@ -9,5 +9,5 @@ class Command(BaseCommand): help = "Load products from csv file" def handle(self, *args, **options): - loader = ProductLoader(settings.PRODUCTS_CSV_PATH) + loader = ProductLoader(settings.PRODUCTS_CSV_PATH, ["mocowanie", "format", "wykonanie"], True) loader.process() diff --git a/artel/store/models.py b/artel/store/models.py index 8bb104f..f7f20bd 100644 --- a/artel/store/models.py +++ b/artel/store/models.py @@ -90,8 +90,7 @@ class ProductCategory(ClusterableModel): return self.name panels = [ - FieldPanel("name"), - InlinePanel("category_params") + FieldPanel("name") ] @@ -122,6 +121,7 @@ class ProductTemplate(ClusterableModel): FieldPanel('description'), InlinePanel("template_images", label="Template Images"), FieldPanel("tags"), + InlinePanel("template_params") ] diff --git a/artel/store/templates/store/forms/button_toggle_select.html b/artel/store/templates/store/forms/button_toggle_select.html index 2d5e4b6..b145705 100644 --- a/artel/store/templates/store/forms/button_toggle_select.html +++ b/artel/store/templates/store/forms/button_toggle_select.html @@ -10,19 +10,3 @@ {% endfor %} {% endwith %} - diff --git a/artel/store/tests/factories.py b/artel/store/tests/factories.py index d951619..31f961d 100644 --- a/artel/store/tests/factories.py +++ b/artel/store/tests/factories.py @@ -74,7 +74,7 @@ class ProductParamFactory(DjangoModelFactory): model = 'store.ProductParam' product = SubFactory(ProductFactory) - param = SubFactory(ProductCategoryParamFactory) + param = SubFactory(ProductTemplateParamValueFactory) class PaymentMethodFactory(DjangoModelFactory): diff --git a/artel/store/tests/test_loader.py b/artel/store/tests/test_loader.py index 428b239..09b039e 100644 --- a/artel/store/tests/test_loader.py +++ b/artel/store/tests/test_loader.py @@ -10,23 +10,21 @@ class TestProductLoader(TestCase): def setUp(self) -> None: self.category = factories.ProductCategoryFactory() self.template = factories.ProductTemplateFactory(category=self.category) - self.category_params = [factories.ProductTemplateParamFactory(category=self.category) for _ in range(3)] - self.category_param_values = [factories.ProductTemplateParamValueFactory(param=param) for param in self.category_params] + self.template_params = [factories.ProductTemplateParamFactory(template=self.template) for _ in range(3)] + self.templat_params_values = [factories.ProductTemplateParamValueFactory(param=param) for param in self.template_params] def test_load_products_single_product_success(self): fake_df = pd.DataFrame({ "template": [self.template.code], - "price": [10.0], + "price": str(10.0), "name": ["Test product"], "available": [True], - "params": [[ - (self.category_params[0].key, self.category_param_values[0].value), - (self.category_params[1].key, self.category_param_values[1].value), - (self.category_params[2].key, self.category_param_values[2].value), - ]] + self.template_params[0].key: self.templat_params_values[0].value, + self.template_params[1].key: self.templat_params_values[1].value, + self.template_params[2].key: self.templat_params_values[2].value }) with patch("store.loader.BaseLoader.load_data", return_value=fake_df): - loader = ProductLoader("fake_path") + loader = ProductLoader("fake_path", [p.key for p in self.template_params]) loader.process() self.assertEqual(self.template.products.count(), 1) @@ -42,14 +40,12 @@ class TestProductLoader(TestCase): "price": ["FASDSADQAW"], "name": ["Test product"], "available": [True], - "params": [[ - (self.category_params[0].key, self.category_param_values[0].value), - (self.category_params[1].key, self.category_param_values[1].value), - (self.category_params[2].key, self.category_param_values[2].value), - ]] + self.template_params[0].key: self.templat_params_values[0].value, + self.template_params[1].key: self.templat_params_values[1].value, + self.template_params[2].key: self.templat_params_values[2].value }) with patch("store.loader.BaseLoader.load_data", return_value=fake_df): - loader = ProductLoader("fake_path") + loader = ProductLoader("fake_path", [p.key for p in self.template_params]) loader.process() self.assertEqual(self.template.products.count(), 0) @@ -59,39 +55,17 @@ class TestProductLoader(TestCase): def test_load_no_existing_template_code_failure(self, mock_logger): fake_df = pd.DataFrame({ "template": ["NOTEEXISTINGTEMPLATE"], - "price": [10.0], + "price": str(10.0), "name": ["Test product"], "available": [True], - "params": [[ - (self.category_params[0].key, self.category_param_values[0].value), - (self.category_params[1].key, self.category_param_values[1].value), - (self.category_params[2].key, self.category_param_values[2].value), - ]] + self.template_params[0].key: self.templat_params_values[0].value, + self.template_params[1].key: self.templat_params_values[1].value, + self.template_params[2].key: self.templat_params_values[2].value }) with patch("store.loader.BaseLoader.load_data", return_value=fake_df): - loader = ProductLoader("fake_path") + loader = ProductLoader("fake_path", [p.key for p in self.template_params]) loader.process() self.assertEqual(self.template.products.count(), 0) mock_logger.exception.assert_called_with("ProductTemplate matching query does not exist.") - - @patch("store.loader.logger") - def test_not_existing_params_key_value_pairs_failure(self, mock_logger): - fake_df = pd.DataFrame({ - "template": [self.template.code], - "price": [10.0], - "name": ["Test product"], - "available": [True], - "params": [[ - (self.category_params[0].key, self.category_param_values[2].value), - (self.category_params[1].key, self.category_param_values[0].value), - (self.category_params[2].key, self.category_param_values[1].value), - ]] - }) - with patch("store.loader.BaseLoader.load_data", return_value=fake_df): - loader = ProductLoader("fake_path") - loader.process() - - self.assertEqual(self.template.products.count(), 0) - mock_logger.exception.assert_called_with("ProductTemplateyParamValue matching query does not exist.") \ No newline at end of file diff --git a/artel/store/tests/test_models.py b/artel/store/tests/test_models.py index 6568242..10b81c3 100644 --- a/artel/store/tests/test_models.py +++ b/artel/store/tests/test_models.py @@ -15,8 +15,9 @@ class ProductCategoryParamTestCase(TestCase): def setUp(self): super().setUp() self.category = factories.ProductCategoryFactory() - self.param = factories.ProductCategoryParamFactory( - category=self.category, + self.template = factories.ProductTemplateFactory(category=self.category) + self.param = factories.ProductTemplateParamFactory( + template=self.template, param_type="int", key="test_param" ) @@ -44,11 +45,12 @@ class ProductTemplateParamValueTestCase(TestCase): def setUp(self): super().setUp() self.category = factories.ProductCategoryFactory() + self.template = factories.ProductTemplateFactory(category=self.category) def test_get_value_success(self): - param = factories.ProductCategoryParamFactory( - category=self.category, + param = factories.ProductTemplateParamFactory( + template=self.template, param_type="int", key="test_param" ) @@ -57,8 +59,8 @@ class ProductTemplateParamValueTestCase(TestCase): self.assertEqual(proper_value, 23) def test_get_value_failure_wrong_value(self): - param = factories.ProductCategoryParamFactory( - category=self.category, + param = factories.ProductTemplateParamFactory( + template=self.template, param_type="int", key="test_param" ) @@ -69,10 +71,10 @@ class ProductTemplateParamValueTestCase(TestCase): class ProductTestCase(TestCase): - def test_category_params_one_value_success(self): + def test_template_params_one_value_success(self): product = factories.ProductFactory() - param = factories.ProductCategoryParamFactory( - category=product.template.category, + param = factories.ProductTemplateParamFactory( + template=product.template, param_type="int", key="test_param" ) @@ -83,10 +85,10 @@ class ProductTestCase(TestCase): self.assertEqual(product.params.count(), 1) self.assertEqual(product.params.first().get_value(), 23) - def test_category_params_multiple_values_failure(self): + def test_template_params_multiple_values_failure(self): product = factories.ProductFactory() - param = factories.ProductCategoryParamFactory( - category=product.template.category, + param = factories.ProductTemplateParamFactory( + template=product.template, param_type="int", key="test_param" ) diff --git a/artel/store/tests/test_views.py b/artel/store/tests/test_views.py index 5883fb7..9f5edfb 100644 --- a/artel/store/tests/test_views.py +++ b/artel/store/tests/test_views.py @@ -22,13 +22,13 @@ class ConfigureProductViewTestCase(TestCase): self.product_template = ProductTemplateFactory(category=self.category) # create template params and values for those params self.param1 = ProductTemplateParam.objects.create( - key="Mocowanie", category=self.category, + key="Mocowanie", template=self.product_template, param_type=TemplateParamValueChoices.STRING ) self.param1_value1 = ProductTemplateParamValueFactory(param=self.param1) self.param1_value2 = ProductTemplateParamValueFactory(param=self.param1) self.param2 = ProductTemplateParam.objects.create( - key="Format", category=self.category, + key="Format", template=self.product_template, param_type=TemplateParamValueChoices.STRING ) self.param2_value1 = ProductTemplateParamValueFactory(param=self.param2)