prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Feature Request: Document the task management
  ### What are you trying to do?
  
  Understand how the task management functions
  
  ### Describe the solution you'd like
  
  A technical documentation of how it works.
  
  ### Have you consider alternatives?
  
  _No response_
  
  ### Additional context
  
  _No response_
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `akvo/rsr/management/commands/django_q_probettp.py`
  Content:
  ```
  1 #!/usr/bin/env python3
  2 # -*- coding: utf-8 -*-
  3 
  4 # Akvo Reporting is covered by the GNU Affero General Public License.
  5 # See more details in the license.txt file located at the root folder of the Akvo RSR module.
  6 # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
  7 
  8 """
  9 Provides a localhost HTTP server to query the local status of the django-q cluster
  10 """
  11 import logging
  12 import signal
  13 import socket
  14 from http.server import BaseHTTPRequestHandler, HTTPServer
  15 
  16 from django.core.management.base import BaseCommand
  17 from django_q.conf import Conf
  18 from django_q.status import Stat
  19 
  20 logger = logging.getLogger(__name__)
  21 
  22 
  23 class Command(BaseCommand):
  24     help = __doc__
  25 
  26     def handle(self, *args, **options):
  27         server = HTTPServer(("localhost", 8080), DjangoQRequestHandler)
  28 
  29         def handle_end(*_):
  30             logger.info("Stopping server")
  31             server.shutdown()
  32 
  33         signal.signal(signal.SIGINT, handle_end)
  34         signal.signal(signal.SIGTERM, handle_end)
  35 
  36         logger.info("Starting server...")
  37         server.serve_forever()
  38 
  39 
  40 class DjangoQRequestHandler(BaseHTTPRequestHandler):
  41 
  42     def do_GET(self):
  43         hostname = socket.gethostname()
  44 
  45         # Find local cluster
  46         local_stat = next(iter(stat for stat in Stat.get_all() if stat.host == hostname), None)
  47         if local_stat:
  48             message = local_stat.status
  49         else:
  50             message = Conf.STOPPED
  51         logger.info(f"Probe response {message}")
  52         self.send_response(200)
  53         self.end_headers()
  54         self.wfile.write(message.encode())
  55 
  56     def log_message(self, format: str, *args) -> None:
  57         logger.debug(format, *args)
  58
  ```
  
  Path: `akvo/cache/heartbeat.py`
  Content:
  ```
  1 import datetime
  2 import logging
  3 from threading import Event, Thread
  4 from typing import Union
  5 
  6 from django.core.cache import cache
  7 
  8 
  9 class CacheHeartbeat(Thread):
  10     """
  11     Thread to update set a cache key with a max life and refresh it as long as the thread is alive
  12 
  13     The thread can be ended by setting the `event_end` flag
  14     """
  15 
  16     def __init__(self, cache_key: str, key_timeout: float = 30.0, beat_interval: int = 3):
  17         """
  18         :param cache_key: The cache key to keep alive
  19         :param key_timeout: How long the cache key should live without the heartbeat thread
  20         :param beat_interval: How often per timeout the key should "beat"
  21         """
  22         super().__init__()
  23         self.cache_key = cache_key
  24         self.event_end = Event()
  25         self.key_timeout = key_timeout
  26         self.beat_interval = beat_interval
  27 
  28     def run(self) -> None:
  29         logger = logging.getLogger("akvo.rsr.CacheHeartBeat")
  30         logger.info("Starting cache heartbeat for '%s' with timeout %s", self.cache_key, self.key_timeout)
  31         self.event_end.clear()
  32         while not self.event_end.is_set():
  33             # Refresh the heartbeat
  34             self.set_cache_value()
  35             self.event_end.wait(self.key_timeout / self.beat_interval)
  36 
  37         cache.delete(self.cache_key)
  38         logger.info("Ended cache heartbeat for '%s'", self.cache_key)
  39 
  40     def set_cache_value(self):
  41         cache.set(self.cache_key, self.get_calc_value(), self.key_timeout)
  42 
  43     def get_calc_value(self) -> Union[str, int, float]:
  44         return datetime.datetime.utcnow().timestamp()
  45
  ```
  --- 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/akvo/cache/heartbeat.py b/akvo/cache/heartbeat.py
  --- a/akvo/cache/heartbeat.py
  +++ b/akvo/cache/heartbeat.py
  @@ -8,7 +8,7 @@
   
   class CacheHeartbeat(Thread):
       """
  -    Thread to update set a cache key with a max life and refresh it as long as the thread is alive
  +    Thread to set a cache key with a max life and refresh it as long as the thread is alive
   
       The thread can be ended by setting the `event_end` flag
       """
  @@ -38,7 +38,13 @@
           logger.info("Ended cache heartbeat for '%s'", self.cache_key)
   
       def set_cache_value(self):
  -        cache.set(self.cache_key, self.get_calc_value(), self.key_timeout)
  +        """
  +        Set the cache key and its value
  +        """
  +        cache.set(self.cache_key, self.calc_cache_value(), self.key_timeout)
   
  -    def get_calc_value(self) -> Union[str, int, float]:
  +    def calc_cache_value(self) -> Union[str, int, float]:
  +        """
  +        Calculate a value to be used when setting the cache key
  +        """
           return datetime.datetime.utcnow().timestamp()
  diff --git a/akvo/rsr/management/commands/django_q_probettp.py b/akvo/rsr/management/commands/django_q_probettp.py
  --- a/akvo/rsr/management/commands/django_q_probettp.py
  +++ b/akvo/rsr/management/commands/django_q_probettp.py
  @@ -38,8 +38,14 @@
   
   
   class DjangoQRequestHandler(BaseHTTPRequestHandler):
  +    """
  +    A handler to be used with HTTPServer to get the status of the local django-q cluster
  +    """
   
       def do_GET(self):
  +        """
  +        Handle GET requests to return a simple string indicating the status of the django-q cluster
  +        """
           hostname = socket.gethostname()
   
           # Find local cluster
  
verification_info: {"golden_diff": "diff --git a/akvo/cache/heartbeat.py b/akvo/cache/heartbeat.py\n--- a/akvo/cache/heartbeat.py\n+++ b/akvo/cache/heartbeat.py\n@@ -8,7 +8,7 @@\n \n class CacheHeartbeat(Thread):\n     \"\"\"\n-    Thread to update set a cache key with a max life and refresh it as long as the thread is alive\n+    Thread to set a cache key with a max life and refresh it as long as the thread is alive\n \n     The thread can be ended by setting the `event_end` flag\n     \"\"\"\n@@ -38,7 +38,13 @@\n         logger.info(\"Ended cache heartbeat for '%s'\", self.cache_key)\n \n     def set_cache_value(self):\n-        cache.set(self.cache_key, self.get_calc_value(), self.key_timeout)\n+        \"\"\"\n+        Set the cache key and its value\n+        \"\"\"\n+        cache.set(self.cache_key, self.calc_cache_value(), self.key_timeout)\n \n-    def get_calc_value(self) -> Union[str, int, float]:\n+    def calc_cache_value(self) -> Union[str, int, float]:\n+        \"\"\"\n+        Calculate a value to be used when setting the cache key\n+        \"\"\"\n         return datetime.datetime.utcnow().timestamp()\ndiff --git a/akvo/rsr/management/commands/django_q_probettp.py b/akvo/rsr/management/commands/django_q_probettp.py\n--- a/akvo/rsr/management/commands/django_q_probettp.py\n+++ b/akvo/rsr/management/commands/django_q_probettp.py\n@@ -38,8 +38,14 @@\n \n \n class DjangoQRequestHandler(BaseHTTPRequestHandler):\n+    \"\"\"\n+    A handler to be used with HTTPServer to get the status of the local django-q cluster\n+    \"\"\"\n \n     def do_GET(self):\n+        \"\"\"\n+        Handle GET requests to return a simple string indicating the status of the django-q cluster\n+        \"\"\"\n         hostname = socket.gethostname()\n \n         # Find local cluster\n", "issue": "Feature Request: Document the task management\n### What are you trying to do?\n\nUnderstand how the task management functions\n\n### Describe the solution you'd like\n\nA technical documentation of how it works.\n\n### Have you consider alternatives?\n\n_No response_\n\n### Additional context\n\n_No response_\n", "before_files": [{"content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Akvo Reporting is covered by the GNU Affero General Public License.\n# See more details in the license.txt file located at the root folder of the Akvo RSR module.\n# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.\n\n\"\"\"\nProvides a localhost HTTP server to query the local status of the django-q cluster\n\"\"\"\nimport logging\nimport signal\nimport socket\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\n\nfrom django.core.management.base import BaseCommand\nfrom django_q.conf import Conf\nfrom django_q.status import Stat\n\nlogger = logging.getLogger(__name__)\n\n\nclass Command(BaseCommand):\n    help = __doc__\n\n    def handle(self, *args, **options):\n        server = HTTPServer((\"localhost\", 8080), DjangoQRequestHandler)\n\n        def handle_end(*_):\n            logger.info(\"Stopping server\")\n            server.shutdown()\n\n        signal.signal(signal.SIGINT, handle_end)\n        signal.signal(signal.SIGTERM, handle_end)\n\n        logger.info(\"Starting server...\")\n        server.serve_forever()\n\n\nclass DjangoQRequestHandler(BaseHTTPRequestHandler):\n\n    def do_GET(self):\n        hostname = socket.gethostname()\n\n        # Find local cluster\n        local_stat = next(iter(stat for stat in Stat.get_all() if stat.host == hostname), None)\n        if local_stat:\n            message = local_stat.status\n        else:\n            message = Conf.STOPPED\n        logger.info(f\"Probe response {message}\")\n        self.send_response(200)\n        self.end_headers()\n        self.wfile.write(message.encode())\n\n    def log_message(self, format: str, *args) -> None:\n        logger.debug(format, *args)\n", "path": "akvo/rsr/management/commands/django_q_probettp.py"}, {"content": "import datetime\nimport logging\nfrom threading import Event, Thread\nfrom typing import Union\n\nfrom django.core.cache import cache\n\n\nclass CacheHeartbeat(Thread):\n    \"\"\"\n    Thread to update set a cache key with a max life and refresh it as long as the thread is alive\n\n    The thread can be ended by setting the `event_end` flag\n    \"\"\"\n\n    def __init__(self, cache_key: str, key_timeout: float = 30.0, beat_interval: int = 3):\n        \"\"\"\n        :param cache_key: The cache key to keep alive\n        :param key_timeout: How long the cache key should live without the heartbeat thread\n        :param beat_interval: How often per timeout the key should \"beat\"\n        \"\"\"\n        super().__init__()\n        self.cache_key = cache_key\n        self.event_end = Event()\n        self.key_timeout = key_timeout\n        self.beat_interval = beat_interval\n\n    def run(self) -> None:\n        logger = logging.getLogger(\"akvo.rsr.CacheHeartBeat\")\n        logger.info(\"Starting cache heartbeat for '%s' with timeout %s\", self.cache_key, self.key_timeout)\n        self.event_end.clear()\n        while not self.event_end.is_set():\n            # Refresh the heartbeat\n            self.set_cache_value()\n            self.event_end.wait(self.key_timeout / self.beat_interval)\n\n        cache.delete(self.cache_key)\n        logger.info(\"Ended cache heartbeat for '%s'\", self.cache_key)\n\n    def set_cache_value(self):\n        cache.set(self.cache_key, self.get_calc_value(), self.key_timeout)\n\n    def get_calc_value(self) -> Union[str, int, float]:\n        return datetime.datetime.utcnow().timestamp()\n", "path": "akvo/cache/heartbeat.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Akvo Reporting is covered by the GNU Affero General Public License.\n# See more details in the license.txt file located at the root folder of the Akvo RSR module.\n# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.\n\n\"\"\"\nProvides a localhost HTTP server to query the local status of the django-q cluster\n\"\"\"\nimport logging\nimport signal\nimport socket\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\n\nfrom django.core.management.base import BaseCommand\nfrom django_q.conf import Conf\nfrom django_q.status import Stat\n\nlogger = logging.getLogger(__name__)\n\n\nclass Command(BaseCommand):\n    help = __doc__\n\n    def handle(self, *args, **options):\n        server = HTTPServer((\"localhost\", 8080), DjangoQRequestHandler)\n\n        def handle_end(*_):\n            logger.info(\"Stopping server\")\n            server.shutdown()\n\n        signal.signal(signal.SIGINT, handle_end)\n        signal.signal(signal.SIGTERM, handle_end)\n\n        logger.info(\"Starting server...\")\n        server.serve_forever()\n\n\nclass DjangoQRequestHandler(BaseHTTPRequestHandler):\n    \"\"\"\n    A handler to be used with HTTPServer to get the status of the local django-q cluster\n    \"\"\"\n\n    def do_GET(self):\n        \"\"\"\n        Handle GET requests to return a simple string indicating the status of the django-q cluster\n        \"\"\"\n        hostname = socket.gethostname()\n\n        # Find local cluster\n        local_stat = next(iter(stat for stat in Stat.get_all() if stat.host == hostname), None)\n        if local_stat:\n            message = local_stat.status\n        else:\n            message = Conf.STOPPED\n        logger.info(f\"Probe response {message}\")\n        self.send_response(200)\n        self.end_headers()\n        self.wfile.write(message.encode())\n\n    def log_message(self, format: str, *args) -> None:\n        logger.debug(format, *args)\n", "path": "akvo/rsr/management/commands/django_q_probettp.py"}, {"content": "import datetime\nimport logging\nfrom threading import Event, Thread\nfrom typing import Union\n\nfrom django.core.cache import cache\n\n\nclass CacheHeartbeat(Thread):\n    \"\"\"\n    Thread to set a cache key with a max life and refresh it as long as the thread is alive\n\n    The thread can be ended by setting the `event_end` flag\n    \"\"\"\n\n    def __init__(self, cache_key: str, key_timeout: float = 30.0, beat_interval: int = 3):\n        \"\"\"\n        :param cache_key: The cache key to keep alive\n        :param key_timeout: How long the cache key should live without the heartbeat thread\n        :param beat_interval: How often per timeout the key should \"beat\"\n        \"\"\"\n        super().__init__()\n        self.cache_key = cache_key\n        self.event_end = Event()\n        self.key_timeout = key_timeout\n        self.beat_interval = beat_interval\n\n    def run(self) -> None:\n        logger = logging.getLogger(\"akvo.rsr.CacheHeartBeat\")\n        logger.info(\"Starting cache heartbeat for '%s' with timeout %s\", self.cache_key, self.key_timeout)\n        self.event_end.clear()\n        while not self.event_end.is_set():\n            # Refresh the heartbeat\n            self.set_cache_value()\n            self.event_end.wait(self.key_timeout / self.beat_interval)\n\n        cache.delete(self.cache_key)\n        logger.info(\"Ended cache heartbeat for '%s'\", self.cache_key)\n\n    def set_cache_value(self):\n        \"\"\"\n        Set the cache key and its value\n        \"\"\"\n        cache.set(self.cache_key, self.calc_cache_value(), self.key_timeout)\n\n    def calc_cache_value(self) -> Union[str, int, float]:\n        \"\"\"\n        Calculate a value to be used when setting the cache key\n        \"\"\"\n        return datetime.datetime.utcnow().timestamp()\n", "path": "akvo/cache/heartbeat.py"}]}