prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  dc-volumes.yml:27-28: Repair npm container reusing at...
  The puzzle `449-27d8c19e` from #449 has to be resolved:
  
  https://github.com/fidals/shopelectro/blob/f8ce85246c00411fcb21d025b0fb42abab81c7d5/docker/dc-volumes.yml#L27-L28
  
  The puzzle was created by duker33 on 28-Jul-18. 
  
  Estimate: 60 minutes,     
  
  If you have any technical questions, don't ask me, submit new tickets instead. The task will be "done" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html).
  В Firefox не поставить оценку товару
  Не нажимаются звездочки, когда хочешь оставить отзыв о товаре
  
  Firefox 58.0.2, чистый профиль. В Chromium работают
  
  --- 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/management/commands/_update_catalog/utils.py`
  Content:
  ```
  1 import glob
  2 import logging
  3 import math
  4 import os
  5 import shutil
  6 import subprocess
  7 import time
  8 from contextlib import contextmanager
  9 from itertools import chain
  10 from typing import Iterator, Dict
  11 from uuid import UUID
  12 from xml.etree import ElementTree
  13 
  14 import requests
  15 from django.conf import settings
  16 
  17 from shopelectro.exception import DownloadFilesError
  18 
  19 logger = logging.getLogger(__name__)
  20 DOWNLOAD_FILES_TIMEOUT = 15.0
  21 UUID_TYPE = str
  22 Data = Dict[str, Dict[str, dict]]
  23 NOT_SAVE_TEMPLATE = '{entity} with name="{name}" has no {field}. It\'ll not be' \
  24                     ' saved'
  25 
  26 
  27 def floor(x: float, precision=0) -> float:
  28     """
  29     The same behaviour as `math.floor`, but with precision.
  30 
  31     >>> floor(1.234, precision=2)  # result: 1.23
  32     """
  33     k = 10**precision
  34     return math.floor(x * k) / k
  35 
  36 
  37 def is_correct_uuid(uuid_):
  38     try:
  39         val = UUID(uuid_)
  40     except (ValueError, TypeError):
  41         return False
  42     return str(val) == uuid_
  43 
  44 
  45 class XmlFile:
  46 
  47     namespace = '{urn:1C.ru:commerceml_2}'
  48 
  49     def __init__(self, fetch_callback, xml_path_pattern, xpath_queries,
  50                  extra_options=None):
  51         self.fetch_callback = fetch_callback
  52         self.xml_path_pattern = xml_path_pattern
  53         self.xpath_queries = xpath_queries
  54         self.extra_options = extra_options or {}
  55 
  56     @property
  57     def parsed_files(self):
  58         """Get parsed xml files, that matched the path pattern."""
  59         xml_files = glob.glob(os.path.join(
  60             settings.ASSETS_DIR, self.xml_path_pattern
  61         ))
  62         assert xml_files, 'Files on path {} does not exist.'.format(
  63             self.xml_path_pattern
  64         )
  65         return [ElementTree.parse(file) for file in xml_files]
  66 
  67     @property
  68     def xpaths(self):
  69         """Get xpath queries for xml."""
  70         return {
  71             name: query.format(self.namespace)
  72             for name, query in self.xpath_queries.items()
  73         }
  74 
  75     def get_data(self) -> Iterator:
  76         """
  77         Get data from xml files.
  78 
  79         Example files with products names or prices.
  80         """
  81         return chain.from_iterable(
  82             self.fetch_callback(file, self)
  83             for file in self.parsed_files
  84         )
  85 
  86 
  87 @contextmanager
  88 def collect_errors(error_types: tuple):
  89     errors = []
  90 
  91     @contextmanager
  92     def collect():
  93         try:
  94             yield
  95         except error_types as error:
  96             errors.append(error)
  97     yield collect
  98     if errors:
  99         raise errors[0]
  100 
  101 
  102 @contextmanager
  103 def download_catalog(destination):
  104     """Download catalog's xml files and delete after handle them."""
  105     wget_command = (
  106         'wget -r -P {} ftp://{}:{}@{}/webdata/'
  107         ' 2>&1 | grep "время\|time\|Downloaded"'.format(
  108             destination,
  109             settings.FTP_USER,
  110             settings.FTP_PASS,
  111             settings.FTP_IP,
  112         )
  113     )
  114 
  115     try:
  116         subprocess.run(wget_command, timeout=DOWNLOAD_FILES_TIMEOUT, shell=True)
  117     except subprocess.TimeoutExpired as e:
  118         raise DownloadFilesError(str(e))
  119 
  120     assert os.path.exists(os.path.join(
  121         destination, settings.FTP_IP)), 'Files do not downloaded...'
  122     logger.info('Download catalog - completed...')
  123 
  124     try:
  125         yield
  126     finally:
  127         # remove downloaded data
  128         shutil.rmtree(os.path.join(destination, settings.FTP_IP))
  129 
  130 
  131 def report(error):
  132     report_url = getattr(settings, 'SLACK_REPORT_URL', None)
  133     if report_url is not None:
  134         requests.post(
  135             url=report_url,
  136             json={
  137                 'text': '*Не удалось обновить каталог Shopelectro.*\n'
  138                         '*Время*: {}\n'
  139                         '*Ошибка*: {}'.format(time.ctime(), error),
  140             }
  141         )
  142
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/shopelectro/management/commands/_update_catalog/utils.py b/shopelectro/management/commands/_update_catalog/utils.py
  --- a/shopelectro/management/commands/_update_catalog/utils.py
  +++ b/shopelectro/management/commands/_update_catalog/utils.py
  @@ -17,7 +17,7 @@
   from shopelectro.exception import DownloadFilesError
   
   logger = logging.getLogger(__name__)
  -DOWNLOAD_FILES_TIMEOUT = 15.0
  +DOWNLOAD_FILES_TIMEOUT = 40.0
   UUID_TYPE = str
   Data = Dict[str, Dict[str, dict]]
   NOT_SAVE_TEMPLATE = '{entity} with name="{name}" has no {field}. It\'ll not be' \
  
verification_info: {"golden_diff": "diff --git a/shopelectro/management/commands/_update_catalog/utils.py b/shopelectro/management/commands/_update_catalog/utils.py\n--- a/shopelectro/management/commands/_update_catalog/utils.py\n+++ b/shopelectro/management/commands/_update_catalog/utils.py\n@@ -17,7 +17,7 @@\n from shopelectro.exception import DownloadFilesError\n \n logger = logging.getLogger(__name__)\n-DOWNLOAD_FILES_TIMEOUT = 15.0\n+DOWNLOAD_FILES_TIMEOUT = 40.0\n UUID_TYPE = str\n Data = Dict[str, Dict[str, dict]]\n NOT_SAVE_TEMPLATE = '{entity} with name=\"{name}\" has no {field}. It\\'ll not be' \\\n", "issue": "dc-volumes.yml:27-28: Repair npm container reusing at...\nThe puzzle `449-27d8c19e` from #449 has to be resolved:\n\nhttps://github.com/fidals/shopelectro/blob/f8ce85246c00411fcb21d025b0fb42abab81c7d5/docker/dc-volumes.yml#L27-L28\n\nThe puzzle was created by duker33 on 28-Jul-18. \n\nEstimate: 60 minutes,     \n\nIf you have any technical questions, don't ask me, submit new tickets instead. The task will be \"done\" when the problem is fixed and the text of the puzzle is _removed_ from the source code. Here is more about [PDD](http://www.yegor256.com/2009/03/04/pdd.html) and [about me](http://www.yegor256.com/2017/04/05/pdd-in-action.html).\n\u0412 Firefox \u043d\u0435 \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043e\u0446\u0435\u043d\u043a\u0443 \u0442\u043e\u0432\u0430\u0440\u0443\n\u041d\u0435 \u043d\u0430\u0436\u0438\u043c\u0430\u044e\u0442\u0441\u044f \u0437\u0432\u0435\u0437\u0434\u043e\u0447\u043a\u0438, \u043a\u043e\u0433\u0434\u0430 \u0445\u043e\u0447\u0435\u0448\u044c \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043e\u0442\u0437\u044b\u0432 \u043e \u0442\u043e\u0432\u0430\u0440\u0435\r\n\r\nFirefox 58.0.2, \u0447\u0438\u0441\u0442\u044b\u0439 \u043f\u0440\u043e\u0444\u0438\u043b\u044c. \u0412 Chromium \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442\n", "before_files": [{"content": "import glob\nimport logging\nimport math\nimport os\nimport shutil\nimport subprocess\nimport time\nfrom contextlib import contextmanager\nfrom itertools import chain\nfrom typing import Iterator, Dict\nfrom uuid import UUID\nfrom xml.etree import ElementTree\n\nimport requests\nfrom django.conf import settings\n\nfrom shopelectro.exception import DownloadFilesError\n\nlogger = logging.getLogger(__name__)\nDOWNLOAD_FILES_TIMEOUT = 15.0\nUUID_TYPE = str\nData = Dict[str, Dict[str, dict]]\nNOT_SAVE_TEMPLATE = '{entity} with name=\"{name}\" has no {field}. It\\'ll not be' \\\n                    ' saved'\n\n\ndef floor(x: float, precision=0) -> float:\n    \"\"\"\n    The same behaviour as `math.floor`, but with precision.\n\n    >>> floor(1.234, precision=2)  # result: 1.23\n    \"\"\"\n    k = 10**precision\n    return math.floor(x * k) / k\n\n\ndef is_correct_uuid(uuid_):\n    try:\n        val = UUID(uuid_)\n    except (ValueError, TypeError):\n        return False\n    return str(val) == uuid_\n\n\nclass XmlFile:\n\n    namespace = '{urn:1C.ru:commerceml_2}'\n\n    def __init__(self, fetch_callback, xml_path_pattern, xpath_queries,\n                 extra_options=None):\n        self.fetch_callback = fetch_callback\n        self.xml_path_pattern = xml_path_pattern\n        self.xpath_queries = xpath_queries\n        self.extra_options = extra_options or {}\n\n    @property\n    def parsed_files(self):\n        \"\"\"Get parsed xml files, that matched the path pattern.\"\"\"\n        xml_files = glob.glob(os.path.join(\n            settings.ASSETS_DIR, self.xml_path_pattern\n        ))\n        assert xml_files, 'Files on path {} does not exist.'.format(\n            self.xml_path_pattern\n        )\n        return [ElementTree.parse(file) for file in xml_files]\n\n    @property\n    def xpaths(self):\n        \"\"\"Get xpath queries for xml.\"\"\"\n        return {\n            name: query.format(self.namespace)\n            for name, query in self.xpath_queries.items()\n        }\n\n    def get_data(self) -> Iterator:\n        \"\"\"\n        Get data from xml files.\n\n        Example files with products names or prices.\n        \"\"\"\n        return chain.from_iterable(\n            self.fetch_callback(file, self)\n            for file in self.parsed_files\n        )\n\n\n@contextmanager\ndef collect_errors(error_types: tuple):\n    errors = []\n\n    @contextmanager\n    def collect():\n        try:\n            yield\n        except error_types as error:\n            errors.append(error)\n    yield collect\n    if errors:\n        raise errors[0]\n\n\n@contextmanager\ndef download_catalog(destination):\n    \"\"\"Download catalog's xml files and delete after handle them.\"\"\"\n    wget_command = (\n        'wget -r -P {} ftp://{}:{}@{}/webdata/'\n        ' 2>&1 | grep \"\u0432\u0440\u0435\u043c\u044f\\|time\\|Downloaded\"'.format(\n            destination,\n            settings.FTP_USER,\n            settings.FTP_PASS,\n            settings.FTP_IP,\n        )\n    )\n\n    try:\n        subprocess.run(wget_command, timeout=DOWNLOAD_FILES_TIMEOUT, shell=True)\n    except subprocess.TimeoutExpired as e:\n        raise DownloadFilesError(str(e))\n\n    assert os.path.exists(os.path.join(\n        destination, settings.FTP_IP)), 'Files do not downloaded...'\n    logger.info('Download catalog - completed...')\n\n    try:\n        yield\n    finally:\n        # remove downloaded data\n        shutil.rmtree(os.path.join(destination, settings.FTP_IP))\n\n\ndef report(error):\n    report_url = getattr(settings, 'SLACK_REPORT_URL', None)\n    if report_url is not None:\n        requests.post(\n            url=report_url,\n            json={\n                'text': '*\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u0430\u0442\u0430\u043b\u043e\u0433 Shopelectro.*\\n'\n                        '*\u0412\u0440\u0435\u043c\u044f*: {}\\n'\n                        '*\u041e\u0448\u0438\u0431\u043a\u0430*: {}'.format(time.ctime(), error),\n            }\n        )\n", "path": "shopelectro/management/commands/_update_catalog/utils.py"}], "after_files": [{"content": "import glob\nimport logging\nimport math\nimport os\nimport shutil\nimport subprocess\nimport time\nfrom contextlib import contextmanager\nfrom itertools import chain\nfrom typing import Iterator, Dict\nfrom uuid import UUID\nfrom xml.etree import ElementTree\n\nimport requests\nfrom django.conf import settings\n\nfrom shopelectro.exception import DownloadFilesError\n\nlogger = logging.getLogger(__name__)\nDOWNLOAD_FILES_TIMEOUT = 40.0\nUUID_TYPE = str\nData = Dict[str, Dict[str, dict]]\nNOT_SAVE_TEMPLATE = '{entity} with name=\"{name}\" has no {field}. It\\'ll not be' \\\n                    ' saved'\n\n\ndef floor(x: float, precision=0) -> float:\n    \"\"\"\n    The same behaviour as `math.floor`, but with precision.\n\n    >>> floor(1.234, precision=2)  # result: 1.23\n    \"\"\"\n    k = 10**precision\n    return math.floor(x * k) / k\n\n\ndef is_correct_uuid(uuid_):\n    try:\n        val = UUID(uuid_)\n    except (ValueError, TypeError):\n        return False\n    return str(val) == uuid_\n\n\nclass XmlFile:\n\n    namespace = '{urn:1C.ru:commerceml_2}'\n\n    def __init__(self, fetch_callback, xml_path_pattern, xpath_queries,\n                 extra_options=None):\n        self.fetch_callback = fetch_callback\n        self.xml_path_pattern = xml_path_pattern\n        self.xpath_queries = xpath_queries\n        self.extra_options = extra_options or {}\n\n    @property\n    def parsed_files(self):\n        \"\"\"Get parsed xml files, that matched the path pattern.\"\"\"\n        xml_files = glob.glob(os.path.join(\n            settings.ASSETS_DIR, self.xml_path_pattern\n        ))\n        assert xml_files, 'Files on path {} does not exist.'.format(\n            self.xml_path_pattern\n        )\n        return [ElementTree.parse(file) for file in xml_files]\n\n    @property\n    def xpaths(self):\n        \"\"\"Get xpath queries for xml.\"\"\"\n        return {\n            name: query.format(self.namespace)\n            for name, query in self.xpath_queries.items()\n        }\n\n    def get_data(self) -> Iterator:\n        \"\"\"\n        Get data from xml files.\n\n        Example files with products names or prices.\n        \"\"\"\n        return chain.from_iterable(\n            self.fetch_callback(file, self)\n            for file in self.parsed_files\n        )\n\n\n@contextmanager\ndef collect_errors(error_types: tuple):\n    errors = []\n\n    @contextmanager\n    def collect():\n        try:\n            yield\n        except error_types as error:\n            errors.append(error)\n    yield collect\n    if errors:\n        raise errors[0]\n\n\n@contextmanager\ndef download_catalog(destination):\n    \"\"\"Download catalog's xml files and delete after handle them.\"\"\"\n    wget_command = (\n        'wget -r -P {} ftp://{}:{}@{}/webdata/'\n        ' 2>&1 | grep \"\u0432\u0440\u0435\u043c\u044f\\|time\\|Downloaded\"'.format(\n            destination,\n            settings.FTP_USER,\n            settings.FTP_PASS,\n            settings.FTP_IP,\n        )\n    )\n\n    try:\n        subprocess.run(wget_command, timeout=DOWNLOAD_FILES_TIMEOUT, shell=True)\n    except subprocess.TimeoutExpired as e:\n        raise DownloadFilesError(str(e))\n\n    assert os.path.exists(os.path.join(\n        destination, settings.FTP_IP)), 'Files do not downloaded...'\n    logger.info('Download catalog - completed...')\n\n    try:\n        yield\n    finally:\n        # remove downloaded data\n        shutil.rmtree(os.path.join(destination, settings.FTP_IP))\n\n\ndef report(error):\n    report_url = getattr(settings, 'SLACK_REPORT_URL', None)\n    if report_url is not None:\n        requests.post(\n            url=report_url,\n            json={\n                'text': '*\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u0430\u0442\u0430\u043b\u043e\u0433 Shopelectro.*\\n'\n                        '*\u0412\u0440\u0435\u043c\u044f*: {}\\n'\n                        '*\u041e\u0448\u0438\u0431\u043a\u0430*: {}'.format(time.ctime(), error),\n            }\n        )\n", "path": "shopelectro/management/commands/_update_catalog/utils.py"}]}