We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Exceptions in TextLab/HIX API not caught
### Describe the Bug
The textlab API is currently not available and crashes all page edit functionality for affected regions.

```
Apr 05 07:15:25 ERROR   django.request - 500 Internal Server Error: /lkmuenchen/pages/de/24515/edit/
Traceback (most recent call last):
  File "/usr/lib/python3.9/urllib/request.py", line 1346, in do_open
    h.request(req.get_method(), req.selector, req.data, headers,
  File "/usr/lib/python3.9/http/client.py", line 1255, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib/python3.9/http/client.py", line 1301, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.9/http/client.py", line 1250, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.9/http/client.py", line 1010, in _send_output
    self.send(msg)
  File "/usr/lib/python3.9/http/client.py", line 950, in send
    self.connect()
  File "/usr/lib/python3.9/http/client.py", line 1417, in connect
    super().connect()
  File "/usr/lib/python3.9/http/client.py", line 921, in connect
    self.sock = self._create_connection(
  File "/usr/lib/python3.9/socket.py", line 843, in create_connection
    raise err
  File "/usr/lib/python3.9/socket.py", line 831, in create_connection
    sock.connect(sa)
OSError: [Errno 113] No route to host
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `integreat_cms/textlab_api/textlab_api_client.py`
Content:
```
1 import json
2 import logging
3 from html import unescape
4 from urllib.error import URLError
5 from urllib.request import Request, urlopen
6 
7 from django.conf import settings
8 from django.core.exceptions import ImproperlyConfigured
9 
10 logger = logging.getLogger(__name__)
11 
12 
13 class TextlabClient:
14     """
15     Client for the textlab api.
16     Supports login and hix-score retrieval.
17     """
18 
19     def __init__(self, username, password):
20         if not settings.TEXTLAB_API_ENABLED:
21             raise ImproperlyConfigured("Textlab API is disabled")
22 
23         self.username = username
24         self.password = password
25         self.token = None
26 
27         self.login()
28 
29     def login(self):
30         """
31         Authorizes for the textlab api. On success, sets the token attribute.
32 
33         :raises urllib.error.HTTPError: If the login was not successful
34         """
35         data = {"identifier": self.username, "password": self.password}
36         response = self.post_request("/user/login", data)
37         self.token = response["token"]
38 
39     def benchmark(self, text):
40         """
41         Retrieves the hix score of the given text.
42 
43         :param text: The text to calculate the score for
44         :type text: str
45 
46         :return: The score, or None if an error occurred
47         :rtype: float
48         """
49         data = {"text": unescape(text), "locale_name": "de_DE"}
50         path = "/benchmark/5"
51         try:
52             response = self.post_request(path, data, self.token)
53         except URLError as e:
54             logger.warning("HIX benchmark API call failed: %r", e)
55             return None
56         return response.get("formulaHix", None)
57 
58     @staticmethod
59     def post_request(path, data, auth_token=None):
60         """
61         Sends a request to the api.
62 
63         :param path: The api path
64         :type path: str
65 
66         :param data: The data to send
67         :type data: dict
68 
69         :param auth_token: The authorization token to use
70         :type auth_token: str
71 
72         :return: The response json dictionary
73         :rtype: dict
74 
75         :raises urllib.error.HTTPError: If the request failed
76         """
77         data = json.dumps(data).encode("utf-8")
78         request = Request(
79             f"{settings.TEXTLAB_API_URL.rstrip('/')}{path}", data=data, method="POST"
80         )
81         if auth_token:
82             request.add_header("authorization", f"Bearer {auth_token}")
83         request.add_header("Content-Type", "application/json")
84         with urlopen(request) as response:
85             return json.loads(response.read().decode("utf-8"))
86
```

Path: `integreat_cms/textlab_api/apps.py`
Content:
```
1 import logging
2 from urllib.error import URLError
3 
4 from django.apps import AppConfig, apps
5 from django.conf import settings
6 from django.utils.translation import gettext_lazy as _
7 
8 from .textlab_api_client import TextlabClient
9 
10 logger = logging.getLogger(__name__)
11 
12 
13 class TextlabApiConfig(AppConfig):
14     """
15     Textlab api config inheriting the django AppConfig
16     """
17 
18     #: Full Python path to the application
19     name = "integreat_cms.textlab_api"
20     #: Human-readable name for the application
21     verbose_name = _("TextLab API")
22 
23     def ready(self):
24         """
25         Checking if api is available
26         """
27         # Only check availability if running a server
28         if apps.get_app_config("core").test_external_apis:
29             # If Textlab API is enabled, check availability
30             if settings.TEXTLAB_API_ENABLED:
31                 try:
32                     TextlabClient(
33                         settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY
34                     )
35                     logger.info(
36                         "Textlab API is available at: %r", settings.TEXTLAB_API_URL
37                     )
38                 except URLError:
39                     logger.info("Textlab API is unavailable")
40             else:
41                 logger.info("Textlab API is disabled")
42
```

Path: `integreat_cms/cms/views/utils/hix.py`
Content:
```
1 """
2 This file contains functionality to communicate with the textlab api to get the hix-value
3 for a given text.
4 """
5 import json
6 import logging
7 from functools import lru_cache
8 
9 from django.conf import settings
10 from django.http import JsonResponse
11 from django.views.decorators.http import require_POST
12 
13 from ....api.decorators import json_response
14 from ....textlab_api.textlab_api_client import TextlabClient
15 
16 logger = logging.getLogger(__name__)
17 
18 MAX_TEXT_LENGTH = 100_000
19 
20 
21 @lru_cache(maxsize=512)
22 def lookup_hix_score(text):
23     """
24     This function returns the hix score for the given text.
25     It either performs an api request or returns the value from cache.
26 
27     :param text: The text to calculate the hix score for
28     :type text: str
29 
30     :return: The score for the given text
31     :rtype: float
32     """
33     return TextlabClient(
34         settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY
35     ).benchmark(text)
36 
37 
38 @require_POST
39 @json_response
40 # pylint: disable=unused-argument
41 def get_hix_score(request, region_slug):
42     """
43     Calculates the hix score for the param 'text' in the request body and returns it.
44 
45     :param request: The request
46     :type request: ~django.http.HttpRequest
47 
48     :param region_slug: The slug of the current region
49     :type region_slug: str
50 
51     :return: A json response, of {"score": value} in case of success
52     :rtype: ~django.http.JsonResponse
53     """
54     # Don't pass texts larger than 100kb to the api in order to avoid being vulnerable to dos attacks
55     if len(request.body) > MAX_TEXT_LENGTH:
56         return JsonResponse({"error": "Request too large"})
57     body = json.loads(request.body.decode("utf-8"))
58     text = body["text"]
59 
60     if not isinstance(text, str) or not text.strip():
61         logger.warning("Received invalid text: %r", text)
62         return JsonResponse({"error": f"Invalid text: '{text}'"})
63 
64     if score := lookup_hix_score(text):
65         return JsonResponse({"score": score})
66     return JsonResponse({"error": "Could not retrieve hix score"})
67
```
--- 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/integreat_cms/cms/views/utils/hix.py b/integreat_cms/cms/views/utils/hix.py
--- a/integreat_cms/cms/views/utils/hix.py
+++ b/integreat_cms/cms/views/utils/hix.py
@@ -1,10 +1,11 @@
 """
-This file contains functionality to communicate with the textlab api to get the hix-value
+This file contains functionality to communicate with the Textlab api to get the hix-value
 for a given text.
 """
 import json
 import logging
 from functools import lru_cache
+from urllib.error import URLError
 
 from django.conf import settings
 from django.http import JsonResponse
@@ -30,9 +31,13 @@
     :return: The score for the given text
     :rtype: float
     """
-    return TextlabClient(
-        settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY
-    ).benchmark(text)
+    try:
+        return TextlabClient(
+            settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY
+        ).benchmark(text)
+    except (URLError, OSError) as e:
+        logger.warning("HIX benchmark API call failed: %r", e)
+        return None
 
 
 @require_POST
diff --git a/integreat_cms/textlab_api/apps.py b/integreat_cms/textlab_api/apps.py
--- a/integreat_cms/textlab_api/apps.py
+++ b/integreat_cms/textlab_api/apps.py
@@ -35,7 +35,7 @@
                     logger.info(
                         "Textlab API is available at: %r", settings.TEXTLAB_API_URL
                     )
-                except URLError:
-                    logger.info("Textlab API is unavailable")
+                except (URLError, OSError) as e:
+                    logger.info("Textlab API is unavailable: %r", e)
             else:
                 logger.info("Textlab API is disabled")
diff --git a/integreat_cms/textlab_api/textlab_api_client.py b/integreat_cms/textlab_api/textlab_api_client.py
--- a/integreat_cms/textlab_api/textlab_api_client.py
+++ b/integreat_cms/textlab_api/textlab_api_client.py
@@ -1,7 +1,6 @@
 import json
 import logging
 from html import unescape
-from urllib.error import URLError
 from urllib.request import Request, urlopen
 
 from django.conf import settings
@@ -14,6 +13,8 @@
     """
     Client for the textlab api.
     Supports login and hix-score retrieval.
+
+    A detailed API documentation can be found at https://comlab-ulm.github.io/swagger-V8/
     """
 
     def __init__(self, username, password):
@@ -48,11 +49,7 @@
         """
         data = {"text": unescape(text), "locale_name": "de_DE"}
         path = "/benchmark/5"
-        try:
-            response = self.post_request(path, data, self.token)
-        except URLError as e:
-            logger.warning("HIX benchmark API call failed: %r", e)
-            return None
+        response = self.post_request(path, data, self.token)
         return response.get("formulaHix", None)
 
     @staticmethod

{"golden_diff": "diff --git a/integreat_cms/cms/views/utils/hix.py b/integreat_cms/cms/views/utils/hix.py\n--- a/integreat_cms/cms/views/utils/hix.py\n+++ b/integreat_cms/cms/views/utils/hix.py\n@@ -1,10 +1,11 @@\n \"\"\"\n-This file contains functionality to communicate with the textlab api to get the hix-value\n+This file contains functionality to communicate with the Textlab api to get the hix-value\n for a given text.\n \"\"\"\n import json\n import logging\n from functools import lru_cache\n+from urllib.error import URLError\n \n from django.conf import settings\n from django.http import JsonResponse\n@@ -30,9 +31,13 @@\n     :return: The score for the given text\n     :rtype: float\n     \"\"\"\n-    return TextlabClient(\n-        settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY\n-    ).benchmark(text)\n+    try:\n+        return TextlabClient(\n+            settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY\n+        ).benchmark(text)\n+    except (URLError, OSError) as e:\n+        logger.warning(\"HIX benchmark API call failed: %r\", e)\n+        return None\n \n \n @require_POST\ndiff --git a/integreat_cms/textlab_api/apps.py b/integreat_cms/textlab_api/apps.py\n--- a/integreat_cms/textlab_api/apps.py\n+++ b/integreat_cms/textlab_api/apps.py\n@@ -35,7 +35,7 @@\n                     logger.info(\n                         \"Textlab API is available at: %r\", settings.TEXTLAB_API_URL\n                     )\n-                except URLError:\n-                    logger.info(\"Textlab API is unavailable\")\n+                except (URLError, OSError) as e:\n+                    logger.info(\"Textlab API is unavailable: %r\", e)\n             else:\n                 logger.info(\"Textlab API is disabled\")\ndiff --git a/integreat_cms/textlab_api/textlab_api_client.py b/integreat_cms/textlab_api/textlab_api_client.py\n--- a/integreat_cms/textlab_api/textlab_api_client.py\n+++ b/integreat_cms/textlab_api/textlab_api_client.py\n@@ -1,7 +1,6 @@\n import json\n import logging\n from html import unescape\n-from urllib.error import URLError\n from urllib.request import Request, urlopen\n \n from django.conf import settings\n@@ -14,6 +13,8 @@\n     \"\"\"\n     Client for the textlab api.\n     Supports login and hix-score retrieval.\n+\n+    A detailed API documentation can be found at https://comlab-ulm.github.io/swagger-V8/\n     \"\"\"\n \n     def __init__(self, username, password):\n@@ -48,11 +49,7 @@\n         \"\"\"\n         data = {\"text\": unescape(text), \"locale_name\": \"de_DE\"}\n         path = \"/benchmark/5\"\n-        try:\n-            response = self.post_request(path, data, self.token)\n-        except URLError as e:\n-            logger.warning(\"HIX benchmark API call failed: %r\", e)\n-            return None\n+        response = self.post_request(path, data, self.token)\n         return response.get(\"formulaHix\", None)\n \n     @staticmethod\n", "issue": "Exceptions in TextLab/HIX API not caught\n### Describe the Bug\r\nThe textlab API is currently not available and crashes all page edit functionality for affected regions.\r\n\r\n```\r\nApr 05 07:15:25 ERROR   django.request - 500 Internal Server Error: /lkmuenchen/pages/de/24515/edit/\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.9/urllib/request.py\", line 1346, in do_open\r\n    h.request(req.get_method(), req.selector, req.data, headers,\r\n  File \"/usr/lib/python3.9/http/client.py\", line 1255, in request\r\n    self._send_request(method, url, body, headers, encode_chunked)\r\n  File \"/usr/lib/python3.9/http/client.py\", line 1301, in _send_request\r\n    self.endheaders(body, encode_chunked=encode_chunked)\r\n  File \"/usr/lib/python3.9/http/client.py\", line 1250, in endheaders\r\n    self._send_output(message_body, encode_chunked=encode_chunked)\r\n  File \"/usr/lib/python3.9/http/client.py\", line 1010, in _send_output\r\n    self.send(msg)\r\n  File \"/usr/lib/python3.9/http/client.py\", line 950, in send\r\n    self.connect()\r\n  File \"/usr/lib/python3.9/http/client.py\", line 1417, in connect\r\n    super().connect()\r\n  File \"/usr/lib/python3.9/http/client.py\", line 921, in connect\r\n    self.sock = self._create_connection(\r\n  File \"/usr/lib/python3.9/socket.py\", line 843, in create_connection\r\n    raise err\r\n  File \"/usr/lib/python3.9/socket.py\", line 831, in create_connection\r\n    sock.connect(sa)\r\nOSError: [Errno 113] No route to host\r\n```\n", "before_files": [{"content": "import json\nimport logging\nfrom html import unescape\nfrom urllib.error import URLError\nfrom urllib.request import Request, urlopen\n\nfrom django.conf import settings\nfrom django.core.exceptions import ImproperlyConfigured\n\nlogger = logging.getLogger(__name__)\n\n\nclass TextlabClient:\n    \"\"\"\n    Client for the textlab api.\n    Supports login and hix-score retrieval.\n    \"\"\"\n\n    def __init__(self, username, password):\n        if not settings.TEXTLAB_API_ENABLED:\n            raise ImproperlyConfigured(\"Textlab API is disabled\")\n\n        self.username = username\n        self.password = password\n        self.token = None\n\n        self.login()\n\n    def login(self):\n        \"\"\"\n        Authorizes for the textlab api. On success, sets the token attribute.\n\n        :raises urllib.error.HTTPError: If the login was not successful\n        \"\"\"\n        data = {\"identifier\": self.username, \"password\": self.password}\n        response = self.post_request(\"/user/login\", data)\n        self.token = response[\"token\"]\n\n    def benchmark(self, text):\n        \"\"\"\n        Retrieves the hix score of the given text.\n\n        :param text: The text to calculate the score for\n        :type text: str\n\n        :return: The score, or None if an error occurred\n        :rtype: float\n        \"\"\"\n        data = {\"text\": unescape(text), \"locale_name\": \"de_DE\"}\n        path = \"/benchmark/5\"\n        try:\n            response = self.post_request(path, data, self.token)\n        except URLError as e:\n            logger.warning(\"HIX benchmark API call failed: %r\", e)\n            return None\n        return response.get(\"formulaHix\", None)\n\n    @staticmethod\n    def post_request(path, data, auth_token=None):\n        \"\"\"\n        Sends a request to the api.\n\n        :param path: The api path\n        :type path: str\n\n        :param data: The data to send\n        :type data: dict\n\n        :param auth_token: The authorization token to use\n        :type auth_token: str\n\n        :return: The response json dictionary\n        :rtype: dict\n\n        :raises urllib.error.HTTPError: If the request failed\n        \"\"\"\n        data = json.dumps(data).encode(\"utf-8\")\n        request = Request(\n            f\"{settings.TEXTLAB_API_URL.rstrip('/')}{path}\", data=data, method=\"POST\"\n        )\n        if auth_token:\n            request.add_header(\"authorization\", f\"Bearer {auth_token}\")\n        request.add_header(\"Content-Type\", \"application/json\")\n        with urlopen(request) as response:\n            return json.loads(response.read().decode(\"utf-8\"))\n", "path": "integreat_cms/textlab_api/textlab_api_client.py"}, {"content": "import logging\nfrom urllib.error import URLError\n\nfrom django.apps import AppConfig, apps\nfrom django.conf import settings\nfrom django.utils.translation import gettext_lazy as _\n\nfrom .textlab_api_client import TextlabClient\n\nlogger = logging.getLogger(__name__)\n\n\nclass TextlabApiConfig(AppConfig):\n    \"\"\"\n    Textlab api config inheriting the django AppConfig\n    \"\"\"\n\n    #: Full Python path to the application\n    name = \"integreat_cms.textlab_api\"\n    #: Human-readable name for the application\n    verbose_name = _(\"TextLab API\")\n\n    def ready(self):\n        \"\"\"\n        Checking if api is available\n        \"\"\"\n        # Only check availability if running a server\n        if apps.get_app_config(\"core\").test_external_apis:\n            # If Textlab API is enabled, check availability\n            if settings.TEXTLAB_API_ENABLED:\n                try:\n                    TextlabClient(\n                        settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY\n                    )\n                    logger.info(\n                        \"Textlab API is available at: %r\", settings.TEXTLAB_API_URL\n                    )\n                except URLError:\n                    logger.info(\"Textlab API is unavailable\")\n            else:\n                logger.info(\"Textlab API is disabled\")\n", "path": "integreat_cms/textlab_api/apps.py"}, {"content": "\"\"\"\nThis file contains functionality to communicate with the textlab api to get the hix-value\nfor a given text.\n\"\"\"\nimport json\nimport logging\nfrom functools import lru_cache\n\nfrom django.conf import settings\nfrom django.http import JsonResponse\nfrom django.views.decorators.http import require_POST\n\nfrom ....api.decorators import json_response\nfrom ....textlab_api.textlab_api_client import TextlabClient\n\nlogger = logging.getLogger(__name__)\n\nMAX_TEXT_LENGTH = 100_000\n\n\n@lru_cache(maxsize=512)\ndef lookup_hix_score(text):\n    \"\"\"\n    This function returns the hix score for the given text.\n    It either performs an api request or returns the value from cache.\n\n    :param text: The text to calculate the hix score for\n    :type text: str\n\n    :return: The score for the given text\n    :rtype: float\n    \"\"\"\n    return TextlabClient(\n        settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY\n    ).benchmark(text)\n\n\n@require_POST\n@json_response\n# pylint: disable=unused-argument\ndef get_hix_score(request, region_slug):\n    \"\"\"\n    Calculates the hix score for the param 'text' in the request body and returns it.\n\n    :param request: The request\n    :type request: ~django.http.HttpRequest\n\n    :param region_slug: The slug of the current region\n    :type region_slug: str\n\n    :return: A json response, of {\"score\": value} in case of success\n    :rtype: ~django.http.JsonResponse\n    \"\"\"\n    # Don't pass texts larger than 100kb to the api in order to avoid being vulnerable to dos attacks\n    if len(request.body) > MAX_TEXT_LENGTH:\n        return JsonResponse({\"error\": \"Request too large\"})\n    body = json.loads(request.body.decode(\"utf-8\"))\n    text = body[\"text\"]\n\n    if not isinstance(text, str) or not text.strip():\n        logger.warning(\"Received invalid text: %r\", text)\n        return JsonResponse({\"error\": f\"Invalid text: '{text}'\"})\n\n    if score := lookup_hix_score(text):\n        return JsonResponse({\"score\": score})\n    return JsonResponse({\"error\": \"Could not retrieve hix score\"})\n", "path": "integreat_cms/cms/views/utils/hix.py"}], "after_files": [{"content": "import json\nimport logging\nfrom html import unescape\nfrom urllib.request import Request, urlopen\n\nfrom django.conf import settings\nfrom django.core.exceptions import ImproperlyConfigured\n\nlogger = logging.getLogger(__name__)\n\n\nclass TextlabClient:\n    \"\"\"\n    Client for the textlab api.\n    Supports login and hix-score retrieval.\n\n    A detailed API documentation can be found at https://comlab-ulm.github.io/swagger-V8/\n    \"\"\"\n\n    def __init__(self, username, password):\n        if not settings.TEXTLAB_API_ENABLED:\n            raise ImproperlyConfigured(\"Textlab API is disabled\")\n\n        self.username = username\n        self.password = password\n        self.token = None\n\n        self.login()\n\n    def login(self):\n        \"\"\"\n        Authorizes for the textlab api. On success, sets the token attribute.\n\n        :raises urllib.error.HTTPError: If the login was not successful\n        \"\"\"\n        data = {\"identifier\": self.username, \"password\": self.password}\n        response = self.post_request(\"/user/login\", data)\n        self.token = response[\"token\"]\n\n    def benchmark(self, text):\n        \"\"\"\n        Retrieves the hix score of the given text.\n\n        :param text: The text to calculate the score for\n        :type text: str\n\n        :return: The score, or None if an error occurred\n        :rtype: float\n        \"\"\"\n        data = {\"text\": unescape(text), \"locale_name\": \"de_DE\"}\n        path = \"/benchmark/5\"\n        response = self.post_request(path, data, self.token)\n        return response.get(\"formulaHix\", None)\n\n    @staticmethod\n    def post_request(path, data, auth_token=None):\n        \"\"\"\n        Sends a request to the api.\n\n        :param path: The api path\n        :type path: str\n\n        :param data: The data to send\n        :type data: dict\n\n        :param auth_token: The authorization token to use\n        :type auth_token: str\n\n        :return: The response json dictionary\n        :rtype: dict\n\n        :raises urllib.error.HTTPError: If the request failed\n        \"\"\"\n        data = json.dumps(data).encode(\"utf-8\")\n        request = Request(\n            f\"{settings.TEXTLAB_API_URL.rstrip('/')}{path}\", data=data, method=\"POST\"\n        )\n        if auth_token:\n            request.add_header(\"authorization\", f\"Bearer {auth_token}\")\n        request.add_header(\"Content-Type\", \"application/json\")\n        with urlopen(request) as response:\n            return json.loads(response.read().decode(\"utf-8\"))\n", "path": "integreat_cms/textlab_api/textlab_api_client.py"}, {"content": "import logging\nfrom urllib.error import URLError\n\nfrom django.apps import AppConfig, apps\nfrom django.conf import settings\nfrom django.utils.translation import gettext_lazy as _\n\nfrom .textlab_api_client import TextlabClient\n\nlogger = logging.getLogger(__name__)\n\n\nclass TextlabApiConfig(AppConfig):\n    \"\"\"\n    Textlab api config inheriting the django AppConfig\n    \"\"\"\n\n    #: Full Python path to the application\n    name = \"integreat_cms.textlab_api\"\n    #: Human-readable name for the application\n    verbose_name = _(\"TextLab API\")\n\n    def ready(self):\n        \"\"\"\n        Checking if api is available\n        \"\"\"\n        # Only check availability if running a server\n        if apps.get_app_config(\"core\").test_external_apis:\n            # If Textlab API is enabled, check availability\n            if settings.TEXTLAB_API_ENABLED:\n                try:\n                    TextlabClient(\n                        settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY\n                    )\n                    logger.info(\n                        \"Textlab API is available at: %r\", settings.TEXTLAB_API_URL\n                    )\n                except (URLError, OSError) as e:\n                    logger.info(\"Textlab API is unavailable: %r\", e)\n            else:\n                logger.info(\"Textlab API is disabled\")\n", "path": "integreat_cms/textlab_api/apps.py"}, {"content": "\"\"\"\nThis file contains functionality to communicate with the Textlab api to get the hix-value\nfor a given text.\n\"\"\"\nimport json\nimport logging\nfrom functools import lru_cache\nfrom urllib.error import URLError\n\nfrom django.conf import settings\nfrom django.http import JsonResponse\nfrom django.views.decorators.http import require_POST\n\nfrom ....api.decorators import json_response\nfrom ....textlab_api.textlab_api_client import TextlabClient\n\nlogger = logging.getLogger(__name__)\n\nMAX_TEXT_LENGTH = 100_000\n\n\n@lru_cache(maxsize=512)\ndef lookup_hix_score(text):\n    \"\"\"\n    This function returns the hix score for the given text.\n    It either performs an api request or returns the value from cache.\n\n    :param text: The text to calculate the hix score for\n    :type text: str\n\n    :return: The score for the given text\n    :rtype: float\n    \"\"\"\n    try:\n        return TextlabClient(\n            settings.TEXTLAB_API_USERNAME, settings.TEXTLAB_API_KEY\n        ).benchmark(text)\n    except (URLError, OSError) as e:\n        logger.warning(\"HIX benchmark API call failed: %r\", e)\n        return None\n\n\n@require_POST\n@json_response\n# pylint: disable=unused-argument\ndef get_hix_score(request, region_slug):\n    \"\"\"\n    Calculates the hix score for the param 'text' in the request body and returns it.\n\n    :param request: The request\n    :type request: ~django.http.HttpRequest\n\n    :param region_slug: The slug of the current region\n    :type region_slug: str\n\n    :return: A json response, of {\"score\": value} in case of success\n    :rtype: ~django.http.JsonResponse\n    \"\"\"\n    # Don't pass texts larger than 100kb to the api in order to avoid being vulnerable to dos attacks\n    if len(request.body) > MAX_TEXT_LENGTH:\n        return JsonResponse({\"error\": \"Request too large\"})\n    body = json.loads(request.body.decode(\"utf-8\"))\n    text = body[\"text\"]\n\n    if not isinstance(text, str) or not text.strip():\n        logger.warning(\"Received invalid text: %r\", text)\n        return JsonResponse({\"error\": f\"Invalid text: '{text}'\"})\n\n    if score := lookup_hix_score(text):\n        return JsonResponse({\"score\": score})\n    return JsonResponse({\"error\": \"Could not retrieve hix score\"})\n", "path": "integreat_cms/cms/views/utils/hix.py"}]}