We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Gen categories tags to sitemap
Сейчас в sitemap выгружаются, помимо прочего, страницы категорий.
Выгружай не только страницы, но и все варианты их тегов.
Однако, не должно быть страниц, удовлетворяющих паттерну `*-or-*`

Пример страницы, которую нужно выгрузить в sitemap:
https://www.shopelectro.ru/catalog/categories/universalnye-ot-seti-12-v/tags/vanson/

Строчку `Disallow: /*-or-*` из `robots.txt` не выпиливай ни в коем случае


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `shopelectro/sitemaps.py`
Content:
```
1 from django.contrib.sitemaps import Sitemap
2 from django.urls import reverse
3 
4 from pages.models import Page
5 
6 from shopelectro.models import Product, Category
7 
8 
9 class AbstractSitemap(Sitemap):
10     protocol = 'https'
11     changefreq = 'weekly'
12     priority = 0.9
13 
14 
15 class IndexSitemap(Sitemap):
16     protocol = 'https'
17     changefreq = 'monthly'
18     priority = 1
19 
20     # items()
21     # Required. A method that returns a list of objects.
22     # https://docs.djangoproject.com/ja/1.9/ref/contrib/sitemaps/#django.contrib.sitemaps.Sitemap.items
23     def items(self):
24         return ['']
25 
26     # location()
27     # Optional. If location isn’t provided, the framework will call the get_absolute_url()
28     # method on each object as returned by items().
29     # https://docs.djangoproject.com/ja/1.9/ref/contrib/sitemaps/#django.contrib.sitemaps.Sitemap.location
30     def location(self, model):
31         return reverse(Page.CUSTOM_PAGES_URL_NAME, args=(model, ))
32 
33 
34 class CategorySitemap(AbstractSitemap):
35 
36     def items(self):
37         return Category.objects.filter(page__is_active=True)
38 
39 
40 class ProductSitemap(AbstractSitemap):
41 
42     def items(self):
43         return Product.objects.filter(page__is_active=True)
44 
45 
46 class PagesSitemap(AbstractSitemap):
47 
48     def items(self):
49         return Page.objects.filter(is_active=True)
50
```

Path: `shopelectro/urls.py`
Content:
```
1 from collections import OrderedDict
2 
3 from django.conf import settings
4 from django.conf.urls import url, include
5 from django.conf.urls.static import static
6 from django.contrib.sitemaps.views import sitemap
7 from django.views.decorators.cache import cache_page
8 
9 from pages.models import Page
10 from pages.views import robots, SitemapPage
11 
12 from shopelectro import sitemaps, config, views
13 from shopelectro.admin import se_admin
14 
15 # Orders sitemaps instances
16 sitemaps = OrderedDict([
17     ('index', sitemaps.IndexSitemap),
18     ('category', sitemaps.CategorySitemap),
19     ('products', sitemaps.ProductSitemap),
20     ('site', sitemaps.PagesSitemap)
21 ])
22 
23 # disable cache
24 if settings.DEBUG:
25     def cache_page(arg):  # Ignore PyFlakesBear
26         if callable(arg):
27             return arg
28         return cache_page
29 
30 cached_60d = cache_page(config.cached_time(days=60))
31 cached_2h = cache_page(config.cached_time(hours=2))
32 
33 admin_urls = [
34     url(r'^', se_admin.urls),
35     url(r'^autocomplete/$', views.AdminAutocomplete.as_view(), name='admin_autocomplete'),
36     url(r'^get-tree-items/$', views.Tree.as_view()),
37     url(r'^redirect-to-product/$', views.RedirectToProduct.as_view()),
38     url(r'^table-editor-api/$', views.TableEditorAPI.as_view()),
39     url(r'^select2/', include('django_select2.urls')),
40 ]
41 
42 catalog_urls = [
43     url(r'^categories/(?P<slug>[\w-]+)/$',
44         cached_2h(views.CategoryPage.as_view()), name='category'),
45     url(r'^categories/(?P<slug>[\w-]+)/tags/(?P<tags>[\w-]+)/$',
46         cached_2h(views.CategoryPage.as_view()), name='category'),
47     url(r'^categories/(?P<slug>[\w-]+)/(?P<sorting>[0-9]*)/$',
48         views.CategoryPage.as_view(), name='category'),
49     url(r'^categories/(?P<slug>[\w-]+)/(?P<sorting>[0-9]*)/tags/(?P<tags>[\w-]+)/$',
50         views.CategoryPage.as_view(), name='category'),
51     url(r'categories/(?P<category_slug>[\w-]+)/load-more/'
52         r'(?P<offset>[0-9]+)/(?P<sorting>[0-9]*)/$',
53         views.load_more, name='load_more'),
54     url(r'categories/(?P<category_slug>[\w-]+)/load-more/'
55         r'(?P<offset>[0-9]+)/(?P<sorting>[0-9]*)/tags/(?P<tags>[\w-]+)/$',
56         views.load_more, name='load_more'),
57     url(r'^no-images/$', views.ProductsWithoutImages.as_view(),
58         name='products_without_images'),
59     url(r'^no-text/$', views.ProductsWithoutText.as_view(),
60         name='products_without_text'),
61     url(r'^products/(?P<product_vendor_code>[0-9]+)/$',
62         views.ProductPage.as_view(), name='product'),
63 ]
64 
65 service_urls = [
66     url(r'^ya-kassa/aviso/$', views.yandex_aviso, name='yandex_aviso'),
67     url(r'^ya-kassa/check/$', views.yandex_check, name='yandex_check'),
68     url(r'^ya-feedback/redirect/$',
69         views.ya_feedback_with_redirect, name='ya_feedback_with_redirect'),
70     url(r'^ya-feedback/request/$',
71         views.ya_feedback_request, name='ya_feedback_request'),
72 ]
73 
74 search_urls = [
75     url(r'^autocomplete/$', views.Autocomplete.as_view(), name='autocomplete'),
76 ]
77 
78 ecommerce_urls = [
79     url(r'^cart-add/$', views.AddToCart.as_view(), name='cart_add'),
80     url(r'^cart-change/$', views.ChangeCount.as_view(), name='cart_set_count'),
81     url(r'^cart-flush/$', views.FlushCart.as_view(), name='cart_flush'),
82     url(r'^cart-remove/$', views.RemoveFromCart.as_view(), name='cart_remove'),
83     url(r'^order-call/$', views.order_call),
84     url(r'^one-click-buy/$', views.one_click_buy),
85     url(r'^yandex-order/$', views.YandexOrder.as_view()),
86     url(r'', include('ecommerce.urls')),
87 ]
88 
89 url_name = Page.CUSTOM_PAGES_URL_NAME
90 custom_pages = [
91     url(r'^(?P<page>)$', cached_2h(views.IndexPage.as_view()), name=url_name),
92     url(r'^(?P<page>search)/$', views.Search.as_view(), name=url_name),
93     url(r'^(?P<page>catalog)/$', cached_2h(views.CategoryTree.as_view()), name=url_name),
94     url(r'^shop/(?P<page>order)/$', views.OrderPage.as_view(), name=url_name),
95     url(r'^shop/(?P<page>order-success)/$', views.OrderSuccess.as_view(), name=url_name),
96     url(r'^(?P<page>sitemap)/$', SitemapPage.as_view(), name=url_name),
97 ]
98 
99 urlpatterns = [
100     url('', include(custom_pages)),
101     url(r'^admin/', include(admin_urls)),
102     url(r'^catalog/', include(catalog_urls)),
103     url(r'^pages/', include('pages.urls')),
104     url(r'^robots\.txt$', robots),
105     url(r'^save-feedback/$', views.save_feedback),
106     url(r'^delete-feedback/$', views.delete_feedback),
107     url(r'^set-view-type/$', views.set_view_type, name='set_view_type'),
108     url(r'^shop/', include(ecommerce_urls)),
109     url(r'^search/', include(search_urls)),
110     url(r'^service/', include(service_urls)),
111     url(r'^sitemap\.xml$', cached_60d(sitemap), {'sitemaps': sitemaps}, name='sitemap'),
112 ]
113 
114 if settings.DEBUG:
115     import debug_toolbar
116 
117     urlpatterns += [
118         url(r'^__debug__/', include(debug_toolbar.urls)),
119         *static(settings.STATIC_URL, document_root=settings.STATIC_ROOT),
120         *static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
121     ]
122
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/shopelectro/sitemaps.py b/shopelectro/sitemaps.py
--- a/shopelectro/sitemaps.py
+++ b/shopelectro/sitemaps.py
@@ -1,9 +1,11 @@
+from typing import Generator, Tuple
+
 from django.contrib.sitemaps import Sitemap
 from django.urls import reverse
 
 from pages.models import Page
 
-from shopelectro.models import Product, Category
+from shopelectro.models import Category, Product, TagGroup, Tag
 
 
 class AbstractSitemap(Sitemap):
@@ -37,6 +39,39 @@
         return Category.objects.filter(page__is_active=True)
 
 
+def get_categories_with_tags() -> Generator[
+    Tuple[Category, Tuple[TagGroup, Tag]], None, None
+]:
+    """
+    Return all unique Category+TagGroup pairs.
+
+    Currently, tags per category is limited to 1 tag (by SEO requirements).
+    So, for each tags group in each category we'll get 1 tag.
+    """
+    for category in Category.objects.filter(page__is_active=True):
+        products = Product.objects.get_by_category(category)
+        tags = Tag.objects.filter(products__in=products).distinct()
+        for group_name, group_tags in tags.get_group_tags_pairs():
+            for group_tag in group_tags:
+                yield category, (group_name, [group_tag])
+
+
+class CategoryWithTagsSitemap(AbstractSitemap):
+
+    def items(self):
+        # `items` method can't return generator (by django design)
+        # so we moved items collection code to dedicated function
+        return list(get_categories_with_tags())
+
+    def location(self, item):
+        category, tags = item
+        tags_slug = Tag.serialize_url_tags([tags])
+        return reverse('category', kwargs={
+            'slug': category.page.slug,
+            'tags': tags_slug,
+        })
+
+
 class ProductSitemap(AbstractSitemap):
 
     def items(self):
diff --git a/shopelectro/urls.py b/shopelectro/urls.py
--- a/shopelectro/urls.py
+++ b/shopelectro/urls.py
@@ -16,6 +16,7 @@
 sitemaps = OrderedDict([
     ('index', sitemaps.IndexSitemap),
     ('category', sitemaps.CategorySitemap),
+    ('category-with-tags', sitemaps.CategoryWithTagsSitemap),
     ('products', sitemaps.ProductSitemap),
     ('site', sitemaps.PagesSitemap)
 ])

{"golden_diff": "diff --git a/shopelectro/sitemaps.py b/shopelectro/sitemaps.py\n--- a/shopelectro/sitemaps.py\n+++ b/shopelectro/sitemaps.py\n@@ -1,9 +1,11 @@\n+from typing import Generator, Tuple\n+\n from django.contrib.sitemaps import Sitemap\n from django.urls import reverse\n \n from pages.models import Page\n \n-from shopelectro.models import Product, Category\n+from shopelectro.models import Category, Product, TagGroup, Tag\n \n \n class AbstractSitemap(Sitemap):\n@@ -37,6 +39,39 @@\n         return Category.objects.filter(page__is_active=True)\n \n \n+def get_categories_with_tags() -> Generator[\n+    Tuple[Category, Tuple[TagGroup, Tag]], None, None\n+]:\n+    \"\"\"\n+    Return all unique Category+TagGroup pairs.\n+\n+    Currently, tags per category is limited to 1 tag (by SEO requirements).\n+    So, for each tags group in each category we'll get 1 tag.\n+    \"\"\"\n+    for category in Category.objects.filter(page__is_active=True):\n+        products = Product.objects.get_by_category(category)\n+        tags = Tag.objects.filter(products__in=products).distinct()\n+        for group_name, group_tags in tags.get_group_tags_pairs():\n+            for group_tag in group_tags:\n+                yield category, (group_name, [group_tag])\n+\n+\n+class CategoryWithTagsSitemap(AbstractSitemap):\n+\n+    def items(self):\n+        # `items` method can't return generator (by django design)\n+        # so we moved items collection code to dedicated function\n+        return list(get_categories_with_tags())\n+\n+    def location(self, item):\n+        category, tags = item\n+        tags_slug = Tag.serialize_url_tags([tags])\n+        return reverse('category', kwargs={\n+            'slug': category.page.slug,\n+            'tags': tags_slug,\n+        })\n+\n+\n class ProductSitemap(AbstractSitemap):\n \n     def items(self):\ndiff --git a/shopelectro/urls.py b/shopelectro/urls.py\n--- a/shopelectro/urls.py\n+++ b/shopelectro/urls.py\n@@ -16,6 +16,7 @@\n sitemaps = OrderedDict([\n     ('index', sitemaps.IndexSitemap),\n     ('category', sitemaps.CategorySitemap),\n+    ('category-with-tags', sitemaps.CategoryWithTagsSitemap),\n     ('products', sitemaps.ProductSitemap),\n     ('site', sitemaps.PagesSitemap)\n ])\n", "issue": "Gen categories tags to sitemap\n\u0421\u0435\u0439\u0447\u0430\u0441 \u0432 sitemap \u0432\u044b\u0433\u0440\u0443\u0436\u0430\u044e\u0442\u0441\u044f, \u043f\u043e\u043c\u0438\u043c\u043e \u043f\u0440\u043e\u0447\u0435\u0433\u043e, \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439.\r\n\u0412\u044b\u0433\u0440\u0443\u0436\u0430\u0439 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043d\u043e \u0438 \u0432\u0441\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0438\u0445 \u0442\u0435\u0433\u043e\u0432.\r\n\u041e\u0434\u043d\u0430\u043a\u043e, \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446, \u0443\u0434\u043e\u0432\u043b\u0435\u0442\u0432\u043e\u0440\u044f\u044e\u0449\u0438\u0445 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0443 `*-or-*`\r\n\r\n\u041f\u0440\u0438\u043c\u0435\u0440 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0432 sitemap:\r\nhttps://www.shopelectro.ru/catalog/categories/universalnye-ot-seti-12-v/tags/vanson/\r\n\r\n\u0421\u0442\u0440\u043e\u0447\u043a\u0443 `Disallow: /*-or-*` \u0438\u0437 `robots.txt` \u043d\u0435 \u0432\u044b\u043f\u0438\u043b\u0438\u0432\u0430\u0439 \u043d\u0438 \u0432 \u043a\u043e\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435\r\n\n", "before_files": [{"content": "from django.contrib.sitemaps import Sitemap\nfrom django.urls import reverse\n\nfrom pages.models import Page\n\nfrom shopelectro.models import Product, Category\n\n\nclass AbstractSitemap(Sitemap):\n    protocol = 'https'\n    changefreq = 'weekly'\n    priority = 0.9\n\n\nclass IndexSitemap(Sitemap):\n    protocol = 'https'\n    changefreq = 'monthly'\n    priority = 1\n\n    # items()\n    # Required. A method that returns a list of objects.\n    # https://docs.djangoproject.com/ja/1.9/ref/contrib/sitemaps/#django.contrib.sitemaps.Sitemap.items\n    def items(self):\n        return ['']\n\n    # location()\n    # Optional. If location isn\u2019t provided, the framework will call the get_absolute_url()\n    # method on each object as returned by items().\n    # https://docs.djangoproject.com/ja/1.9/ref/contrib/sitemaps/#django.contrib.sitemaps.Sitemap.location\n    def location(self, model):\n        return reverse(Page.CUSTOM_PAGES_URL_NAME, args=(model, ))\n\n\nclass CategorySitemap(AbstractSitemap):\n\n    def items(self):\n        return Category.objects.filter(page__is_active=True)\n\n\nclass ProductSitemap(AbstractSitemap):\n\n    def items(self):\n        return Product.objects.filter(page__is_active=True)\n\n\nclass PagesSitemap(AbstractSitemap):\n\n    def items(self):\n        return Page.objects.filter(is_active=True)\n", "path": "shopelectro/sitemaps.py"}, {"content": "from collections import OrderedDict\n\nfrom django.conf import settings\nfrom django.conf.urls import url, include\nfrom django.conf.urls.static import static\nfrom django.contrib.sitemaps.views import sitemap\nfrom django.views.decorators.cache import cache_page\n\nfrom pages.models import Page\nfrom pages.views import robots, SitemapPage\n\nfrom shopelectro import sitemaps, config, views\nfrom shopelectro.admin import se_admin\n\n# Orders sitemaps instances\nsitemaps = OrderedDict([\n    ('index', sitemaps.IndexSitemap),\n    ('category', sitemaps.CategorySitemap),\n    ('products', sitemaps.ProductSitemap),\n    ('site', sitemaps.PagesSitemap)\n])\n\n# disable cache\nif settings.DEBUG:\n    def cache_page(arg):  # Ignore PyFlakesBear\n        if callable(arg):\n            return arg\n        return cache_page\n\ncached_60d = cache_page(config.cached_time(days=60))\ncached_2h = cache_page(config.cached_time(hours=2))\n\nadmin_urls = [\n    url(r'^', se_admin.urls),\n    url(r'^autocomplete/$', views.AdminAutocomplete.as_view(), name='admin_autocomplete'),\n    url(r'^get-tree-items/$', views.Tree.as_view()),\n    url(r'^redirect-to-product/$', views.RedirectToProduct.as_view()),\n    url(r'^table-editor-api/$', views.TableEditorAPI.as_view()),\n    url(r'^select2/', include('django_select2.urls')),\n]\n\ncatalog_urls = [\n    url(r'^categories/(?P<slug>[\\w-]+)/$',\n        cached_2h(views.CategoryPage.as_view()), name='category'),\n    url(r'^categories/(?P<slug>[\\w-]+)/tags/(?P<tags>[\\w-]+)/$',\n        cached_2h(views.CategoryPage.as_view()), name='category'),\n    url(r'^categories/(?P<slug>[\\w-]+)/(?P<sorting>[0-9]*)/$',\n        views.CategoryPage.as_view(), name='category'),\n    url(r'^categories/(?P<slug>[\\w-]+)/(?P<sorting>[0-9]*)/tags/(?P<tags>[\\w-]+)/$',\n        views.CategoryPage.as_view(), name='category'),\n    url(r'categories/(?P<category_slug>[\\w-]+)/load-more/'\n        r'(?P<offset>[0-9]+)/(?P<sorting>[0-9]*)/$',\n        views.load_more, name='load_more'),\n    url(r'categories/(?P<category_slug>[\\w-]+)/load-more/'\n        r'(?P<offset>[0-9]+)/(?P<sorting>[0-9]*)/tags/(?P<tags>[\\w-]+)/$',\n        views.load_more, name='load_more'),\n    url(r'^no-images/$', views.ProductsWithoutImages.as_view(),\n        name='products_without_images'),\n    url(r'^no-text/$', views.ProductsWithoutText.as_view(),\n        name='products_without_text'),\n    url(r'^products/(?P<product_vendor_code>[0-9]+)/$',\n        views.ProductPage.as_view(), name='product'),\n]\n\nservice_urls = [\n    url(r'^ya-kassa/aviso/$', views.yandex_aviso, name='yandex_aviso'),\n    url(r'^ya-kassa/check/$', views.yandex_check, name='yandex_check'),\n    url(r'^ya-feedback/redirect/$',\n        views.ya_feedback_with_redirect, name='ya_feedback_with_redirect'),\n    url(r'^ya-feedback/request/$',\n        views.ya_feedback_request, name='ya_feedback_request'),\n]\n\nsearch_urls = [\n    url(r'^autocomplete/$', views.Autocomplete.as_view(), name='autocomplete'),\n]\n\necommerce_urls = [\n    url(r'^cart-add/$', views.AddToCart.as_view(), name='cart_add'),\n    url(r'^cart-change/$', views.ChangeCount.as_view(), name='cart_set_count'),\n    url(r'^cart-flush/$', views.FlushCart.as_view(), name='cart_flush'),\n    url(r'^cart-remove/$', views.RemoveFromCart.as_view(), name='cart_remove'),\n    url(r'^order-call/$', views.order_call),\n    url(r'^one-click-buy/$', views.one_click_buy),\n    url(r'^yandex-order/$', views.YandexOrder.as_view()),\n    url(r'', include('ecommerce.urls')),\n]\n\nurl_name = Page.CUSTOM_PAGES_URL_NAME\ncustom_pages = [\n    url(r'^(?P<page>)$', cached_2h(views.IndexPage.as_view()), name=url_name),\n    url(r'^(?P<page>search)/$', views.Search.as_view(), name=url_name),\n    url(r'^(?P<page>catalog)/$', cached_2h(views.CategoryTree.as_view()), name=url_name),\n    url(r'^shop/(?P<page>order)/$', views.OrderPage.as_view(), name=url_name),\n    url(r'^shop/(?P<page>order-success)/$', views.OrderSuccess.as_view(), name=url_name),\n    url(r'^(?P<page>sitemap)/$', SitemapPage.as_view(), name=url_name),\n]\n\nurlpatterns = [\n    url('', include(custom_pages)),\n    url(r'^admin/', include(admin_urls)),\n    url(r'^catalog/', include(catalog_urls)),\n    url(r'^pages/', include('pages.urls')),\n    url(r'^robots\\.txt$', robots),\n    url(r'^save-feedback/$', views.save_feedback),\n    url(r'^delete-feedback/$', views.delete_feedback),\n    url(r'^set-view-type/$', views.set_view_type, name='set_view_type'),\n    url(r'^shop/', include(ecommerce_urls)),\n    url(r'^search/', include(search_urls)),\n    url(r'^service/', include(service_urls)),\n    url(r'^sitemap\\.xml$', cached_60d(sitemap), {'sitemaps': sitemaps}, name='sitemap'),\n]\n\nif settings.DEBUG:\n    import debug_toolbar\n\n    urlpatterns += [\n        url(r'^__debug__/', include(debug_toolbar.urls)),\n        *static(settings.STATIC_URL, document_root=settings.STATIC_ROOT),\n        *static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),\n    ]\n", "path": "shopelectro/urls.py"}], "after_files": [{"content": "from typing import Generator, Tuple\n\nfrom django.contrib.sitemaps import Sitemap\nfrom django.urls import reverse\n\nfrom pages.models import Page\n\nfrom shopelectro.models import Category, Product, TagGroup, Tag\n\n\nclass AbstractSitemap(Sitemap):\n    protocol = 'https'\n    changefreq = 'weekly'\n    priority = 0.9\n\n\nclass IndexSitemap(Sitemap):\n    protocol = 'https'\n    changefreq = 'monthly'\n    priority = 1\n\n    # items()\n    # Required. A method that returns a list of objects.\n    # https://docs.djangoproject.com/ja/1.9/ref/contrib/sitemaps/#django.contrib.sitemaps.Sitemap.items\n    def items(self):\n        return ['']\n\n    # location()\n    # Optional. If location isn\u2019t provided, the framework will call the get_absolute_url()\n    # method on each object as returned by items().\n    # https://docs.djangoproject.com/ja/1.9/ref/contrib/sitemaps/#django.contrib.sitemaps.Sitemap.location\n    def location(self, model):\n        return reverse(Page.CUSTOM_PAGES_URL_NAME, args=(model, ))\n\n\nclass CategorySitemap(AbstractSitemap):\n\n    def items(self):\n        return Category.objects.filter(page__is_active=True)\n\n\ndef get_categories_with_tags() -> Generator[\n    Tuple[Category, Tuple[TagGroup, Tag]], None, None\n]:\n    \"\"\"\n    Return all unique Category+TagGroup pairs.\n\n    Currently, tags per category is limited to 1 tag (by SEO requirements).\n    So, for each tags group in each category we'll get 1 tag.\n    \"\"\"\n    for category in Category.objects.filter(page__is_active=True):\n        products = Product.objects.get_by_category(category)\n        tags = Tag.objects.filter(products__in=products).distinct()\n        for group_name, group_tags in tags.get_group_tags_pairs():\n            for group_tag in group_tags:\n                yield category, (group_name, [group_tag])\n\n\nclass CategoryWithTagsSitemap(AbstractSitemap):\n\n    def items(self):\n        # `items` method can't return generator (by django design)\n        # so we moved items collection code to dedicated function\n        return list(get_categories_with_tags())\n\n    def location(self, item):\n        category, tags = item\n        tags_slug = Tag.serialize_url_tags([tags])\n        return reverse('category', kwargs={\n            'slug': category.page.slug,\n            'tags': tags_slug,\n        })\n\n\nclass ProductSitemap(AbstractSitemap):\n\n    def items(self):\n        return Product.objects.filter(page__is_active=True)\n\n\nclass PagesSitemap(AbstractSitemap):\n\n    def items(self):\n        return Page.objects.filter(is_active=True)\n", "path": "shopelectro/sitemaps.py"}, {"content": "from collections import OrderedDict\n\nfrom django.conf import settings\nfrom django.conf.urls import url, include\nfrom django.conf.urls.static import static\nfrom django.contrib.sitemaps.views import sitemap\nfrom django.views.decorators.cache import cache_page\n\nfrom pages.models import Page\nfrom pages.views import robots, SitemapPage\n\nfrom shopelectro import sitemaps, config, views\nfrom shopelectro.admin import se_admin\n\n# Orders sitemaps instances\nsitemaps = OrderedDict([\n    ('index', sitemaps.IndexSitemap),\n    ('category', sitemaps.CategorySitemap),\n    ('category-with-tags', sitemaps.CategoryWithTagsSitemap),\n    ('products', sitemaps.ProductSitemap),\n    ('site', sitemaps.PagesSitemap)\n])\n\n# disable cache\nif settings.DEBUG:\n    def cache_page(arg):  # Ignore PyFlakesBear\n        if callable(arg):\n            return arg\n        return cache_page\n\ncached_60d = cache_page(config.cached_time(days=60))\ncached_2h = cache_page(config.cached_time(hours=2))\n\nadmin_urls = [\n    url(r'^', se_admin.urls),\n    url(r'^autocomplete/$', views.AdminAutocomplete.as_view(), name='admin_autocomplete'),\n    url(r'^get-tree-items/$', views.Tree.as_view()),\n    url(r'^redirect-to-product/$', views.RedirectToProduct.as_view()),\n    url(r'^table-editor-api/$', views.TableEditorAPI.as_view()),\n    url(r'^select2/', include('django_select2.urls')),\n]\n\ncatalog_urls = [\n    url(r'^categories/(?P<slug>[\\w-]+)/$',\n        cached_2h(views.CategoryPage.as_view()), name='category'),\n    url(r'^categories/(?P<slug>[\\w-]+)/tags/(?P<tags>[\\w-]+)/$',\n        cached_2h(views.CategoryPage.as_view()), name='category'),\n    url(r'^categories/(?P<slug>[\\w-]+)/(?P<sorting>[0-9]*)/$',\n        views.CategoryPage.as_view(), name='category'),\n    url(r'^categories/(?P<slug>[\\w-]+)/(?P<sorting>[0-9]*)/tags/(?P<tags>[\\w-]+)/$',\n        views.CategoryPage.as_view(), name='category'),\n    url(r'categories/(?P<category_slug>[\\w-]+)/load-more/'\n        r'(?P<offset>[0-9]+)/(?P<sorting>[0-9]*)/$',\n        views.load_more, name='load_more'),\n    url(r'categories/(?P<category_slug>[\\w-]+)/load-more/'\n        r'(?P<offset>[0-9]+)/(?P<sorting>[0-9]*)/tags/(?P<tags>[\\w-]+)/$',\n        views.load_more, name='load_more'),\n    url(r'^no-images/$', views.ProductsWithoutImages.as_view(),\n        name='products_without_images'),\n    url(r'^no-text/$', views.ProductsWithoutText.as_view(),\n        name='products_without_text'),\n    url(r'^products/(?P<product_vendor_code>[0-9]+)/$',\n        views.ProductPage.as_view(), name='product'),\n]\n\nservice_urls = [\n    url(r'^ya-kassa/aviso/$', views.yandex_aviso, name='yandex_aviso'),\n    url(r'^ya-kassa/check/$', views.yandex_check, name='yandex_check'),\n    url(r'^ya-feedback/redirect/$',\n        views.ya_feedback_with_redirect, name='ya_feedback_with_redirect'),\n    url(r'^ya-feedback/request/$',\n        views.ya_feedback_request, name='ya_feedback_request'),\n]\n\nsearch_urls = [\n    url(r'^autocomplete/$', views.Autocomplete.as_view(), name='autocomplete'),\n]\n\necommerce_urls = [\n    url(r'^cart-add/$', views.AddToCart.as_view(), name='cart_add'),\n    url(r'^cart-change/$', views.ChangeCount.as_view(), name='cart_set_count'),\n    url(r'^cart-flush/$', views.FlushCart.as_view(), name='cart_flush'),\n    url(r'^cart-remove/$', views.RemoveFromCart.as_view(), name='cart_remove'),\n    url(r'^order-call/$', views.order_call),\n    url(r'^one-click-buy/$', views.one_click_buy),\n    url(r'^yandex-order/$', views.YandexOrder.as_view()),\n    url(r'', include('ecommerce.urls')),\n]\n\nurl_name = Page.CUSTOM_PAGES_URL_NAME\ncustom_pages = [\n    url(r'^(?P<page>)$', cached_2h(views.IndexPage.as_view()), name=url_name),\n    url(r'^(?P<page>search)/$', views.Search.as_view(), name=url_name),\n    url(r'^(?P<page>catalog)/$', cached_2h(views.CategoryTree.as_view()), name=url_name),\n    url(r'^shop/(?P<page>order)/$', views.OrderPage.as_view(), name=url_name),\n    url(r'^shop/(?P<page>order-success)/$', views.OrderSuccess.as_view(), name=url_name),\n    url(r'^(?P<page>sitemap)/$', SitemapPage.as_view(), name=url_name),\n]\n\nurlpatterns = [\n    url('', include(custom_pages)),\n    url(r'^admin/', include(admin_urls)),\n    url(r'^catalog/', include(catalog_urls)),\n    url(r'^pages/', include('pages.urls')),\n    url(r'^robots\\.txt$', robots),\n    url(r'^save-feedback/$', views.save_feedback),\n    url(r'^delete-feedback/$', views.delete_feedback),\n    url(r'^set-view-type/$', views.set_view_type, name='set_view_type'),\n    url(r'^shop/', include(ecommerce_urls)),\n    url(r'^search/', include(search_urls)),\n    url(r'^service/', include(service_urls)),\n    url(r'^sitemap\\.xml$', cached_60d(sitemap), {'sitemaps': sitemaps}, name='sitemap'),\n]\n\nif settings.DEBUG:\n    import debug_toolbar\n\n    urlpatterns += [\n        url(r'^__debug__/', include(debug_toolbar.urls)),\n        *static(settings.STATIC_URL, document_root=settings.STATIC_ROOT),\n        *static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),\n    ]\n", "path": "shopelectro/urls.py"}]}