diff --git a/.gitignore b/.gitignore index a91c0c66..1b3e2edb 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ package-lock.json report.xml database *.orig +*.sublime-project +*.sublime-workspace diff --git a/stroyprombeton/context/__init__.py b/stroyprombeton/context/__init__.py index fc4df825..a063d1ec 100644 --- a/stroyprombeton/context/__init__.py +++ b/stroyprombeton/context/__init__.py @@ -1 +1,2 @@ from .catalog import * +from . import options, tags diff --git a/stroyprombeton/context/catalog.py b/stroyprombeton/context/catalog.py index ec5fa92e..40eaa9dd 100644 --- a/stroyprombeton/context/catalog.py +++ b/stroyprombeton/context/catalog.py @@ -15,8 +15,7 @@ from catalog import context, typing from images.models import Image from pages import context as pages_context -from stroyprombeton import models as stb_models, request_data -from stroyprombeton.context import options +from stroyprombeton import models as stb_models, context as stb_context, request_data class TagsByOptions(context.Tags): @@ -75,13 +74,9 @@ def category(self): id=self.request_data.id ) - @property - def tags(self) -> context.Tags: - return context.Tags(stb_models.Tag.objects.all()) - def context(self) -> typing.ContextDict: tags = FilteredTags(stb_models.Tag.objects.all(), self.request_data) - options_ = options.Filtered(self.category, tags.qs(), self.request_data) + options_ = stb_context.options.Filtered(self.category, tags.qs()) if not options_.qs(): raise http.Http404('

В категории нет изделий typing.ContextDict: sliced_options.products, Image.objects.all() ) grouped_tags = context.tags.GroupedTags( - tags=TagsByOptions(self.tags, options_.qs()) + tags=TagsByOptions(stb_context.tags.All(), options_.qs()) ) page = Page(self.page, tags) category = CategoryContext(self.request_data) @@ -162,12 +157,12 @@ def __init__(self, request_data_: request_data.FetchProducts): def context(self) -> typing.ContextDict: category = CategoryContext(self.request_data) tags = FilteredTags(stb_models.Tag.objects.all(), self.request_data) - options_ = options.Sliced( + options_ = stb_context.options.Sliced( request_data_=self.request_data, - options=options.Searched( + options=stb_context.options.Searched( request_data_=self.request_data, - options=options.Filtered( - category.object(), tags.qs(), self.request_data + options=stb_context.options.Filtered( + category.object(), tags.qs(), ) ) ) diff --git a/stroyprombeton/context/options.py b/stroyprombeton/context/options.py index ca7ffb2c..c0105646 100644 --- a/stroyprombeton/context/options.py +++ b/stroyprombeton/context/options.py @@ -49,28 +49,74 @@ def context(self) -> typing.ContextDict: } -class Filtered(Options): +class All(Options): + + def __init__(self, qs: stb_models.OptionQuerySet = None): + self._qs = qs or stb_models.Option.objects.all() + + def qs(self) -> stb_models.OptionQuerySet: + return ( + self._qs + .bind_fields() + .active() + .order_by(*settings.OPTIONS_ORDERING) + ) + + +class CategoryFiltered(Options): + def __init__( self, - category: stb_models.Category, - tags: stb_models.TagQuerySet, - request_data_: request_data.Category + options: Options, + category: stb_models.Category ): + self.options = options self.category = category - self.tags = tags - self.request_data = request_data_ def qs(self) -> stb_models.OptionQuerySet: return ( - stb_models.Option.objects - .bind_fields() - .active() + self.options + .qs() .filter_descendants(self.category) + ) + + +class TagsFiltered(Options): + def __init__( + self, + options: Options, + tags: stb_models.TagQuerySet, + ): + self.options = options + self.tags = tags + + def qs(self) -> stb_models.OptionQuerySet: + return ( + self.options + .qs() .tagged_or_all(self.tags) - .order_by(*settings.OPTIONS_ORDERING) ) +class Filtered(Options): + def __init__( + self, + category: stb_models.Category, + tags: stb_models.TagQuerySet, + ): + """Filtered options by a category and tags.""" + self.filtered = TagsFiltered( + CategoryFiltered( + All(), + category, + ), + tags, + ) + + def qs(self) -> stb_models.OptionQuerySet: + return self.filtered.qs() + + class Searched(Options): LOOKUPS = [ diff --git a/stroyprombeton/context/tags.py b/stroyprombeton/context/tags.py new file mode 100644 index 00000000..af68418a --- /dev/null +++ b/stroyprombeton/context/tags.py @@ -0,0 +1,13 @@ +from catalog import context +from stroyprombeton import models as stb_models + +# @todo #744:30m Move all tags related context classes in this file. + + +class All(context.Tags): + + def __init__(self, qs: stb_models.TagQuerySet = None): + self._qs = qs or stb_models.Tag.objects.all() + + def qs(self) -> stb_models.OptionQuerySet: + return self._qs diff --git a/stroyprombeton/tests/tests_views.py b/stroyprombeton/tests/tests_views.py index 64338741..06b839a7 100644 --- a/stroyprombeton/tests/tests_views.py +++ b/stroyprombeton/tests/tests_views.py @@ -741,13 +741,36 @@ class ProductPrice(TestCase): fixtures = ['dump.json'] - def test_price_list(self): - """Context for pdf generation should include Category and Products.""" - self.response = self.client.get('/gbi/categories/1/pdf/') + def setUp(self): + self.category = models.CategoryPage.objects.filter(level=0).first() + self.response = self.client.get( + reverse('product_pdf', args=(self.category.id,)) + ) + self.context = self.response.context + + def test_content_type(self): + self.assertEqual(self.response['Content-Type'], 'application/pdf') + + def test_category_name(self): + self.assertEqual(self.context['category'].name, self.category.name) + + def test_products(self): + self.assertIsInstance(self.context['products'], models.OptionQuerySet) + self.assertGreater(self.context['products'].count(), 100) + + def test_tags_are_grouped(self): + for group, tags in self.context['group_tags_pairs']: + for tag_ in tags: + self.assertEqual(group, tag_.group) + + def test_product_tags(self): + tags = set(chain.from_iterable( + grouped_tags + for _, grouped_tags in self.context['group_tags_pairs'] + )) - self.assertTrue(self.response['Content-Type'] == 'application/pdf') - self.assertTrue(self.response.context['category'].name == CATEGORY_ROOT_NAME) - self.assertTrue(len(self.response.context['products']) > 100) + for product in self.context['products'].filter(tags__isnull=False): + self.assertTrue(tags & set(product.tags.all())) @tag('fast', 'catalog') diff --git a/stroyprombeton/views/catalog.py b/stroyprombeton/views/catalog.py index ccd77bb2..36e05cfb 100644 --- a/stroyprombeton/views/catalog.py +++ b/stroyprombeton/views/catalog.py @@ -13,6 +13,7 @@ from catalog import context from catalog.views import catalog from images.models import Image +from pages import context as pages_context from pages.models import CustomPage, ModelPage from pages.templatetags.pages_extras import breadcrumbs as get_page_breadcrumbs from stroyprombeton import context as stb_context, models, exception, request_data @@ -192,24 +193,29 @@ class ProductPDF(PDFTemplateView, DetailView): def get(self, *args, **kwargs): self.object = self.get_object() - return super(ProductPDF, self).get(*args, **kwargs) + return super().get(*args, **kwargs) def get_context_data(self, **kwargs): - context = super(ProductPDF, self).get_context_data(**kwargs) - category = context[self.context_object_name] + context_ = super().get_context_data(**kwargs) + category = context_[self.context_object_name] - products = ( - models.Product.objects - .active() - .filter_descendants(category) - # use `OPTIONS_ORDERING` instead - # .order_by(*settings.PRODUCTS_ORDERING) + options_ = stb_context.options.CategoryFiltered( + stb_context.options.All(), + category + ) + grouped_tags = context.tags.GroupedTags( + tags=stb_context.TagsByOptions( + stb_context.tags.All(), + options_.qs(), + ) ) return { - **context, + **context_, + **pages_context.Contexts([ + options_, grouped_tags, + ]).context(), 'category': category, - 'products': products, } diff --git a/templates/catalog/product_pdf_price.html b/templates/catalog/product_pdf_price.html index 32b878b7..f2cd847f 100644 --- a/templates/catalog/product_pdf_price.html +++ b/templates/catalog/product_pdf_price.html @@ -4,7 +4,6 @@ -